diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-01-25 11:39:07 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2016-01-25 15:20:42 +0000 |
commit | 6c91641271e536ffaa88a1dff5127e42ee99a91e (patch) | |
tree | 703d9dd49602377ddc90cbf886aad37913f2496b /chromium/chrome/browser/resources/chromeos/chromevox/common | |
parent | b145b7fafd36f0c260d6a768c81fc14e32578099 (diff) | |
download | qtwebengine-chromium-6c91641271e536ffaa88a1dff5127e42ee99a91e.tar.gz |
BASELINE: Update Chromium to 49.0.2623.23
Also adds missing printing sources.
Change-Id: I3726b8f0c7d6751c9fc846096c571fadca7108cd
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/chrome/browser/resources/chromeos/chromevox/common')
67 files changed, 0 insertions, 31888 deletions
diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/aria_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/aria_util.js deleted file mode 100644 index 09b36bdf1a1..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/aria_util.js +++ /dev/null @@ -1,978 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A collection of JavaScript utilities used to simplify working - * with ARIA (http://www.w3.org/TR/wai-aria). - */ - - -goog.provide('cvox.AriaUtil'); -goog.require('cvox.AbstractEarcons'); -goog.require('cvox.ChromeVox'); -goog.require('cvox.NodeState'); -goog.require('cvox.NodeStateUtil'); - - -/** - * Create the namespace - * @constructor - */ -cvox.AriaUtil = function() { -}; - - -/** - * A mapping from ARIA role names to their message ids. - * Note: If you are adding a new mapping, the new message identifier needs a - * corresponding braille message. For example, a message id 'tag_button' - * requires another message 'tag_button_brl' within messages.js. - * @type {Object<string>} - */ -cvox.AriaUtil.WIDGET_ROLE_TO_NAME = { - 'alert' : 'role_alert', - 'alertdialog' : 'role_alertdialog', - 'button' : 'role_button', - 'checkbox' : 'role_checkbox', - 'columnheader' : 'role_columnheader', - 'combobox' : 'role_combobox', - 'dialog' : 'role_dialog', - 'grid' : 'role_grid', - 'gridcell' : 'role_gridcell', - 'link' : 'role_link', - 'listbox' : 'role_listbox', - 'log' : 'role_log', - 'marquee' : 'role_marquee', - 'menu' : 'role_menu', - 'menubar' : 'role_menubar', - 'menuitem' : 'role_menuitem', - 'menuitemcheckbox' : 'role_menuitemcheckbox', - 'menuitemradio' : 'role_menuitemradio', - 'option' : 'role_option', - 'progressbar' : 'role_progressbar', - 'radio' : 'role_radio', - 'radiogroup' : 'role_radiogroup', - 'rowheader' : 'role_rowheader', - 'scrollbar' : 'role_scrollbar', - 'slider' : 'role_slider', - 'spinbutton' : 'role_spinbutton', - 'status' : 'role_status', - 'tab' : 'role_tab', - 'tablist' : 'role_tablist', - 'tabpanel' : 'role_tabpanel', - 'textbox' : 'role_textbox', - 'timer' : 'role_timer', - 'toolbar' : 'role_toolbar', - 'tooltip' : 'role_tooltip', - 'treeitem' : 'role_treeitem' -}; - - -/** - * Note: If you are adding a new mapping, the new message identifier needs a - * corresponding braille message. For example, a message id 'tag_button' - * requires another message 'tag_button_brl' within messages.js. - * @type {Object<string>} - */ -cvox.AriaUtil.STRUCTURE_ROLE_TO_NAME = { - 'article' : 'role_article', - 'application' : 'role_application', - 'banner' : 'role_banner', - 'columnheader' : 'role_columnheader', - 'complementary' : 'role_complementary', - 'contentinfo' : 'role_contentinfo', - 'definition' : 'role_definition', - 'directory' : 'role_directory', - 'document' : 'role_document', - 'form' : 'role_form', - 'group' : 'role_group', - 'heading' : 'role_heading', - 'img' : 'role_img', - 'list' : 'role_list', - 'listitem' : 'role_listitem', - 'main' : 'role_main', - 'math' : 'role_math', - 'navigation' : 'role_navigation', - 'note' : 'role_note', - 'region' : 'role_region', - 'rowheader' : 'role_rowheader', - 'search' : 'role_search', - 'separator' : 'role_separator' -}; - - -/** - * @type {Array<Object>} - */ -cvox.AriaUtil.ATTRIBUTE_VALUE_TO_STATUS = [ - { name: 'aria-autocomplete', values: - {'inline' : 'aria_autocomplete_inline', - 'list' : 'aria_autocomplete_list', - 'both' : 'aria_autocomplete_both'} }, - { name: 'aria-checked', values: - {'true' : 'aria_checked_true', - 'false' : 'aria_checked_false', - 'mixed' : 'aria_checked_mixed'} }, - { name: 'aria-disabled', values: - {'true' : 'aria_disabled_true'} }, - { name: 'aria-expanded', values: - {'true' : 'aria_expanded_true', - 'false' : 'aria_expanded_false'} }, - { name: 'aria-invalid', values: - {'true' : 'aria_invalid_true', - 'grammar' : 'aria_invalid_grammar', - 'spelling' : 'aria_invalid_spelling'} }, - { name: 'aria-multiline', values: - {'true' : 'aria_multiline_true'} }, - { name: 'aria-multiselectable', values: - {'true' : 'aria_multiselectable_true'} }, - { name: 'aria-pressed', values: - {'true' : 'aria_pressed_true', - 'false' : 'aria_pressed_false', - 'mixed' : 'aria_pressed_mixed'} }, - { name: 'aria-readonly', values: - {'true' : 'aria_readonly_true'} }, - { name: 'aria-required', values: - {'true' : 'aria_required_true'} }, - { name: 'aria-selected', values: - {'true' : 'aria_selected_true', - 'false' : 'aria_selected_false'} } -]; - - -/** - * Checks if a node should be treated as a hidden node because of its ARIA - * markup. - * - * @param {Node} targetNode The node to check. - * @return {boolean} True if the targetNode should be treated as hidden. - */ -cvox.AriaUtil.isHiddenRecursive = function(targetNode) { - if (cvox.AriaUtil.isHidden(targetNode)) { - return true; - } - var parent = targetNode.parentElement; - while (parent) { - if ((parent.getAttribute('aria-hidden') == 'true') && - (parent.getAttribute('chromevoxignoreariahidden') != 'true')) { - return true; - } - parent = parent.parentElement; - } - return false; -}; - - -/** - * Checks if a node should be treated as a hidden node because of its ARIA - * markup. Does not check parents, so if you need to know if this is a - * descendant of a hidden node, call isHiddenRecursive. - * - * @param {Node} targetNode The node to check. - * @return {boolean} True if the targetNode should be treated as hidden. - */ -cvox.AriaUtil.isHidden = function(targetNode) { - if (!targetNode) { - return true; - } - if (targetNode.getAttribute) { - if ((targetNode.getAttribute('aria-hidden') == 'true') && - (targetNode.getAttribute('chromevoxignoreariahidden') != 'true')) { - return true; - } - } - return false; -}; - - -/** - * Checks if a node should be treated as a visible node because of its ARIA - * markup, regardless of whatever other styling/attributes it may have. - * It is possible to force a node to be visible by setting aria-hidden to - * false. - * - * @param {Node} targetNode The node to check. - * @return {boolean} True if the targetNode should be treated as visible. - */ -cvox.AriaUtil.isForcedVisibleRecursive = function(targetNode) { - var node = targetNode; - while (node) { - if (node.getAttribute) { - // Stop and return the result based on the closest node that has - // aria-hidden set. - if (node.hasAttribute('aria-hidden') && - (node.getAttribute('chromevoxignoreariahidden') != 'true')) { - return node.getAttribute('aria-hidden') == 'false'; - } - } - node = node.parentElement; - } - return false; -}; - - -/** - * Checks if a node should be treated as a leaf node because of its ARIA - * markup. Does not check recursively, and does not check isControlWidget. - * Note that elements with aria-label are treated as leaf elements. See: - * http://www.w3.org/TR/wai-aria/roles#textalternativecomputation - * - * @param {Element} targetElement The node to check. - * @return {boolean} True if the targetNode should be treated as a leaf node. - */ -cvox.AriaUtil.isLeafElement = function(targetElement) { - var role = targetElement.getAttribute('role'); - var hasArialLabel = targetElement.hasAttribute('aria-label') && - (targetElement.getAttribute('aria-label').length > 0); - return (role == 'img' || role == 'progressbar' || hasArialLabel); -}; - - -/** - * Determines whether or not a node is or is the descendant of a node - * with a particular role. - * - * @param {Node} node The node to be checked. - * @param {string} roleName The role to check for. - * @return {boolean} True if the node or one of its ancestor has the specified - * role. - */ -cvox.AriaUtil.isDescendantOfRole = function(node, roleName) { - while (node) { - if (roleName && node && (node.getAttribute('role') == roleName)) { - return true; - } - node = node.parentNode; - } - return false; -}; - - -/** - * Helper function to return the role name message identifier for a role. - * @param {string} role The role. - * @return {?string} The role name message identifier. - * @private - */ -cvox.AriaUtil.getRoleNameMsgForRole_ = function(role) { - var msgId = cvox.AriaUtil.WIDGET_ROLE_TO_NAME[role]; - if (!msgId) { - return null; - } - return msgId; -}; - -/** - * Returns true is the node is any kind of button. - * - * @param {Node} node The node to check. - * @return {boolean} True if the node is a button. - */ -cvox.AriaUtil.isButton = function(node) { - var role = cvox.AriaUtil.getRoleAttribute(node); - if (role == 'button') { - return true; - } - if (node.tagName == 'BUTTON') { - return true; - } - if (node.tagName == 'INPUT') { - return (node.type == 'submit' || - node.type == 'reset' || - node.type == 'button'); - } - return false; -}; - -/** - * Returns a role message identifier for a node. - * For a localized string, see cvox.AriaUtil.getRoleName. - * @param {Node} targetNode The node to get the role name for. - * @return {string} The role name message identifier for the targetNode. - */ -cvox.AriaUtil.getRoleNameMsg = function(targetNode) { - var roleName; - if (targetNode && targetNode.getAttribute) { - var role = cvox.AriaUtil.getRoleAttribute(targetNode); - - // Special case for pop-up buttons. - if (targetNode.getAttribute('aria-haspopup') == 'true' && - cvox.AriaUtil.isButton(targetNode)) { - return 'role_popup_button'; - } - - if (role) { - roleName = cvox.AriaUtil.getRoleNameMsgForRole_(role); - if (!roleName) { - roleName = cvox.AriaUtil.STRUCTURE_ROLE_TO_NAME[role]; - } - } - - // To a user, a menu item within a menu bar is called a "menu"; - // any other menu item is called a "menu item". - // - // TODO(deboer): This block feels like a hack. dmazzoni suggests - // using css-like syntax for names. Investigate further if - // we need more of these hacks. - if (role == 'menuitem') { - var container = targetNode.parentElement; - while (container) { - if (container.getAttribute && - (cvox.AriaUtil.getRoleAttribute(container) == 'menu' || - cvox.AriaUtil.getRoleAttribute(container) == 'menubar')) { - break; - } - container = container.parentElement; - } - if (container && cvox.AriaUtil.getRoleAttribute(container) == 'menubar') { - roleName = cvox.AriaUtil.getRoleNameMsgForRole_('menu'); - } // else roleName is already 'Menu item', no need to change it. - } - } - if (!roleName) { - roleName = ''; - } - return roleName; -}; - -/** - * Returns a string to be presented to the user that identifies what the - * targetNode's role is. - * - * @param {Node} targetNode The node to get the role name for. - * @return {string} The role name for the targetNode. - */ -cvox.AriaUtil.getRoleName = function(targetNode) { - var roleMsg = cvox.AriaUtil.getRoleNameMsg(targetNode); - var roleName = Msgs.getMsg(roleMsg); - var role = cvox.AriaUtil.getRoleAttribute(targetNode); - if ((role == 'heading') && (targetNode.hasAttribute('aria-level'))) { - roleName += ' ' + targetNode.getAttribute('aria-level'); - } - return roleName ? roleName : ''; -}; - -/** - * Returns a string that gives information about the state of the targetNode. - * - * @param {Node} targetNode The node to get the state information for. - * @param {boolean} primary Whether this is the primary node we're - * interested in, where we might want extra information - as - * opposed to an ancestor, where we might be more brief. - * @return {cvox.NodeState} The status information about the node. - */ -cvox.AriaUtil.getStateMsgs = function(targetNode, primary) { - var state = []; - if (!targetNode || !targetNode.getAttribute) { - return state; - } - - for (var i = 0, attr; attr = cvox.AriaUtil.ATTRIBUTE_VALUE_TO_STATUS[i]; - i++) { - var value = targetNode.getAttribute(attr.name); - var msgId = attr.values[value]; - if (msgId) { - state.push([msgId]); - } - } - if (targetNode.getAttribute('role') == 'grid') { - return cvox.AriaUtil.getGridState_(targetNode, targetNode); - } - - var role = cvox.AriaUtil.getRoleAttribute(targetNode); - if (targetNode.getAttribute('aria-haspopup') == 'true') { - if (role == 'menuitem') { - state.push(['has_submenu']); - } else if (cvox.AriaUtil.isButton(targetNode)) { - // Do nothing - the role name will be 'pop-up button'. - } else { - state.push(['has_popup']); - } - } - - var valueText = targetNode.getAttribute('aria-valuetext'); - if (valueText) { - // If there is a valueText, that always wins. - state.push(['aria_value_text', valueText]); - return state; - } - - var valueNow = targetNode.getAttribute('aria-valuenow'); - var valueMin = targetNode.getAttribute('aria-valuemin'); - var valueMax = targetNode.getAttribute('aria-valuemax'); - - // Scrollbar and progressbar should speak the percentage. - // http://www.w3.org/TR/wai-aria/roles#scrollbar - // http://www.w3.org/TR/wai-aria/roles#progressbar - if ((valueNow != null) && (valueMin != null) && (valueMax != null)) { - if ((role == 'scrollbar') || (role == 'progressbar')) { - var percent = Math.round((valueNow / (valueMax - valueMin)) * 100); - state.push(['state_percent', percent]); - return state; - } - } - - // Return as many of the value attributes as possible. - if (valueNow != null) { - state.push(['aria_value_now', valueNow]); - } - if (valueMin != null) { - state.push(['aria_value_min', valueMin]); - } - if (valueMax != null) { - state.push(['aria_value_max', valueMax]); - } - - // If this is a composite control or an item within a composite control, - // get the index and count of the current descendant or active - // descendant. - var parentControl = targetNode; - var currentDescendant = null; - - if (cvox.AriaUtil.isCompositeControl(parentControl) && primary) { - currentDescendant = cvox.AriaUtil.getActiveDescendant(parentControl); - } else { - role = cvox.AriaUtil.getRoleAttribute(targetNode); - if (role == 'option' || - role == 'menuitem' || - role == 'menuitemcheckbox' || - role == 'menuitemradio' || - role == 'radio' || - role == 'tab' || - role == 'treeitem') { - currentDescendant = targetNode; - parentControl = targetNode.parentElement; - while (parentControl && - !cvox.AriaUtil.isCompositeControl(parentControl)) { - parentControl = parentControl.parentElement; - if (parentControl && - cvox.AriaUtil.getRoleAttribute(parentControl) == 'treeitem') { - break; - } - } - } - } - - if (parentControl && - (cvox.AriaUtil.isCompositeControl(parentControl) || - cvox.AriaUtil.getRoleAttribute(parentControl) == 'treeitem') && - currentDescendant) { - var parentRole = cvox.AriaUtil.getRoleAttribute(parentControl); - var descendantRoleList; - switch (parentRole) { - case 'combobox': - case 'listbox': - descendantRoleList = ['option']; - break; - case 'menu': - descendantRoleList = ['menuitem', - 'menuitemcheckbox', - 'menuitemradio']; - break; - case 'radiogroup': - descendantRoleList = ['radio']; - break; - case 'tablist': - descendantRoleList = ['tab']; - break; - case 'tree': - case 'treegrid': - case 'treeitem': - descendantRoleList = ['treeitem']; - break; - } - - if (descendantRoleList) { - var listLength; - var currentIndex; - - var ariaLength = - parseInt(currentDescendant.getAttribute('aria-setsize'), 10); - if (!isNaN(ariaLength)) { - listLength = ariaLength; - } - var ariaIndex = - parseInt(currentDescendant.getAttribute('aria-posinset'), 10); - if (!isNaN(ariaIndex)) { - currentIndex = ariaIndex; - } - - if (listLength == undefined || currentIndex == undefined) { - var descendants = cvox.AriaUtil.getNextLevel(parentControl, - descendantRoleList); - if (listLength == undefined) { - listLength = descendants.length; - } - if (currentIndex == undefined) { - for (var j = 0; j < descendants.length; j++) { - if (descendants[j] == currentDescendant) { - currentIndex = j + 1; - } - } - } - } - if (currentIndex && listLength) { - state.push(['list_position', currentIndex, listLength]); - } - } - } - return state; -}; - - -/** - * Returns a string that gives information about the state of the grid node. - * - * @param {Node} targetNode The node to get the state information for. - * @param {Node} parentControl The parent composite control. - * @return {cvox.NodeState} The status information about the node. - * @private - */ -cvox.AriaUtil.getGridState_ = function(targetNode, parentControl) { - var activeDescendant = cvox.AriaUtil.getActiveDescendant(parentControl); - - if (activeDescendant) { - var descendantSelector = '*[role~="row"]'; - var rows = parentControl.querySelectorAll(descendantSelector); - var currentIndex = null; - for (var j = 0; j < rows.length; j++) { - var gridcells = rows[j].querySelectorAll('*[role~="gridcell"]'); - for (var k = 0; k < gridcells.length; k++) { - if (gridcells[k] == activeDescendant) { - return /** @type {cvox.NodeState} */ ( - [['role_gridcell_pos', j + 1, k + 1]]); - } - } - } - } - return []; -}; - - -/** - * Returns the id of a node's active descendant - * @param {Node} targetNode The node. - * @return {?string} The id of the active descendant. - * @private - */ -cvox.AriaUtil.getActiveDescendantId_ = function(targetNode) { - if (!targetNode.getAttribute) { - return null; - } - - var activeId = targetNode.getAttribute('aria-activedescendant'); - if (!activeId) { - return null; - } - return activeId; -}; - - -/** - * Returns the list of elements that are one aria-level below. - * - * @param {Node} parentControl The node whose descendants should be analyzed. - * @param {Array<string>} role The role(s) of descendant we are looking for. - * @return {Array<Node>} The array of matching nodes. - */ -cvox.AriaUtil.getNextLevel = function(parentControl, role) { - var result = []; - var children = parentControl.childNodes; - var length = children.length; - for (var i = 0; i < children.length; i++) { - if (cvox.AriaUtil.isHidden(children[i]) || - !cvox.DomUtil.isVisible(children[i])) { - continue; - } - var nextLevel = cvox.AriaUtil.getNextLevelItems(children[i], role); - if (nextLevel.length > 0) { - result = result.concat(nextLevel); - } - } - return result; -}; - - -/** - * Recursively finds the first node(s) that match the role. - * - * @param {Element} current The node to start looking at. - * @param {Array<string>} role The role(s) to match. - * @return {Array<Element>} The array of matching nodes. - */ -cvox.AriaUtil.getNextLevelItems = function(current, role) { - if (current.nodeType != 1) { // If reached a node that is not an element. - return []; - } - if (role.indexOf(cvox.AriaUtil.getRoleAttribute(current)) != -1) { - return [current]; - } else { - var children = current.childNodes; - var length = children.length; - if (length == 0) { - return []; - } else { - var resultArray = []; - for (var i = 0; i < length; i++) { - var result = cvox.AriaUtil.getNextLevelItems(children[i], role); - if (result.length > 0) { - resultArray = resultArray.concat(result); - } - } - return resultArray; - } - } -}; - - -/** - * If the node is an object with an active descendant, returns the - * descendant node. - * - * This function will fully resolve an active descendant chain. If a circular - * chain is detected, it will return null. - * - * @param {Node} targetNode The node to get descendant information for. - * @return {Node} The descendant node or null if no node exists. - */ -cvox.AriaUtil.getActiveDescendant = function(targetNode) { - var seenIds = {}; - var node = targetNode; - - while (node) { - var activeId = cvox.AriaUtil.getActiveDescendantId_(node); - if (!activeId) { - break; - } - if (activeId in seenIds) { - // A circlar activeDescendant is an error, so return null. - return null; - } - seenIds[activeId] = true; - node = document.getElementById(activeId); - } - - if (node == targetNode) { - return null; - } - return node; -}; - - -/** - * Given a node, returns true if it's an ARIA control widget. Control widgets - * are treated as leaf nodes. - * - * @param {Node} targetNode The node to be checked. - * @return {boolean} Whether the targetNode is an ARIA control widget. - */ -cvox.AriaUtil.isControlWidget = function(targetNode) { - if (targetNode && targetNode.getAttribute) { - var role = cvox.AriaUtil.getRoleAttribute(targetNode); - switch (role) { - case 'button': - case 'checkbox': - case 'combobox': - case 'listbox': - case 'menu': - case 'menuitemcheckbox': - case 'menuitemradio': - case 'radio': - case 'slider': - case 'progressbar': - case 'scrollbar': - case 'spinbutton': - case 'tab': - case 'tablist': - case 'textbox': - return true; - } - } - return false; -}; - - -/** - * Given a node, returns true if it's an ARIA composite control. - * - * @param {Node} targetNode The node to be checked. - * @return {boolean} Whether the targetNode is an ARIA composite control. - */ -cvox.AriaUtil.isCompositeControl = function(targetNode) { - if (targetNode && targetNode.getAttribute) { - var role = cvox.AriaUtil.getRoleAttribute(targetNode); - switch (role) { - case 'combobox': - case 'grid': - case 'listbox': - case 'menu': - case 'menubar': - case 'radiogroup': - case 'tablist': - case 'tree': - case 'treegrid': - return true; - } - } - return false; -}; - - -/** - * Given a node, returns its 'aria-live' value if it's a live region, or - * null otherwise. - * - * @param {Node} node The node to be checked. - * @return {?string} The live region value, like 'polite' or - * 'assertive', or null if 'off' or none. - */ -cvox.AriaUtil.getAriaLive = function(node) { - if (!node.hasAttribute) - return null; - var value = node.getAttribute('aria-live'); - if (value == 'off') { - return null; - } else if (value) { - return value; - } - var role = cvox.AriaUtil.getRoleAttribute(node); - switch (role) { - case 'alert': - return 'assertive'; - case 'log': - case 'status': - return 'polite'; - default: - return null; - } -}; - - -/** - * Given a node, returns its 'aria-atomic' value. - * - * @param {Node} node The node to be checked. - * @return {boolean} The aria-atomic live region value, either true or false. - */ -cvox.AriaUtil.getAriaAtomic = function(node) { - if (!node.hasAttribute) - return false; - var value = node.getAttribute('aria-atomic'); - if (value) { - return (value === 'true'); - } - var role = cvox.AriaUtil.getRoleAttribute(node); - if (role == 'alert') { - return true; - } - return false; -}; - - -/** - * Given a node, returns its 'aria-busy' value. - * - * @param {Node} node The node to be checked. - * @return {boolean} The aria-busy live region value, either true or false. - */ -cvox.AriaUtil.getAriaBusy = function(node) { - if (!node.hasAttribute) - return false; - var value = node.getAttribute('aria-busy'); - if (value) { - return (value === 'true'); - } - return false; -}; - - -/** - * Given a node, checks its aria-relevant attribute (with proper inheritance) - * and determines whether the given change (additions, removals, text, all) - * is relevant and should be announced. - * - * @param {Node} node The node to be checked. - * @param {string} change The name of the change to check - one of - * 'additions', 'removals', 'text', 'all'. - * @return {boolean} True if that change is relevant to that node as part of - * a live region. - */ -cvox.AriaUtil.getAriaRelevant = function(node, change) { - if (!node.hasAttribute) - return false; - var value; - if (node.hasAttribute('aria-relevant')) { - value = node.getAttribute('aria-relevant'); - } else { - value = 'additions text'; - } - if (value == 'all') { - value = 'additions removals text'; - } - - var tokens = value.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').split(' '); - - if (change == 'all') { - return (tokens.indexOf('additions') >= 0 && - tokens.indexOf('text') >= 0 && - tokens.indexOf('removals') >= 0); - } else { - return (tokens.indexOf(change) >= 0); - } -}; - - -/** - * Given a node, return all live regions that are either rooted at this - * node or contain this node. - * - * @param {Node} node The node to be checked. - * @return {Array<Element>} All live regions affected by this node changing. - */ -cvox.AriaUtil.getLiveRegions = function(node) { - var result = []; - if (node.querySelectorAll) { - var nodes = node.querySelectorAll( - '[role="alert"], [role="log"], [role="marquee"], ' + - '[role="status"], [role="timer"], [aria-live]'); - if (nodes) { - for (var i = 0; i < nodes.length; i++) { - result.push(nodes[i]); - } - } - } - - while (node) { - if (cvox.AriaUtil.getAriaLive(node)) { - result.push(node); - return result; - } - node = node.parentElement; - } - - return result; -}; - - -/** - * Checks to see whether or not a node is an ARIA landmark. - * - * @param {Node} node The node to be checked. - * @return {boolean} Whether or not the node is an ARIA landmark. - */ -cvox.AriaUtil.isLandmark = function(node) { - if (!node || !node.getAttribute) { - return false; - } - var role = cvox.AriaUtil.getRoleAttribute(node); - switch (role) { - case 'application': - case 'banner': - case 'complementary': - case 'contentinfo': - case 'form': - case 'main': - case 'navigation': - case 'search': - return true; - } - return false; -}; - - -/** - * Checks to see whether or not a node is an ARIA grid. - * - * @param {Node} node The node to be checked. - * @return {boolean} Whether or not the node is an ARIA grid. - */ -cvox.AriaUtil.isGrid = function(node) { - if (!node || !node.getAttribute) { - return false; - } - var role = cvox.AriaUtil.getRoleAttribute(node); - switch (role) { - case 'grid': - case 'treegrid': - return true; - } - return false; -}; - - -/** - * Returns the id of an earcon to play along with the description for a node. - * - * @param {Node} node The node to get the earcon for. - * @return {cvox.Earcon?} The earcon id, or null if none applies. - */ -cvox.AriaUtil.getEarcon = function(node) { - if (!node || !node.getAttribute) { - return null; - } - var role = cvox.AriaUtil.getRoleAttribute(node); - switch (role) { - case 'button': - return cvox.Earcon.BUTTON; - case 'checkbox': - case 'radio': - case 'menuitemcheckbox': - case 'menuitemradio': - var checked = node.getAttribute('aria-checked'); - if (checked == 'true') { - return cvox.Earcon.CHECK_ON; - } else { - return cvox.Earcon.CHECK_OFF; - } - case 'combobox': - case 'listbox': - return cvox.Earcon.LISTBOX; - case 'textbox': - return cvox.Earcon.EDITABLE_TEXT; - case 'listitem': - return cvox.Earcon.LIST_ITEM; - case 'link': - return cvox.Earcon.LINK; - } - - return null; -}; - - -/** - * Returns the role of the node. - * - * This is equivalent to targetNode.getAttribute('role') - * except it also takes into account cases where ChromeVox - * itself has changed the role (ie, adding role="application" - * to BODY elements for better screen reader compatibility. - * - * @param {Node} targetNode The node to get the role for. - * @return {string} role of the targetNode. - */ -cvox.AriaUtil.getRoleAttribute = function(targetNode) { - if (!targetNode.getAttribute) { - return ''; - } - var role = targetNode.getAttribute('role'); - if (targetNode.hasAttribute('chromevoxoriginalrole')) { - role = targetNode.getAttribute('chromevoxoriginalrole'); - } - return role; -}; - - -/** - * Checks to see whether or not a node is an ARIA math node. - * - * @param {Node} node The node to be checked. - * @return {boolean} Whether or not the node is an ARIA math node. - */ -cvox.AriaUtil.isMath = function(node) { - if (!node || !node.getAttribute) { - return false; - } - var role = cvox.AriaUtil.getRoleAttribute(node); - return role == 'math'; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs deleted file mode 100644 index bf13830445b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/aria_util_test.unitjs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture for aria_util.js. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxAriaUtilUnitTest() {} - -CvoxAriaUtilUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.AriaUtil', - 'cvox.ChromeVox', - 'cvox.DomUtil',] -}; - -TEST_F('CvoxAriaUtilUnitTest', 'GetStateGridWithActiveCell', function() { - this.loadDoc(function() {/*! - <div id="grid" role="grid" aria-activedescendant="cell"> - <div role="row"> - <div id="cell" role="gridcell"> - </div> - </div> - */}); - assertThat( - cvox.AriaUtil.getStateMsgs($('grid'), true), - eqJSON([['role_gridcell_pos', 1, 1]])); -}); - -TEST_F('CvoxAriaUtilUnitTest', 'GetActiveDescendant', function() { - this.loadDoc(function() {/*! - <div id="top" aria-activedescendant="child"> - <div id="child" /> - </div> - <div id="top_2" aria-activedescendant="child_2"> - <div id="child_2" aria-activedescendant="grandchild_2"> - <div id="grandchild_2" /> - </div> - </div> - - <h1>The buggy cases.</h1> - <div id="loop" aria-activedescendant="loop" /> - <div id="circleA" aria-activedescendant="circleB"> - <div id="circleB" aria-activedescendant="circleA" /> - </div> - */}); - - // The typical case. - var topElt = $('top'); - var childElt = $('child'); - assertEquals(childElt, cvox.AriaUtil.getActiveDescendant(topElt)); - - // childElt has not aria-activedescendant, so return null. - assertEquals(null, cvox.AriaUtil.getActiveDescendant(childElt)); - - // The chained case. - var top2Elt = $('top_2'); - var grandchild2Elt = $('grandchild_2'); - assertEquals(grandchild2Elt, cvox.AriaUtil.getActiveDescendant(top2Elt)); - - // The buggy cases. These are invalid, so return null as if the - // aria-activedescendant tags did not exist. - var loopElt = $('loop'); - assertEquals(null, cvox.AriaUtil.getActiveDescendant(loopElt)); - - var circleAElt = $('circleA'); - assertEquals(null, cvox.AriaUtil.getActiveDescendant(circleAElt)); -}); - -TEST_F('CvoxAriaUtilUnitTest', 'ListIndexAndState', function() { - this.loadDoc(function() {/*! - <div id="l" role="listbox" tabindex="0" aria-activedescendant="l2"> - <div id="l1" role="option">A</div> - <div id="l2" role="option">B</div> - <div id="l3" role="option">C</div> - </div> - <div id="a" role="listbox" tabindex="0" aria-activedescendant="a2"> - <div id="a1" role="option" aria-setsize="10" aria-posinset="5">A</div> - <div id="a2" role="option" aria-setsize="20" aria-posinset="15">B</div> - <div id="a3" role="option" aria-setsize="30" aria-posinset="25">C</div> - </div> - <div id="b" role="listbox" tabindex="0" aria-activedescendant="b2"> - <div id="b1" role="option" aria-posinset="3">A</div> - <div id="b2" role="option" aria-posinset="2">B</div> - <div id="b3" role="option" aria-posinset="1">C</div> - </div> - */}); - - var optionElt = $('l2'); - assertThat( - cvox.AriaUtil.getStateMsgs(optionElt), - eqJSON([['list_position', 2, 3]])); - - var ariaOptionElt = $('a2'); - assertThat( - cvox.AriaUtil.getStateMsgs(ariaOptionElt), - eqJSON([['list_position', 15, 20]])); - - ariaOptionElt = $('b3'); - assertThat( - cvox.AriaUtil.getStateMsgs(ariaOptionElt), - eqJSON([['list_position', 1, 3]])); -}); - -TEST_F('CvoxAriaUtilUnitTest', 'GetLiveRegions', function() { - this.loadDoc(function() {/*! - <div id="outer"> - <div id="progress" role="progressbar" aria-live="polite" aria-valuenow="1"> - <div id="ptext"> - 1% complete. - </div> - </div> - <div id="progress2" role="progressbar" aria-live="polite" aria-valuenow="1"> - <div id="ptext2"> - 1% complete. - </div> - </div> - </div> - */}); - - var progressLiveRegions = cvox.AriaUtil.getLiveRegions(progress); - assertEquals(1, progressLiveRegions.length); - assertNotEquals(-1, progressLiveRegions.indexOf(progress)); - - var outerLiveRegions = cvox.AriaUtil.getLiveRegions(outer); - assertEquals(2, outerLiveRegions.length); - assertNotEquals(-1, outerLiveRegions.indexOf(progress)); - assertNotEquals(-1, outerLiveRegions.indexOf(progress2)); - - // getLiveRegions works walking up the tree as well. - var ptextLiveRegions = cvox.AriaUtil.getLiveRegions(ptext); - assertEquals(1, ptextLiveRegions.length); - assertNotEquals(-1, ptextLiveRegions.indexOf(progress)); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/aural_style_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/aural_style_util.js deleted file mode 100644 index 0e8c3b5cf57..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/aural_style_util.js +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A set of classes to support aural CSS. - */ - - -goog.provide('cvox.AuralProperty'); -goog.provide('cvox.AuralStyleConverter'); -goog.provide('cvox.AuralStyleUtil'); - -// This seems the only way to lay out an enum and use it as a key. -/** - * @enum {string} - */ -cvox.AuralProperty = { - VOLUME: 'VOLUME', - SPEAK: 'SPEAK', - PAUSE_BEFORE: 'PAUSE_BEFORE', - PAUSE_AFTER: 'PAUSE_AFTER', - PAUSE: 'PAUSE', - CUE_BEFORE: 'CUE_BEFORE', - CUE_AFTER: 'CUE_AFTER', - CUE: 'CUE', - PLAY_DURING: 'PLAY_DURING', - AZIMUTH: 'AZIMUTH', - ELEVATION: 'ELEVATION', - SPEECH_RATE: 'SPEECH_RATE', - VOICE_FAMILY: 'VOICE_FAMILY', - PITCH: 'PITCH', - PITCH_RANGE: 'PITCH_RANGE', - STRESS: 'STRESS', - RICHNESS: 'RICHNESS', - SPEAK_PUNCTUATION: 'SPEAK_PUNCTUATION', - SPEAK_NUMERIAL: 'SPEAK_NUMERIAL', - SPEAK_HEADER: 'SPEAK_HEADER', - NONE: 'NONE' -}; - - -/* A series of conversion functions. */ -/** - * An identity conversion. - * @param {number} value The aural CSS value to convert. - * @return {number} The resulting tts property value. - */ -cvox.AuralStyleConverter.identity = function(value) { - return value; -}; - - -/** - * Conversion from an aural style property to Chrome TTS property. - * TODO(dtseng): no-op's below need to be supported by the extension API itself - * or by ChromeVox. - * @type {Object<cvox.AuralProperty, string>} - */ -cvox.AuralStyleConverter.propertyTable = { - VOLUME: 'volume', - SPEAK: 'no-op', - PAUSE_BEFORE: 'no-op', - PAUSE_AFTER: 'no-op', - PAUSE: 'no-op', - CUE_BEFORE: 'no-op', - CUE_AFTER: 'no-op', - CUE: 'no-op', - PLAY_DURING: 'no-op', - AZIMUTH: 'no-op', - ELEVATION: 'no-op', - SPEECH_RATE: 'relativeRate', - VOICE_FAMILY: 'no-op', - PITCH: 'relativePitch', - PITCH_RANGE: 'no-op', - STRESS: 'no-op', - RICHNESS: 'no-op', - SPEAK_PUNCTUATION: 'no-op', - SPEAK_NUMERIAL: 'no-op', - SPEAK_HEADER: 'no-op', - NONE: 'no-op' -}; - - -/** - * Conversion from an aural style value to Chrome TTS value. - * TODO(dtseng): Conversion of aural CSS values is incomplete; everything is an - * identity conversion at the moment. - * @type {Object<cvox.AuralProperty, function(*)>} - */ -cvox.AuralStyleConverter.valueTable = { - VOLUME: cvox.AuralStyleConverter.identity, - SPEAK: cvox.AuralStyleConverter.identity, - PAUSE_BEFORE: cvox.AuralStyleConverter.identity, - PAUSE_AFTER: cvox.AuralStyleConverter.identity, - PAUSE: cvox.AuralStyleConverter.identity, - CUE_BEFORE: cvox.AuralStyleConverter.identity, - CUE_AFTER: cvox.AuralStyleConverter.identity, - CUE: cvox.AuralStyleConverter.identity, - PLAY_DURING: cvox.AuralStyleConverter.identity, - AZIMUTH: cvox.AuralStyleConverter.identity, - ELEVATION: cvox.AuralStyleConverter.identity, - SPEECH_RATE: cvox.AuralStyleConverter.identity, - VOICE_FAMILY: cvox.AuralStyleConverter.identity, - PITCH: cvox.AuralStyleConverter.identity, - PITCH_RANGE: cvox.AuralStyleConverter.identity, - STRESS: cvox.AuralStyleConverter.identity, - RICHNESS: cvox.AuralStyleConverter.identity, - SPEAK_PUNCTUATION: cvox.AuralStyleConverter.identity, - SPEAK_NUMERIAL: cvox.AuralStyleConverter.identity, - SPEAK_HEADER: cvox.AuralStyleConverter.identity, - NONE: cvox.AuralStyleConverter.identity -}; - - -/** - * Converts a given aural property/value rule to a tts property/value. - * @param {cvox.AuralProperty} property The property. - * @param {*} value The CSS-based value. - * @return {Object} An object holding tts property and value. - */ -cvox.AuralStyleConverter.convertRule = function(property, value) { - return { - property: cvox.AuralStyleConverter.propertyTable[property], - value: cvox.AuralStyleConverter.valueTable[property](value) - }; -}; - - -/** - * Converts an aural CSS style block to a TTS property object. - * @param {Object<cvox.AuralProperty, *>} style The style. - * @return {Object} The tts property object. - */ -cvox.AuralStyleConverter.convertStyle = function(style) { - var ttsProperties = {}; - for (var property in style) { - var ttsProperty = - cvox.AuralStyleConverter.convertRule(property, style[property]); - ttsProperties[ttsProperty.property] = ttsProperty.value; - } - return ttsProperties; -}; - - -/** - * Gets the aural style for a node. - * @param {Node} node The node. - * @return {Object} The aural style, converted to tts properties. -*/ -cvox.AuralStyleUtil.getStyleForNode = function(node) { - var style = cvox.AuralStyleUtil.defaultStyles[node.tagName]; - if (!style) { - return null; - } - return cvox.AuralStyleConverter.convertStyle(style); -}; - - -/** - * A list of default aural styles. - */ -cvox.AuralStyleUtil.defaultStyles = { - 'ARTICLE': { - PITCH: -0.1 - }, - 'ASIDE': { - PITCH: -0.1 - }, - 'FOOTER': { - PITCH: -0.1 - }, - 'H1': { - PITCH: -0.3 - }, - 'H2': { - PITCH: -0.25 - }, - 'H3': { - PITCH: -0.2 - }, - 'H4': { - PITCH: -0.15 - }, - 'H5': { - PITCH: -0.1 - }, - 'H6': { - PITCH: -0.05 - }, - 'HEADER': { - PITCH: -0.1 - }, - 'HGROUP': { - PITCH: -0.1 - }, - 'MARK': { - PITCH: -0.1 - }, - 'NAV': { - PITCH: -0.1 - }, - 'SECTION': { - PITCH: -0.1 - }, - 'TIME': { - PITCH: -0.1 - } -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler.js deleted file mode 100644 index 36d3e04cba8..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler.js +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.BrailleTextHandler'); - -goog.require('cvox.BrailleInterface'); -goog.require('cvox.BrailleUtil'); -goog.require('cvox.ChromeVox'); -goog.require('cvox.NavBraille'); - -/** - * @fileoverview Updates braille display contents following text changes. - * - */ - -/** - * Represents an editable text region. - * - * @constructor - * @param {!cvox.BrailleInterface} braille Braille interface. - */ -cvox.BrailleTextHandler = function(braille) { - /** - * Braille interface used to produce output. - * @type {!cvox.BrailleInterface} - * @private - */ - this.braille_ = braille; -}; - - -/** - * Called by controller class when text changes. - * @param {string} line The text of the line. - * @param {number} start The 0-based index starting selection. - * @param {number} end The 0-based index ending selection. - * @param {boolean} multiline True if the text comes from a multi line text - * field. - * @param {Element} element DOM node which line comes from. - * @param {number} lineStart Start offset of line (might be > 0 for multiline - * fields). - */ -cvox.BrailleTextHandler.prototype.changed = function( - line, start, end, multiline, element, lineStart) { - var content; - if (multiline) { - var spannable = cvox.BrailleUtil.createValue(line, start, end, lineStart); - if (element) { - spannable.setSpan(element, 0, line.length); - } - content = new cvox.NavBraille({text: spannable, - startIndex: start, - endIndex: end}); - } else { - if (cvox.ChromeVox.navigationManager) { - content = cvox.ChromeVox.navigationManager.getBraille(); - } - } - if (content) { - this.braille_.write(content); - } -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs deleted file mode 100644 index 3f53422bd38..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_text_handler_test.unitjs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * @extends {cvox.BrailleInterface} - * @constructor - */ -function FakeBraille() { -} - -FakeBraille.prototype = { - /** @override */ - write: function(content) { - this.content = content; - } -}; - -/** @constructor */ -function FakeNavigationManager() { -} - -FakeNavigationManager.prototype = { - getBraille: function() { - return this.navBraille; - }, - - setNavBraille: function(navBraille) { - this.navBraille = navBraille; - } -}; - - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxBrailleTextHandlerUnitTest() {} - -CvoxBrailleTextHandlerUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.BrailleInterface', - 'cvox.BrailleTextHandler', - 'cvox.NavBraille', - 'cvox.NavigationManager', - ], - - /** @override */ - setUp: function() { - this.navigationManager = new FakeNavigationManager(); - this.braille = new FakeBraille(); - cvox.ChromeVox.navigationManager = this.navigationManager; - this.brailleTextHandler = new cvox.BrailleTextHandler(this.braille); - } -}; - -TEST_F('CvoxBrailleTextHandlerUnitTest', 'UpdateByUser', function() { - var navBraille = new cvox.NavBraille({ text: 'Hello, world!' }); - this.navigationManager.setNavBraille(navBraille); - - this.brailleTextHandler.changed('', 0, 0, false); - assertEquals(navBraille, this.braille.content); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_util.js deleted file mode 100644 index 77ace8dcfc0..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_util.js +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A utility class for general braille functionality. - */ - - -goog.provide('cvox.BrailleUtil'); - -goog.require('cvox.ChromeVox'); -goog.require('cvox.DomUtil'); -goog.require('cvox.EditableTextAreaShadow'); -goog.require('cvox.Focuser'); -goog.require('cvox.NavBraille'); -goog.require('cvox.NodeStateUtil'); -goog.require('cvox.Spannable'); -goog.require('cvox.ValueSelectionSpan'); -goog.require('cvox.ValueSpan'); - - -/** - * Trimmable whitespace character that appears between consecutive items in - * braille. - * @const {string} - */ -cvox.BrailleUtil.ITEM_SEPARATOR = ' '; - - -/** - * Messages considered as containers in braille. - * Containers are distinguished from roles by their appearance higher up in the - * DOM tree of a selected node. - * This list should be very short. - * @type {!Array<string>} - */ -cvox.BrailleUtil.CONTAINER = [ - 'tag_h1_brl', - 'tag_h2_brl', - 'tag_h3_brl', - 'tag_h4_brl', - 'tag_h5_brl', - 'tag_h6_brl' -]; - - -/** - * Maps a ChromeVox message id to a braille template. - * The template takes one-character specifiers: - * n: replaced with braille name. - * r: replaced with braille role. - * s: replaced with braille state. - * c: replaced with braille container role; this potentially returns whitespace, - * so place at the beginning or end of templates for trimming. - * v: replaced with braille value. - * @type {Object<string>} - */ -cvox.BrailleUtil.TEMPLATE = { - 'base': 'c n v r s', - 'role_alert': 'r: n', - 'role_button': 'n r s', - 'role_checkbox': 'n r s', - 'role_menuitemcheckbox': 'n r s', - 'role_menuitemradio': 'n r s', - 'role_radio': 'n r s', - 'role_textbox': 'n: v r s', - 'input_type_email': 'n: v r s', - 'input_type_number': 'n: v r s', - 'input_type_password': 'n: v r s', - 'input_type_search': 'n: v r s', - 'input_type_text': 'n: v r s', - 'input_type_url': 'n: v r s', - 'tag_textarea': 'n: v r s' -}; - - -/** - * Gets the braille name for a node. - * See DomUtil for a more precise definition of 'name'. - * Additionally, whitespace is trimmed. - * @param {Node} node The node. - * @return {string} The string representation. - */ -cvox.BrailleUtil.getName = function(node) { - if (!node) { - return ''; - } - return cvox.DomUtil.getName(node).trim(); -}; - - -/** - * Gets the braille role message id for a node. - * See DomUtil for a more precise definition of 'role'. - * @param {Node} node The node. - * @return {string} The string representation. - */ -cvox.BrailleUtil.getRoleMsg = function(node) { - if (!node) { - return ''; - } - var roleMsg = cvox.DomUtil.getRoleMsg(node, cvox.VERBOSITY_VERBOSE); - if (roleMsg) { - roleMsg = cvox.DomUtil.collapseWhitespace(roleMsg); - } - if (roleMsg && (roleMsg.length > 0)) { - if (Msgs.getMsg(roleMsg + '_brl')) { - roleMsg += '_brl'; - } - } - return roleMsg; -}; - - -/** - * Transforms a {@code cvox.NodeState} list of state messages to the - * corresponding messages for braille and expands them into a localized - * string suitable for output on a braille display. - * @param {cvox.NodeState} stateMsgs The states to expand. The content of this - * array is modified. - * @return {string} The string representation. - * @private - */ -cvox.BrailleUtil.expandStateMsgs_ = function(stateMsgs) { - stateMsgs.forEach(function(state) { - // Check to see if a variant of the message with '_brl' exists, - // and use it if so. - // - // Note: many messages are templatized, and if we don't pass any - // argument to substitute, getMsg might throw an error if the - // resulting string is empty. To avoid this, we pass a dummy - // substitution string array here. - var dummySubs = ['dummy', 'dummy', 'dummy']; - if (Msgs.getMsg(state[0] + '_brl', dummySubs)) { - state[0] += '_brl'; - } - }); - return cvox.NodeStateUtil.expand(stateMsgs); -}; - - -/** - * Gets the braille container role of a node. - * @param {Node} prev The previous node in navigation. - * @param {Node} node The node. - * @return {string} The string representation. - */ -cvox.BrailleUtil.getContainer = function(prev, node) { - if (!prev || !node) { - return ''; - } - var ancestors = cvox.DomUtil.getUniqueAncestors(prev, node); - for (var i = 0, container; container = ancestors[i]; i++) { - var msg = cvox.BrailleUtil.getRoleMsg(container); - if (msg && cvox.BrailleUtil.CONTAINER.indexOf(msg) != -1) { - return Msgs.getMsg(msg); - } - } - return ''; -}; - - -/** - * Gets the braille value of a node. A {@code cvox.ValueSpan} will be - * attached, along with (possibly) a {@code cvox.ValueSelectionSpan}. - * @param {Node} node The node. - * @return {!cvox.Spannable} The value spannable. - */ -cvox.BrailleUtil.getValue = function(node) { - if (!node) { - return new cvox.Spannable(); - } - var valueSpan = new cvox.ValueSpan(0 /* offset */); - if (cvox.DomUtil.isInputTypeText(node)) { - var value = node.value; - if (node.type === 'password') { - value = value.replace(/./g, '*'); - } - var spannable = new cvox.Spannable(value, valueSpan); - if (node === document.activeElement && - cvox.DomUtil.doesInputSupportSelection(node)) { - var selectionStart = cvox.BrailleUtil.clamp_( - node.selectionStart, 0, spannable.getLength()); - var selectionEnd = cvox.BrailleUtil.clamp_( - node.selectionEnd, 0, spannable.getLength()); - spannable.setSpan(new cvox.ValueSelectionSpan(), - Math.min(selectionStart, selectionEnd), - Math.max(selectionStart, selectionEnd)); - } - return spannable; - } else if (node instanceof HTMLTextAreaElement) { - var shadow = new cvox.EditableTextAreaShadow(); - shadow.update(node); - var lineIndex = shadow.getLineIndex(node.selectionEnd); - var lineStart = shadow.getLineStart(lineIndex); - var lineEnd = shadow.getLineEnd(lineIndex); - var lineText = node.value.substring(lineStart, lineEnd); - valueSpan.offset = lineStart; - var spannable = new cvox.Spannable(lineText, valueSpan); - if (node === document.activeElement) { - var selectionStart = cvox.BrailleUtil.clamp_( - node.selectionStart - lineStart, 0, spannable.getLength()); - var selectionEnd = cvox.BrailleUtil.clamp_( - node.selectionEnd - lineStart, 0, spannable.getLength()); - spannable.setSpan(new cvox.ValueSelectionSpan(), - Math.min(selectionStart, selectionEnd), - Math.max(selectionStart, selectionEnd)); - } - return spannable; - } else { - return new cvox.Spannable(cvox.DomUtil.getValue(node), valueSpan); - } -}; - - -/** - * Gets the templated representation of braille. - * @param {Node} prev The previous node (during navigation). - * @param {Node} node The node. - * @param {{name:(undefined|string), - * role:(undefined|string), - * roleMsg:(undefined|string), - * state:(undefined|string), - * container:(undefined|string), - * value:(undefined|cvox.Spannable)}|Object=} opt_override Override a - * specific property for the given node. - * @return {!cvox.Spannable} The string representation. - */ -cvox.BrailleUtil.getTemplated = function(prev, node, opt_override) { - opt_override = opt_override ? opt_override : {}; - var roleMsg = opt_override.roleMsg || - (node ? cvox.DomUtil.getRoleMsg(node, cvox.VERBOSITY_VERBOSE) : ''); - var template = cvox.BrailleUtil.TEMPLATE[roleMsg] || - cvox.BrailleUtil.TEMPLATE['base']; - var state = opt_override.state; - if (!state) { - if (node) { - state = cvox.BrailleUtil.expandStateMsgs_( - cvox.DomUtil.getStateMsgs(node, true)); - } else { - state = ''; - } - } - var role = opt_override.role || ''; - if (!role && roleMsg) { - role = Msgs.getMsg(roleMsg + '_brl') || - Msgs.getMsg(roleMsg); - } - - var templated = new cvox.Spannable(); - var mapChar = function(c) { - switch (c) { - case 'n': - return opt_override.name || cvox.BrailleUtil.getName(node); - case 'r': - return role; - case 's': - return state; - case 'c': - return opt_override.container || - cvox.BrailleUtil.getContainer(prev, node); - case 'v': - return opt_override.value || cvox.BrailleUtil.getValue(node); - default: - return c; - } - }; - for (var i = 0; i < template.length; i++) { - var component = mapChar(template[i]); - templated.append(component); - // Ignore the next whitespace separator if the current component is empty, - // unless the empty value has a selection, in which case the cursor - // should be placed on the empty space after the empty value. - if (!component.toString() && template[i + 1] == ' ' && - (!(component instanceof cvox.Spannable) || - !/**@type {cvox.Spannable}*/(component).getSpanInstanceOf( - cvox.ValueSelectionSpan))) { - i++; - } - } - return templated.trimRight(); -}; - - -/** - * Creates a braille value from a string and, optionally, a selection range. - * A {@code cvox.ValueSpan} will be attached, along with a - * {@code cvox.ValueSelectionSpan} if applicable. - * @param {string} text The text to display as the value. - * @param {number=} opt_selStart Selection start. - * @param {number=} opt_selEnd Selection end if different from selection start. - * @param {number=} opt_textOffset Start offset of text. - * @return {!cvox.Spannable} The value spannable. - */ -cvox.BrailleUtil.createValue = function(text, opt_selStart, opt_selEnd, - opt_textOffset) { - var spannable = new cvox.Spannable( - text, new cvox.ValueSpan(opt_textOffset || 0)); - if (goog.isDef(opt_selStart)) { - opt_selEnd = goog.isDef(opt_selEnd) ? opt_selEnd : opt_selStart; - // TODO(plundblad): This looses the distinction between the selection - // anchor (start) and focus (end). We should use that information to - // decide where to pan the braille display. - if (opt_selStart > opt_selEnd) { - var temp = opt_selStart; - opt_selStart = opt_selEnd; - opt_selEnd = temp; - } - - spannable.setSpan(new cvox.ValueSelectionSpan(), opt_selStart, opt_selEnd); - } - return spannable; -}; - - -/** - * Activates a position in a nav braille. Moves the caret in text fields - * and simulates a mouse click on the node at the position. - * - * @param {!cvox.NavBraille} braille the nav braille representing the display - * content that was active when the user issued the key command. - * The annotations in the spannable are used to decide what - * node to activate and what part of the node value (if any) to - * move the caret to. - * @param {number=} opt_displayPosition position of the display that the user - * activated, relative to the start of braille. - */ -cvox.BrailleUtil.click = function(braille, opt_displayPosition) { - var handled = false; - var spans = braille.text.getSpans(opt_displayPosition || 0); - var node = spans.filter(function(n) { return n instanceof Node; })[0]; - if (node) { - if (goog.isDef(opt_displayPosition) && - (cvox.DomUtil.isInputTypeText(node) || - node instanceof HTMLTextAreaElement)) { - var valueSpan = spans.filter( - function(s) { - return s instanceof cvox.ValueSpan; - })[0]; - if (valueSpan) { - if (document.activeElement !== node) { - cvox.Focuser.setFocus(node); - } - var cursorPosition = opt_displayPosition - - braille.text.getSpanStart(valueSpan) + - valueSpan.offset; - cvox.ChromeVoxEventWatcher.setUpTextHandler(); - node.selectionStart = node.selectionEnd = cursorPosition; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - handled = true; - } - } - } - if (!handled) { - cvox.DomUtil.clickElem( - node || cvox.ChromeVox.navigationManager.getCurrentNode(), - false, false, false, true); - } -}; - - -/** - * Clamps a number so it is within the given boundaries. - * @param {number} number The number to clamp. - * @param {number} min The minimum value to return. - * @param {number} max The maximum value to return. - * @return {number} {@code number} if it is within the bounds, or the nearest - * number within the bounds otherwise. - * @private - */ -cvox.BrailleUtil.clamp_ = function(number, min, max) { - return Math.min(Math.max(number, min), max); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs deleted file mode 100644 index be935d5df48..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/braille_util_test.unitjs +++ /dev/null @@ -1,420 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxBrailleUtilUnitTest() {} - -CvoxBrailleUtilUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.BrailleUtil', - 'cvox.CursorSelection', - 'cvox.NavigationShifter', - 'TestMsgs', - ], - - /** @override */ - setUp: function() { - Msgs = TestMsgs; - }, - - /** - * @param {!Node} expectedParent Expected parent node. - * @param {!Node} node Node to examine. - * @private - */ - assertTextNodeChildOf_: function(expectedParent, node) { - assertEquals(Node.TEXT_NODE, node.nodeType); - assertEquals(expectedParent, node.parentNode); - }, - - /** - * Helper to retrieve braille for testing. - * @param {!cvox.CursorSelection} prevSel Previous selection. - * @param {!cvox.CursorSelection} sel Current selection. - * @return {!cvox.NavBraille} Resulting braille. - * @private - */ - getBraille_: function(prevSel, sel) { - return (new cvox.NavigationShifter).getBraille(prevSel, sel); - }, - - /** - * Asserts that two NavBraille objects are equal, ignoring spans. - * @param {Object} expected Expected result, should have no spans. - * @param {cvox.NavBraille} actual Actual result. - */ - assertBrailleEquals: function(expected, actual) { - actual = new cvox.NavBraille({ - text: actual.text.toString(), - startIndex: actual.startIndex, - endIndex: actual.endIndex - }); - assertThat(actual, eqJSON(new cvox.NavBraille(expected))); - } -}; - -TEST_F('CvoxBrailleUtilUnitTest', 'BrailleName', function() { - this.loadHtml( - '<div id="navbar">' + - '<a id="1" href="one.com">one</a>' + - '<a id="2" href="two.com">two</a>' + - '<a id="3" href="three.com">three</a>' + - '</div>'); - var navbar = cvox.CursorSelection.fromNode($('navbar')); - var braille = this.getBraille_(navbar, navbar); - this.assertBrailleEquals( - {text: 'one lnk two lnk three lnk', - startIndex: 0, - endIndex: 1 - }, braille); - - var one = - cvox.CursorSelection.fromNode($('1').firstChild); - braille = this.getBraille_(one, one); - this.assertBrailleEquals( - {text: 'one lnk two lnk three lnk', - startIndex: 0, - endIndex: 1 - }, braille); - - var two = - cvox.CursorSelection.fromNode($('2').firstChild); - braille = this.getBraille_(one, two); - this.assertBrailleEquals( - {text: 'one lnk two lnk three lnk', - startIndex: 8, - endIndex: 9 - }, braille); - - var three = - cvox.CursorSelection.fromNode($('3').firstChild); - braille = this.getBraille_(two, three); - this.assertBrailleEquals( - {text: 'one lnk two lnk three lnk', - startIndex: 16, - endIndex: 17 - }, braille); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'NameTemplate', function() { - this.loadHtml( - '<button id="1">Submit</button>' + - '<input id="2" type="text" aria-label="Search">' - ); - - var button = cvox.CursorSelection.fromNode($('1')); - - this.assertBrailleEquals( - {text: 'Submit btn', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(button, button)); - - var inputElement = $('2'); - var input = cvox.CursorSelection.fromNode(inputElement); - - // Note: the cursor appears where text would be typed. - this.assertBrailleEquals( - {text: 'Search: ed', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(input, input)); - inputElement.focus(); - this.assertBrailleEquals( - {text: 'Search: ed', - startIndex: 8, - endIndex: 8 - }, this.getBraille_(input, input)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'TextField', function() { - this.loadHtml( - '<input id="1" type="text" aria-label="Search" value="larry">' - ); - - var inputElement = $('1'); - var input = cvox.CursorSelection.fromNode(inputElement); - - // Note: the cursor appears where text would be typed. - // The cursor is at the beginning by default. - this.assertBrailleEquals( - {text: 'Search: larry ed', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(input, input)); - inputElement.focus(); - inputElement.selectionStart = 0; - inputElement.selectionEnd = 5; - this.assertBrailleEquals( - {text: 'Search: larry ed', - startIndex: 8, - endIndex: 13 - }, this.getBraille_(input, input)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'TextFieldEmpty', function() { - this.loadHtml( - '<input id="1" type="text">' - ); - - var inputElement = $('1'); - var input = cvox.CursorSelection.fromNode($('1')); - - this.assertBrailleEquals( - {text: ': ed', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(input, input)); - inputElement.focus(); - this.assertBrailleEquals( - {text: ': ed', - startIndex: 2, - endIndex: 2 - }, this.getBraille_(input, input)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'TextFieldSelection', function() { - this.loadHtml( - '<input id="1" type="text" value="strawberry">' - ); - - var inputElem = $('1'); - inputElem.focus(); - var input = cvox.CursorSelection.fromNode(inputElem); - - // Selection. - inputElem.selectionStart = 2; - inputElem.selectionEnd = 5; - this.assertBrailleEquals( - {text: ': strawberry ed', - startIndex: 4, - endIndex: 7 - }, this.getBraille_(input, input)); - - // Cursor at end. - inputElem.selectionStart = 10; - inputElem.selectionEnd = 10; - this.assertBrailleEquals( - {text: ': strawberry ed', - startIndex: 12, - endIndex: 12 - }, this.getBraille_(input, input)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'StateTemplate', function() { - this.loadHtml( - '<input id="1" type="checkbox" aria-label="Save">'); - - var checkbox = cvox.CursorSelection.fromNode($('1')); - - this.assertBrailleEquals( - {text: 'Save chk ( )', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(checkbox, checkbox)); - - $('1').checked = true; - - this.assertBrailleEquals( - {text: 'Save chk (x)', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(checkbox, checkbox)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'AccessKey', function() { - this.loadHtml( - '<a href="http://www.google.com" id="1" accesskey="g">Google</a>'); - - var link = cvox.CursorSelection.fromNode($('1')); - - this.assertBrailleEquals( - {text: 'Google lnk access key:g', - startIndex: 0, - endIndex: 1 - }, this.getBraille_(link, link)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'ContainerTemplate', function() { - this.loadHtml( - '<h1>' + - '<a id="1" href="#menu">Skip To Menu</a>' + - '</h1>' - ); - - var link = cvox.CursorSelection.fromNode($('1')); - - var navBraille = this.getBraille_( - cvox.CursorSelection.fromBody(), link); - this.assertBrailleEquals( - {text: 'h1 Skip To Menu intlnk', - startIndex: 0, - endIndex: 1 - }, navBraille); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'LinkSpans', function() { - this.loadHtml('<p><a id="1" href="#1">Hello</a> from' + - ' <a id="2" href="//www.google.com/">ChromeVox</a>'); - var link1 = $('1'); - var link2 = $('2'); - var navBraille = this.getBraille_( - cvox.CursorSelection.fromBody(), cvox.CursorSelection.fromNode(link1)); - assertEquals('Hello intlnk from ChromeVox lnk', - navBraille.text.toString()); - assertEquals(link1, navBraille.text.getSpan(0)); - assertEquals(link1, navBraille.text.getSpan(11)); - assertEquals('undefined', typeof navBraille.text.getSpan(12)); - assertEquals('undefined', typeof navBraille.text.getSpan(17)); - assertEquals(link2, navBraille.text.getSpan(18)); - assertEquals(link2, navBraille.text.getSpan(30)); -}); - - -TEST_F('CvoxBrailleUtilUnitTest', 'VisitedLink', function() { - this.loadHtml('<p><a id="1" href="http://visited.link">Hello</a> there.'); - var link = $('1'); - var navBraille = this.getBraille_( - cvox.CursorSelection.fromBody(), cvox.CursorSelection.fromNode(link)); - this.assertBrailleEquals({text: 'Hello lnk there.', - startIndex: 0, - endIndex: 1}, - navBraille); - cvox.ChromeVox.visitedUrls[link.href] = true; - navBraille = this.getBraille_( - cvox.CursorSelection.fromBody(), cvox.CursorSelection.fromNode(link)); - this.assertBrailleEquals({text: 'Hello vlnk there.', - startIndex: 0, - endIndex: 1}, - navBraille); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'NestedElements', function() { - this.loadHtml('<h1 id="test-h1">Larry, ' + - '<a href="#batman" id="batman-link">Sergey</a> and Eric</h1>'); - var h1 = $('test-h1'); - var link = $('batman-link'); - var navBraille = this.getBraille_( - cvox.CursorSelection.fromBody(), cvox.CursorSelection.fromNode(h1)); - assertEquals('h1 Larry, Sergey intlnk and Eric', - navBraille.text.toString()); - this.assertTextNodeChildOf_(h1, navBraille.text.getSpan(0)); - this.assertTextNodeChildOf_(h1, navBraille.text.getSpan(5)); - assertEquals(link, navBraille.text.getSpan(15)); - this.assertTextNodeChildOf_(h1, navBraille.text.getSpan(30)); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'GetTemplatedOverride', function() { - assertEquals('Menu mnu', - cvox.BrailleUtil.getTemplated(null, null, - { 'name': 'Menu', - 'roleMsg': 'role_menu' }). - toString()); - assertEquals('alrt: Watch out!', - cvox.BrailleUtil.getTemplated(null, null, - { 'name': 'Watch out!', - 'roleMsg': 'role_alert' }). - toString()); - // Test all properties. role, if present, overrides roleMsg. - assertEquals('Name Value Role State', - cvox.BrailleUtil.getTemplated(null, null, - { 'name': 'Name', - 'role': 'Role', - 'roleMsg': 'excluded', - 'value': 'Value', - 'state': 'State' - }).toString()); -}); - - -/** - * @export - */ -TEST_F('CvoxBrailleUtilUnitTest', 'CreateValue', function() { - var s; - var valueSpan; - var selectiponSpan; - - // Value without a selection. - s = cvox.BrailleUtil.createValue('value'); - assertEquals('value', s.toString()); - assertUndefined(s.getSpanInstanceOf(cvox.ValueSelectionSpan)); - valueSpan = s.getSpanInstanceOf(cvox.ValueSpan); - assertEquals(0, s.getSpanStart(valueSpan)); - assertEquals(s.getLength(), s.getSpanEnd(valueSpan)); - - // Value with a carret at the start of the text. - s = cvox.BrailleUtil.createValue('value', 0); - selectionSpan = s.getSpanInstanceOf(cvox.ValueSelectionSpan); - assertEquals(0, s.getSpanStart(selectionSpan)); - assertEquals(0, s.getSpanEnd(selectionSpan)); - - // Value with a carret inside the text. - s = cvox.BrailleUtil.createValue('value', 1); - selectionSpan = s.getSpanInstanceOf(cvox.ValueSelectionSpan); - assertEquals(1, s.getSpanStart(selectionSpan)); - assertEquals(1, s.getSpanEnd(selectionSpan)); - - // Value with a carret at the end of the text. - s = cvox.BrailleUtil.createValue('value', 5); - selectionSpan = s.getSpanInstanceOf(cvox.ValueSelectionSpan); - assertEquals(5, s.getSpanStart(selectionSpan)); - assertEquals(5, s.getSpanEnd(selectionSpan)); - - // All of the value selected selected with reversed start and end. - s = cvox.BrailleUtil.createValue('value', 5, 0); - selectionSpan = s.getSpanInstanceOf(cvox.ValueSelectionSpan); - assertEquals(0, s.getSpanStart(selectionSpan)); - assertEquals(5, s.getSpanEnd(selectionSpan)); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/buildinfo.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/buildinfo.js deleted file mode 100644 index 47ee49d4581..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/buildinfo.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -/** - * @fileoverview Build info for ChromeVox. - * - * This file may be replaced in package.py for dev builds. - * For releases, it is updated by hand with meaningful values. - * - */ - -goog.provide('cvox.BuildInfo'); - -/** - * An identified for this build. - * @type {string} - */ -cvox.BuildInfo.build = 'development build'; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js deleted file mode 100644 index 6ba315b3fcf..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -/** - * @fileoverview Definitions for the Chromium extensions API used by ChromeVox. - * - * @externs - */ - - -// TODO: Move these to //third_party/closure_compiler/externs. - -// Begin auto generated externs; do not edit. -// The following was generated from: -// -// python tools/json_schema_compiler/compiler.py -// -g externs -// chrome/common/extensions/api/automation.idl - -/** - * @const - */ -chrome.automation = {}; - -/** - * @enum {string} - */ -chrome.automation.EventType = { - activedescendantchanged: 'activedescendantchanged', - alert: 'alert', - ariaAttributeChanged: 'ariaAttributeChanged', - autocorrectionOccured: 'autocorrectionOccured', - blur: 'blur', - checkedStateChanged: 'checkedStateChanged', - childrenChanged: 'childrenChanged', - focus: 'focus', - hide: 'hide', - hover: 'hover', - invalidStatusChanged: 'invalidStatusChanged', - layoutComplete: 'layoutComplete', - liveRegionChanged: 'liveRegionChanged', - loadComplete: 'loadComplete', - locationChanged: 'locationChanged', - menuEnd: 'menuEnd', - menuListItemSelected: 'menuListItemSelected', - menuListValueChanged: 'menuListValueChanged', - menuPopupEnd: 'menuPopupEnd', - menuPopupStart: 'menuPopupStart', - menuStart: 'menuStart', - rowCollapsed: 'rowCollapsed', - rowCountChanged: 'rowCountChanged', - rowExpanded: 'rowExpanded', - scrollPositionChanged: 'scrollPositionChanged', - scrolledToAnchor: 'scrolledToAnchor', - selectedChildrenChanged: 'selectedChildrenChanged', - selection: 'selection', - selectionAdd: 'selectionAdd', - selectionRemove: 'selectionRemove', - show: 'show', - textChanged: 'textChanged', - textSelectionChanged: 'textSelectionChanged', - treeChanged: 'treeChanged', - valueChanged: 'valueChanged', -}; - -/** - * @enum {string} - */ -chrome.automation.RoleType = { - alertDialog: 'alertDialog', - alert: 'alert', - annotation: 'annotation', - application: 'application', - article: 'article', - banner: 'banner', - blockquote: 'blockquote', - busyIndicator: 'busyIndicator', - button: 'button', - buttonDropDown: 'buttonDropDown', - canvas: 'canvas', - caption: 'caption', - cell: 'cell', - checkBox: 'checkBox', - client: 'client', - colorWell: 'colorWell', - columnHeader: 'columnHeader', - column: 'column', - comboBox: 'comboBox', - complementary: 'complementary', - contentInfo: 'contentInfo', - date: 'date', - dateTime: 'dateTime', - definition: 'definition', - descriptionListDetail: 'descriptionListDetail', - descriptionList: 'descriptionList', - descriptionListTerm: 'descriptionListTerm', - desktop: 'desktop', - details: 'details', - dialog: 'dialog', - directory: 'directory', - disclosureTriangle: 'disclosureTriangle', - div: 'div', - document: 'document', - embeddedObject: 'embeddedObject', - figcaption: 'figcaption', - figure: 'figure', - footer: 'footer', - form: 'form', - grid: 'grid', - group: 'group', - heading: 'heading', - iframe: 'iframe', - iframePresentational: 'iframePresentational', - ignored: 'ignored', - imageMapLink: 'imageMapLink', - imageMap: 'imageMap', - image: 'image', - inlineTextBox: 'inlineTextBox', - labelText: 'labelText', - legend: 'legend', - lineBreak: 'lineBreak', - link: 'link', - listBoxOption: 'listBoxOption', - listBox: 'listBox', - listItem: 'listItem', - listMarker: 'listMarker', - list: 'list', - locationBar: 'locationBar', - log: 'log', - main: 'main', - marquee: 'marquee', - math: 'math', - menuBar: 'menuBar', - menuButton: 'menuButton', - menuItem: 'menuItem', - menuItemCheckBox: 'menuItemCheckBox', - menuItemRadio: 'menuItemRadio', - menuListOption: 'menuListOption', - menuListPopup: 'menuListPopup', - menu: 'menu', - meter: 'meter', - navigation: 'navigation', - note: 'note', - outline: 'outline', - pane: 'pane', - paragraph: 'paragraph', - popUpButton: 'popUpButton', - pre: 'pre', - presentational: 'presentational', - progressIndicator: 'progressIndicator', - radioButton: 'radioButton', - radioGroup: 'radioGroup', - region: 'region', - rootWebArea: 'rootWebArea', - rowHeader: 'rowHeader', - row: 'row', - ruby: 'ruby', - ruler: 'ruler', - svgRoot: 'svgRoot', - scrollArea: 'scrollArea', - scrollBar: 'scrollBar', - seamlessWebArea: 'seamlessWebArea', - search: 'search', - searchBox: 'searchBox', - slider: 'slider', - sliderThumb: 'sliderThumb', - spinButtonPart: 'spinButtonPart', - spinButton: 'spinButton', - splitter: 'splitter', - staticText: 'staticText', - status: 'status', - switch: 'switch', - tabGroup: 'tabGroup', - tabList: 'tabList', - tabPanel: 'tabPanel', - tab: 'tab', - tableHeaderContainer: 'tableHeaderContainer', - table: 'table', - textField: 'textField', - time: 'time', - timer: 'timer', - titleBar: 'titleBar', - toggleButton: 'toggleButton', - toolbar: 'toolbar', - treeGrid: 'treeGrid', - treeItem: 'treeItem', - tree: 'tree', - unknown: 'unknown', - tooltip: 'tooltip', - webArea: 'webArea', - webView: 'webView', - window: 'window', -}; - -/** - * @enum {string} - */ -chrome.automation.StateType = { - busy: 'busy', - checked: 'checked', - collapsed: 'collapsed', - default: 'default', - disabled: 'disabled', - editable: 'editable', - enabled: 'enabled', - expanded: 'expanded', - focusable: 'focusable', - focused: 'focused', - haspopup: 'haspopup', - horizontal: 'horizontal', - hovered: 'hovered', - indeterminate: 'indeterminate', - invisible: 'invisible', - linked: 'linked', - multiselectable: 'multiselectable', - offscreen: 'offscreen', - pressed: 'pressed', - protected: 'protected', - readOnly: 'readOnly', - required: 'required', - selectable: 'selectable', - selected: 'selected', - vertical: 'vertical', - visited: 'visited', -}; - -/** - * @enum {string} - */ -chrome.automation.TreeChangeType = { - nodeCreated: 'nodeCreated', - subtreeCreated: 'subtreeCreated', - nodeChanged: 'nodeChanged', - nodeRemoved: 'nodeRemoved', -}; - -/** - * @typedef {{ - * left: number, - * top: number, - * width: number, - * height: number - * }} - */ -chrome.automation.Rect; - -/** - * @typedef {{ - * role: (!chrome.automation.RoleType|undefined), - * state: (Object|undefined), - * attributes: (Object|undefined) - * }} - */ -chrome.automation.FindParams; - -/** - * @constructor - */ -chrome.automation.AutomationEvent = function() {}; - -/** - * @typedef {{ - * target: chrome.automation.AutomationNode, - * type: !chrome.automation.TreeChangeType - * }} - */ -chrome.automation.TreeChange; - -/** - * @constructor - */ -chrome.automation.AutomationNode = function() {}; - - -/** - * Get the automation tree for the tab with the given tabId, or the current tab - * if no tabID is given, enabling automation if necessary. Returns a tree with a - * placeholder root node; listen for the "loadComplete" event to get a - * notification that the tree has fully loaded (the previous root node reference - * will stop working at or before this point). - * @param {number} tabId - * @param {function(chrome.automation.AutomationNode):void} callback - * Called when the <code>AutomationNode</code> for the page is available. - */ -chrome.automation.getTree = function(tabId, callback) {}; - -/** - * Get the automation tree for the whole desktop which consists of all on screen - * views. Note this API is currently only supported on Chrome OS. - * @param {function(chrome.automation.AutomationNode):void} callback - * Called when the <code>AutomationNode</code> for the page is available. - */ -chrome.automation.getDesktop = function(callback) {}; - -/** - * Add a tree change observer. Tree change observers are static/global, - * they listen to tree changes across all trees. - * @param {function(chrome.automation.TreeChange):void} observer - * A listener for tree changes on the <code>AutomationNode</code> tree. - */ -chrome.automation.addTreeChangeObserver = function(observer) {}; - -/** - * Remove a tree change observer. - * @param {function(chrome.automation.TreeChange):void} observer - * A listener for tree changes on the <code>AutomationNode</code> tree. - */ -chrome.automation.removeTreeChangeObserver = function(observer) {}; - -// -// End auto generated externs; do not edit. -// - - - -/** - * @type {chrome.automation.RoleType} - */ -chrome.automation.AutomationNode.prototype.role; - - -/** - * @type {!Object<chrome.automation.StateType, boolean>} - */ -chrome.automation.AutomationNode.prototype.state; - - -/** - * @type {number} - */ -chrome.automation.AutomationNode.prototype.indexInParent; - - -/** - * @type {string} - */ -chrome.automation.AutomationNode.prototype.name; - -/** - * @type {string} - */ -chrome.automation.AutomationNode.prototype.description; - - -/** - * @type {string} - */ -chrome.automation.AutomationNode.prototype.url; - - -/** - * @type {string} - */ -chrome.automation.AutomationNode.prototype.docUrl; - - -/** - * @type {string} - */ -chrome.automation.AutomationNode.prototype.value; - - -/** - * @type {number} - */ -chrome.automation.AutomationNode.prototype.textSelStart; - - -/** - * @type {number} - */ -chrome.automation.AutomationNode.prototype.textSelEnd; - - -/** - * @type {Array<number>} - */ -chrome.automation.AutomationNode.prototype.wordStarts; - - -/** - * @type {Array<number>} - */ -chrome.automation.AutomationNode.prototype.wordEnds; - - -/** - * @type {!chrome.automation.AutomationNode} - */ -chrome.automation.AutomationNode.prototype.root; - - -/** - * @type {chrome.automation.AutomationNode} - */ -chrome.automation.AutomationNode.prototype.firstChild; - - -/** - * @type {chrome.automation.AutomationNode} - */ -chrome.automation.AutomationNode.prototype.lastChild; - - -/** - * @type {chrome.automation.AutomationNode} - */ -chrome.automation.AutomationNode.prototype.nextSibling; - - -/** - * @type {chrome.automation.AutomationNode} - */ -chrome.automation.AutomationNode.prototype.previousSibling; - - -/** - * @type {chrome.automation.AutomationNode} - */ -chrome.automation.AutomationNode.prototype.parent; - - -/** - * @type {!Array<chrome.automation.AutomationNode>} - */ -chrome.automation.AutomationNode.prototype.children; - - -/** - * @type {{top: number, left: number, height: number, width: number}} - */ -chrome.automation.AutomationNode.prototype.location; - - -/** - * @param {chrome.automation.EventType} eventType - * @param {function(!chrome.automation.AutomationEvent) : void} callback - * @param {boolean} capture - */ -chrome.automation.AutomationNode.prototype.addEventListener = - function(eventType, callback, capture) {}; - - -/** - * @param {chrome.automation.EventType} eventType - * @param {function(chrome.automation.AutomationNode) : void} callback - * @param {boolean} capture - */ -chrome.automation.AutomationNode.prototype.removeEventListener = - function(eventType, callback, capture) {}; - - -/** - * @type {chrome.automation.AutomationNode} - */ -chrome.automation.TreeChange.prototype.target; - - -/** - * @type {chrome.automation.TreeChangeType} - */ -chrome.automation.TreeChange.prototype.type; - - -/** - * @param {function(chrome.automation.TreeChange) : void} - * callback - */ -chrome.automation.AutomationNode.prototype.addTreeChangeObserver = - function(callback) {}; - - -/** - * @param {function(chrome.automation.TreeChange) : void} - * callback - */ -chrome.automation.AutomationNode.prototype.removeTreeChangeObserver = - function(callback) {}; - - -chrome.automation.AutomationNode.prototype.doDefault = function() {}; - - -chrome.automation.AutomationNode.prototype.focus = function() {}; - - -chrome.automation.AutomationNode.prototype.showContextMenu = function() {}; - - -/** - * @param {number} start - * @param {number} end - */ -chrome.automation.AutomationNode.prototype.setSelection = - function(start, end) {}; - - -/** @type {string} */ -chrome.automation.AutomationNode.prototype.containerLiveStatus; - -/** @type {string} */ -chrome.automation.AutomationNode.prototype.containerLiveRelevant; - -/** @type {boolean} */ -chrome.automation.AutomationNode.prototype.containerLiveAtomic; - -/** @type {boolean} */ -chrome.automation.AutomationNode.prototype.containerLiveBusy; - - -/** - * @param {Object} findParams - */ -chrome.automation.AutomationNode.prototype.find = function(findParams) {}; - -/** - * @type {string} - */ -chrome.automation.AutomationNode.prototype.inputType; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/chromevox.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/chromevox.js deleted file mode 100644 index 88bfbc54a59..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/chromevox.js +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Defines a global object. The initialization of this - * object happens in init.js. - * - */ - -goog.provide('cvox.ChromeVox'); - -// Forward declarations. -// TODO (stoarca): Put these in a separate file and pass that -// into the build system instead of having it here. This will allow -// us to group all of the forward declarations for each file without -// having them overwrite the mapping in deps.js -goog.addDependency( - '../host/interface/abstract_host.js', - ['cvox.AbstractHost'], - []); - -goog.addDependency( - '../host/interface/tts_interface.js', - ['cvox.TtsInterface'], - []); - -goog.addDependency( - '../host/interface/braille_interface.js', - ['cvox.BrailleInterface'], - []); - -goog.addDependency( - '../host/interface/mathjax_interface.js', - ['cvox.MathJaxInterface'], - []); - -goog.addDependency( - '../chromevox/messages/msgs.js', - ['Msgs'], - []); - -goog.addDependency( - '../host/interface/abstract_earcons.js', - ['cvox.AbstractEarcons'], - []); - -goog.addDependency( - '../chromevox/common/key_sequence.js', - ['cvox.KeySequence'], - []); - -goog.addDependency( - '../chromevox/injected/navigation_manager.js', - ['cvox.NavigationManager'], - []); - -goog.addDependency( - '../chromevox/injected/serializer.js', - ['cvox.Serializer'], - []); - -// Constants -/** - * Constant for verbosity setting (cvox.ChromeVox.verbosity). - * @const - * @type {number} - */ -cvox.VERBOSITY_VERBOSE = 0; -/** - * Constant for verbosity setting (cvox.ChromeVox.verbosity). - * @const - * @type {number} - */ -cvox.VERBOSITY_BRIEF = 1; - - -/** - * @constructor - */ -cvox.ChromeVox = function() {}; - -/** - * @type {cvox.AbstractHost} - */ -cvox.ChromeVox.host = null; -/** - * @type {cvox.TtsInterface} - */ -cvox.ChromeVox.tts; -/** - * @type {cvox.BrailleInterface} - */ -cvox.ChromeVox.braille; -/** - * @type {cvox.MathJaxInterface} - */ -cvox.ChromeVox.mathJax; -/** - * @type {boolean} - */ -cvox.ChromeVox.isActive = true; -/** - * @type {?string} - */ -cvox.ChromeVox.version = null; -/** - * @type {cvox.AbstractEarcons} - */ -cvox.ChromeVox.earcons = null; -/** - * @type {cvox.NavigationManager} - */ -cvox.ChromeVox.navigationManager = null; -/** - * @type {cvox.Serializer} - */ -cvox.ChromeVox.serializer = null; -/** - * This indicates whether or not the sticky mode pref is toggled on. - * Use cvox.ChromeVox.isStickyModeOn() to test if sticky mode is enabled - * either through the pref or due to being temporarily toggled on. - * @type {boolean} - */ -cvox.ChromeVox.isStickyPrefOn = false; -/** - * If set to true or false, this value overrides cvox.ChromeVox.isStickyPrefOn - * temporarily - in order to temporarily enable sticky mode while doing - * 'read from here' or to temporarily disable it while using a widget. - * @type {?boolean} - */ -cvox.ChromeVox.stickyOverride = null; -/** - * @type {boolean} - */ -cvox.ChromeVox.keyPrefixOn = false; -/** - * Verbosity setting. - * See: cvox.VERBOSITY_VERBOSE and cvox.VERBOSITY_BRIEF - * @type {number} - */ -cvox.ChromeVox.verbosity = cvox.VERBOSITY_VERBOSE; -/** - * @type {number} - */ -cvox.ChromeVox.typingEcho = 0; -/** - * Echoing on key press events. - * @type {Object<boolean>} - */ -cvox.ChromeVox.keyEcho = {}; -/** - * @type {Object<{x:number, y:number}>} - */ -cvox.ChromeVox.position = {}; -/** - * @type {boolean} - */ -cvox.ChromeVox.isChromeOS = navigator.userAgent.indexOf('CrOS') != -1; -/** - * @type {boolean} - */ -cvox.ChromeVox.isMac = navigator.platform.indexOf('Mac') != -1; -/** - * @type {string} - */ -cvox.ChromeVox.modKeyStr; -if (cvox.ChromeVox.isChromeOS) { - cvox.ChromeVox.modKeyStr = 'Shift+Search'; -} else if (cvox.ChromeVox.isMac) { - cvox.ChromeVox.modKeyStr = 'Ctrl+Cmd'; -} else { - cvox.ChromeVox.modKeyStr = 'Shift+Alt'; -} -/** - * If any of these keys is pressed with the modifier key, we go in sequence mode - * where the subsequent independent key downs (while modifier keys are down) - * are a part of the same shortcut. This array is populated in - * cvox.ChromeVoxKbHandler.loadKeyToFunctionsTable(). - * @type {!Array<cvox.KeySequence>} - */ -cvox.ChromeVox.sequenceSwitchKeyCodes = []; -/** @type {Object<boolean>} */ -cvox.ChromeVox.visitedUrls = {}; -/** - * This function can be called before doing an operation that may trigger - * focus events and other events that would normally be announced. This - * tells the event manager that these events should be ignored, they're - * a result of another command that's already announced them. This is - * a temporary state that's automatically reverted after a few milliseconds, - * there's no way to explicitly "un-mark". - * @type {Function} - */ -cvox.ChromeVox.markInUserCommand = function() {}; -/** - * Synchronizes ChromeVox's internal cursor to the targetNode. - * @param {Node} targetNode The node that ChromeVox should be synced to. - * @param {boolean=} speakNode If true, speaks out the node. - * @param {number=} opt_queueMode The queue mode to use for speaking. - */ -cvox.ChromeVox.syncToNode = function( - targetNode, speakNode, opt_queueMode) {}; - -/** - * Speaks the given node. - * @param {Node} targetNode The node that ChromeVox should be synced to. - * @param {number=} queueMode The queue mode to use for speaking. - * @param {Object=} properties Speech properties to use for this utterance. - */ -cvox.ChromeVox.speakNode = function(targetNode, queueMode, properties) {}; - -/** - * Provide a way for modules that can't depend on cvox.ChromeVoxUserCommands - * to execute commands. - * - * @param {string} commandName The command name as a string. - */ -cvox.ChromeVox.executeUserCommand = function(commandName) {}; - -/** - * True if the document body has aria-hidden='true' when we first load. - * ChromeVox will disallow any navigation and not eat any keystrokes. - * @type {boolean} - */ -cvox.ChromeVox.entireDocumentIsHidden = false; - -/** - * Stores state variables in a provided object. - * - * @param {Object} store The object. - */ -cvox.ChromeVox.storeOn = function(store) { - store['isStickyPrefOn'] = cvox.ChromeVox.isStickyPrefOn; - cvox.ChromeVox.navigationManager.storeOn(store); -}; - -/** - * Updates the object with state variables from an earlier storeOn call. - * - * @param {Object} store The object. - */ -cvox.ChromeVox.readFrom = function(store) { - cvox.ChromeVox.isStickyPrefOn = store['isStickyPrefOn']; - cvox.ChromeVox.navigationManager.readFrom(store); -}; - -/** - * Returns whether sticky mode is on, taking both the global sticky mode - * pref and the temporary sticky mode override into account. - * - * @return {boolean} Whether sticky mode is on. - */ -cvox.ChromeVox.isStickyModeOn = function() { - if (cvox.ChromeVox.stickyOverride !== null) { - return cvox.ChromeVox.stickyOverride; - } else { - return cvox.ChromeVox.isStickyPrefOn; - } -}; - -/** - * Shortcut for document.getElementById. - * @param {string} id of the element. - * @return {HTMLElement} with the id. - */ -function $(id) { - return document.getElementById(id); -} - -/** - * @param {Array} tabs - */ -cvox.ChromeVox.injectChromeVoxIntoTabs = function(tabs) {}; - -/** - * Returns whether the document has focus, taking into account whether - * it's hidden and also that if an iframe or webview element has focus, - * the focus is really inside that frame and not in this document. - * @return {boolean} True if the document has focus. - */ -cvox.ChromeVox.documentHasFocus = function() { - if (!document.hasFocus() || document.hidden) { - return false; - } - if (document.activeElement.tagName == 'IFRAME' || - document.activeElement.tagName == 'WEBVIEW') { - return false; - } - return true; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/chromevox_json.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/chromevox_json.js deleted file mode 100644 index 96c78ca9f31..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/chromevox_json.js +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.ChromeVoxJSON'); - - -/** - * @fileoverview A simple wrapper around the JSON APIs. - * If it is possible to use the browser's built in native JSON, then - * cvox.ChromeVoxJSON is the same as JSON. - * If the page has its own version of JSON, cvox.ChromeVoxJSON will use its - * own implementation (rather than the version of JSON on the page - * which may be outdated/broken). - */ - -if (!cvox.ChromeVoxJSON) { - /** Placeholder object. */ - cvox.ChromeVoxJSON = {}; -} - -if (window.JSON && window.JSON.toString() == '[object JSON]') { - cvox.ChromeVoxJSON = window.JSON; -} else { - /* - * JSON implementation renamed to cvox.ChromeVoxJSON. - * This only gets called if the page has its own version of JSON. - * - * Based on: - * http://www.JSON.org/json2.js - * 2010-03-20 - * - * Public Domain. - * - * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - * - * See http://www.JSON.org/js.html - */ - (function() { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - if (typeof Date.prototype.toJSON !== 'function') { - - Date.prototype.toJSON = function(key) { - - return isFinite(this.valueOf()) ? - this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' : 'null'; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function(key) { - return /** @type {string} */ (this.valueOf()); - }; - } - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - - // If the string contains no control characters, no quote characters, and - // no backslash characters, then we can safely slap some quotes around it. - // Otherwise we must also replace the offending characters with safe - // escape sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? - '"' + string.replace(escapable, function(a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : - '"' + string + '"'; - } - - - function str(key, holder) { - - // Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - - // If the value has a toJSON method, call it to obtain a replacement - // value. - - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - - // If we were called with a replacer function, then call the replacer to - // obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - - // What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - // JSON numbers must be finite. Encode non-finite numbers as null. - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - // If the value is a boolean or null, convert it to a string. Note: - // typeof null does not produce 'null'. The case is included here in - // the remote chance that this gets fixed someday. - return String(value); - - // If the type is 'object', we might be dealing with an object or an - // array or null. - - case 'object': - - // Due to a specification blunder in ECMAScript, typeof null is - // 'object', so watch out for that case. - - if (!value) { - return 'null'; - } - - // Make an array to hold the partial results of stringifying this - // object value. - - gap += indent; - partial = []; - - // Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - - // The value is an array. Stringify every element. Use null as a - // placeholder for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - - // Join all of the elements together, separated with commas, and - // wrap them in brackets. - - v = partial.length === 0 ? '[]' : - gap ? '[\n' + gap + - partial.join(',\n' + gap) + '\n' + - mind + ']' : - '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - - // If the replacer is an array, use it to select the members to be - // stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - k = rep[i]; - if (typeof k === 'string') { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - - // Otherwise, iterate through all of the keys in the object. - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - - // Join all of the member texts together, separated with commas, - // and wrap them in braces. - - v = partial.length === 0 ? '{}' : - gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + - mind + '}' : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - - // If the JSON object does not yet have a stringify method, give it one. - - if (typeof cvox.ChromeVoxJSON.stringify !== 'function') { - /** - * @param {*} value Input object. - * @param {(Array<string>|(function(string, *) : *)|null)=} replacer - * Replacer array or function. - * @param {(number|string|null)=} space Whitespace character. - * @return {string} json string which represents jsonObj. - */ - cvox.ChromeVoxJSON.stringify = function(value, replacer, space) { - - // The stringify method takes a value and an optional replacer, and an - // optional space parameter, and returns a JSON text. The replacer can - // be a function that can replace values, or an array of strings that - // will select the keys. A default replacer method can be provided. Use - // of the space parameter can produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - - // If the space parameter is a number, make an indent string containing - // that many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - - // If the space parameter is a string, it will be used as the indent - // string. - - } else if (typeof space === 'string') { - indent = space; - } - - // If there is a replacer, it must be a function or an array. - // Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - - // Make a fake root object containing our value under the key of ''. - // Return the result of stringifying the value. - - return str('', {'': value}); - }; - } - - - // If the JSON object does not yet have a parse method, give it one. - - if (typeof cvox.ChromeVoxJSON.parse !== 'function') { - /** - * @param {string} text The string to parse. - * @param {(function(string, *) : *|null)=} reviver Reviver function. - * @return {*} The JSON object. - */ - cvox.ChromeVoxJSON.parse = function(text, reviver) { - - // The parse method takes a text and an optional reviver function, and - // returns a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - - // The walk method is used to recursively walk the resulting structure - // so that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - - // Parsing happens in four stages. In the first stage, we replace - // certain Unicode characters with escape sequences. JavaScript handles - // many characters incorrectly, either silently deleting them, or - // treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function(a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - - // In the second stage, we run the text against regular expressions that - // look for non-JSON patterns. We are especially concerned with '()' and - // 'new' because they can cause invocation, and '=' because it can cause - // mutation. But just to be safe, we want to reject all unexpected - // forms. - // We split the second stage into 4 regexp operations in order to work - // around crippling inefficiencies in IE's and Safari's regexp engines. - // First we replace the JSON backslash pairs with '@' (a non-JSON - // character). Second, we replace all simple value tokens with ']' - // characters. Third, we delete all open brackets that follow a colon or - // comma or that begin the text. Finally, we look to see that the - // remaining characters are only whitespace or ']' or ',' or ':' or '{' - // or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/. - test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). - replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). - replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - - // In the third stage we use the eval function to compile the text - // into a JavaScript structure. The '{' operator is subject to a - // syntactic ambiguity in JavaScript: it can begin a block or an - // object literal. We wrap the text in parens to eliminate the - // ambiguity. - - j = eval('(' + text + ')'); - - // In the optional fourth stage, we recursively walk the new - // structure, passing each name/value pair to a reviver function for - // possible transformation. - return typeof reviver === 'function' ? walk({'': j}, '') : j; - } - - // If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } - }()); -} diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/command_store.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/command_store.js deleted file mode 100644 index 05e02731933..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/command_store.js +++ /dev/null @@ -1,784 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -/** - * @fileoverview This class acts as the persistent store for all static data - * about commands. - * - * This store can safely be used within either a content or background script - * context. - * - * If you are looking to add a user command, follow the below steps for best - * integration with existing components: - * 1. Add a command below in cvox.CommandStore.CMD_WHITELIST. Pick a - * programmatic name and fill in each of the relevant JSON keys. - * Be sure to add a msg id and define it in chromevox/messages/messages.js which - * describes the command. Please also add a category msg id so that the command - * will show up in the options page. - * 2. Add the command's logic to cvox.UserCommands inside of our switch-based - * dispatch method (doCommand_). - * 3. Add a key binding in chromevox/background/keymaps/classic_keymap.json and - * chromevox/background/keymaps/flat_keymap.json. - * - * Class description: - * This class is entirely static and holds a JSON structure that stores - * commands and their associated metadata. - * - * From this metadata, we compute relevant subsets of data such as all present - * categories. - */ - - -goog.provide('cvox.CommandStore'); - -goog.require('cvox.PlatformFilter'); - - -/** - * Returns all of the categories in the store as an array. - * @return {Array<string>} The collection of categories. - */ -cvox.CommandStore.categories = function() { - var categorySet = {}; - for (var cmd in cvox.CommandStore.CMD_WHITELIST) { - var struct = cvox.CommandStore.CMD_WHITELIST[cmd]; - if (struct.category) { - categorySet[struct.category] = true; - } - } - var ret = []; - for (var category in categorySet) { - ret.push(category); - } - return ret; -}; - - -/** - * Gets a message given a command. - * @param {string} command The command to query. - * @return {string|undefined} The message id, if any. - */ -cvox.CommandStore.messageForCommand = function(command) { - return (cvox.CommandStore.CMD_WHITELIST[command] || {}).msgId; -}; - - -/** - * Gets a category given a command. - * @param {string} command The command to query. - * @return {string|undefined} The command, if any. - */ -cvox.CommandStore.categoryForCommand = function(command) { - return (cvox.CommandStore.CMD_WHITELIST[command] || {}).category; -}; - - -/** - * Gets all commands for a category. - * @param {string} category The category to query. - * @return {Array<string>} The commands, if any. - */ -cvox.CommandStore.commandsForCategory = function(category) { - var ret = []; - for (var cmd in cvox.CommandStore.CMD_WHITELIST) { - var struct = cvox.CommandStore.CMD_WHITELIST[cmd]; - if (category == struct.category) { - ret.push(cmd); - } - } - return ret; -}; - - -/** - * List of commands and their properties - * @type {Object<{forward: (undefined|boolean), - * backward: (undefined|boolean), - * announce: boolean, - * category: (undefined|string), - * findNext: (undefined|string), - * doDefault: (undefined|boolean), - * msgId: (undefined|string), - * nodeList: (undefined|string), - * platformFilter: (undefined|cvox.PlatformFilter), - * skipInput: (undefined|boolean), - * allowEvents: (undefined|boolean), - * disallowContinuation: (undefined|boolean)}>} - * forward: Whether this command points forward. - * backward: Whether this command points backward. If neither forward or - * backward are specified, it stays facing in the current direction. - * announce: Whether to call finishNavCommand and announce the current - * position after the command is done. - * findNext: The id from the map above if this command is used for - * finding next/previous of something. - * category: The message resource describing the command's category. - * doDefault: Whether to do the default action. This means that keys will be - * passed through to the usual DOM capture/bubble phases. - * msgId: The message resource describing the command. - * nodeList: The id from the map above if this command is used for - * showing a list of nodes. - * platformFilter: Specifies to which platforms this command applies. If left - * undefined, the command applies to all platforms. - * skipInput: Explicitly skips this command when text input has focus. - * Defaults to false. - * disallowOOBE: Explicitly disallows this command when on chrome://oobe/*. - * Defaults to false. - * allowEvents: Allows EventWatcher to continue processing events which can - * trump TTS. - * disallowContinuation: Disallows continuous read to proceed. Defaults to - * false. - */ -cvox.CommandStore.CMD_WHITELIST = { - 'toggleStickyMode': {announce: false, - msgId: 'toggle_sticky_mode', - 'disallowOOBE': true, - category: 'modifier_keys'}, - 'toggleKeyPrefix': {announce: false, - skipInput: true, - msgId: 'prefix_key', - 'disallowOOBE': true, - category: 'modifier_keys'}, - 'passThroughMode': {announce: false, - msgId: 'pass_through_key_description', - category: 'modifier_keys'}, - - 'stopSpeech': {announce: false, - disallowContinuation: true, - doDefault: true, - msgId: 'stop_speech_key', - category: 'controlling_speech'}, - 'toggleChromeVox': {announce: false, - platformFilter: cvox.PlatformFilter.WML, - msgId: 'toggle_chromevox_active', - category: 'controlling_speech'}, - 'decreaseTtsRate': {announce: false, - msgId: 'decrease_tts_rate', - category: 'controlling_speech'}, - 'increaseTtsRate': {announce: false, - msgId: 'increase_tts_rate', - category: 'controlling_speech'}, - 'decreaseTtsPitch': {announce: false, - msgId: 'decrease_tts_pitch', - category: 'controlling_speech'}, - 'increaseTtsPitch': {announce: false, - msgId: 'increase_tts_pitch', - category: 'controlling_speech'}, - 'decreaseTtsVolume': {announce: false, - msgId: 'decrease_tts_volume', - category: 'controlling_speech'}, - 'increaseTtsVolume': {announce: false, - msgId: 'increase_tts_volume', - category: 'controlling_speech'}, - 'cyclePunctuationEcho': {announce: false, - msgId: 'cycle_punctuation_echo', - category: 'controlling_speech'}, - 'cycleTypingEcho': {announce: false, - msgId: 'cycle_typing_echo', - category: 'controlling_speech'}, - - - 'toggleEarcons': {announce: true, - msgId: 'toggle_earcons', - category: 'controlling_speech'}, - - 'handleTab': { - allowEvents: true, - msgId: 'handle_tab_next', - disallowContinuation: true, - category: 'navigation'}, - 'handleTabPrev': { - allowEvents: true, - msgId: 'handle_tab_prev', - disallowContinuation: true, - category: 'navigation'}, - 'forward': {forward: true, - announce: true, - msgId: 'forward', - category: 'navigation'}, - 'backward': {backward: true, - announce: true, - msgId: 'backward', - category: 'navigation'}, - 'right': {forward: true, - announce: true, - msgId: 'right', - category: 'navigation'}, - 'left': {backward: true, - announce: true, - msgId: 'left', - category: 'navigation'}, - 'previousGranularity': {announce: true, - msgId: 'previous_granularity', - category: 'navigation'}, - 'nextGranularity': {announce: true, - msgId: 'next_granularity', - category: 'navigation'}, - - 'previousCharacter': {backward: true, - announce: true, - msgId: 'previous_character', - skipInput: true, - category: 'navigation'}, - 'nextCharacter': {forward: true, - announce: true, - msgId: 'next_character', - skipInput: true, - category: 'navigation'}, - 'previousWord': {backward: true, - announce: true, - msgId: 'previous_word', - skipInput: true, - category: 'navigation'}, - 'nextWord': {forward: true, - announce: true, - msgId: 'next_word', - skipInput: true, - category: 'navigation'}, - 'previousLine': {backward: true, - announce: true, - msgId: 'previous_line', - category: 'navigation'}, - 'nextLine': {forward: true, - announce: true, - msgId: 'next_line', - category: 'navigation'}, - 'previousSentence': {backward: true, - announce: true, - msgId: 'previous_sentence', - skipInput: true, - category: 'navigation'}, - 'nextSentence': {forward: true, - announce: true, - msgId: 'next_sentence', - skipInput: true, - category: 'navigation'}, - 'previousObject': {backward: true, - announce: true, - msgId: 'previous_object', - skipInput: true, - category: 'navigation'}, - 'nextObject': {forward: true, - announce: true, - msgId: 'next_object', - skipInput: true, - category: 'navigation'}, - 'previousGroup': {backward: true, - announce: true, - msgId: 'previous_group', - skipInput: true, - category: 'navigation'}, - 'nextGroup': {forward: true, - announce: true, - msgId: 'next_group', - skipInput: true, - category: 'navigation'}, - - 'jumpToTop': {forward: true, - announce: true, - msgId: 'jump_to_top', - category: 'navigation' -}, - 'jumpToBottom': {backward: true, - announce: true, - msgId: 'jump_to_bottom', - category: 'navigation'}, - // Intentionally uncategorized. - 'moveToStartOfLine': {forward: true, announce: true}, - 'moveToEndOfLine': {backward: true, announce: true}, - - 'readFromHere': {forward: true, - announce: false, - msgId: 'read_from_here', - category: 'navigation'}, - - 'performDefaultAction': {disallowContinuation: true, - msgId: 'perform_default_action', - doDefault: true, - skipInput: true, - category: 'navigation'}, - 'forceClickOnCurrentItem': {announce: true, - disallowContinuation: true, - allowEvents: true, - msgId: 'force_click_on_current_item', - category: 'navigation'}, - 'forceDoubleClickOnCurrentItem': {announce: true, - allowEvents: true, - disallowContinuation: true}, - - 'readLinkURL': {announce: false, - msgId: 'read_link_url', - category: 'information'}, - 'readCurrentTitle': {announce: false, - msgId: 'read_current_title', - category: 'information'}, - 'readCurrentURL': {announce: false, - msgId: 'read_current_url', - category: 'information'}, - - 'fullyDescribe': {announce: false, - msgId: 'fully_describe', - category: 'information'}, - 'speakTimeAndDate': {announce: false, - msgId: 'speak_time_and_date', - category: 'information'}, - 'toggleSelection': {announce: true, - msgId: 'toggle_selection', - category: 'information'}, - - 'toggleSearchWidget': {announce: false, - disallowContinuation: true, - msgId: 'toggle_search_widget', - category: 'information'}, - - 'toggleKeyboardHelp': {announce: false, - disallowContinuation: true, - msgId: 'show_power_key', - category: 'help_commands'}, - 'help': {announce: false, - msgId: 'help', - 'disallowOOBE': true, - disallowContinuation: true, - category: 'help_commands'}, - 'contextMenu': {announce: false, - disallowContinuation: true}, - - 'showOptionsPage': {announce: false, - disallowContinuation: true, - msgId: 'show_options_page', - 'disallowOOBE': true, - category: 'help_commands'}, - 'showKbExplorerPage': {announce: false, - disallowContinuation: true, - msgId: 'show_kb_explorer_page', - 'disallowOOBE': true, - category: 'help_commands'}, - - - 'showFormsList': {announce: false, - disallowContinuation: true, - nodeList: 'formField', - msgId: 'show_forms_list', - category: 'overview'}, - 'showHeadingsList': {announce: false, nodeList: 'heading', - disallowContinuation: true, - msgId: 'show_headings_list', - category: 'overview'}, - 'showLandmarksList': {announce: false, nodeList: 'landmark', - disallowContinuation: true, - msgId: 'show_landmarks_list', - category: 'overview'}, - 'showLinksList': {announce: false, nodeList: 'link', - disallowContinuation: true, - msgId: 'show_links_list', - category: 'overview'}, - 'showTablesList': {announce: false, nodeList: 'table', - disallowContinuation: true, - msgId: 'show_tables_list', - category: 'overview'}, - - 'nextArticle': {forward: true, - findNext: 'article'}, - - 'nextButton': {forward: true, - findNext: 'button', - msgId: 'next_button', - category: 'jump_commands'}, - 'nextCheckbox': {forward: true, - findNext: 'checkbox', - msgId: 'next_checkbox', - category: 'jump_commands'}, - 'nextComboBox': {forward: true, - findNext: 'combobox', - msgId: 'next_combo_box', - category: 'jump_commands'}, - 'nextControl': {forward: true, findNext: 'control'}, - 'nextEditText': {forward: true, - findNext: 'editText', - msgId: 'next_edit_text', - category: 'jump_commands'}, - 'nextFormField': {forward: true, - findNext: 'formField', - msgId: 'next_form_field', - category: 'jump_commands'}, - 'nextGraphic': {forward: true, - findNext: 'graphic', - msgId: 'next_graphic', - category: 'jump_commands'}, - 'nextHeading': {forward: true, - findNext: 'heading', - msgId: 'next_heading', - category: 'jump_commands'}, - 'nextHeading1': {forward: true, - findNext: 'heading1', - msgId: 'next_heading1', - category: 'jump_commands'}, - 'nextHeading2': {forward: true, - findNext: 'heading2', - msgId: 'next_heading2', - category: 'jump_commands'}, - 'nextHeading3': {forward: true, - findNext: 'heading3', - msgId: 'next_heading3', - category: 'jump_commands'}, - 'nextHeading4': {forward: true, - findNext: 'heading4', - msgId: 'next_heading4', - category: 'jump_commands'}, - 'nextHeading5': {forward: true, - findNext: 'heading5', - msgId: 'next_heading5', - category: 'jump_commands'}, - 'nextHeading6': {forward: true, - findNext: 'heading6', - msgId: 'next_heading6', - category: 'jump_commands'}, - - 'nextLandmark': {forward: true, - findNext: 'landmark', - msgId: 'next_landmark', - category: 'jump_commands'}, - 'nextLink': {forward: true, - findNext: 'link', - msgId: 'next_link', - category: 'jump_commands'}, - 'nextList': {forward: true, - findNext: 'list', - msgId: 'next_list', - category: 'jump_commands'}, - 'nextListItem': {forward: true, - findNext: 'listItem', - msgId: 'next_list_item', - category: 'jump_commands'}, - 'nextMath': {forward: true, - findNext: 'math', - msgId: 'next_math', - category: 'jump_commands'}, - 'nextMedia': {forward: true, - findNext: 'media', - msgId: 'next_media', - category: 'jump_commands'}, - 'nextRadio': {forward: true, - findNext: 'radio', - msgId: 'next_radio', - category: 'jump_commands'}, - 'nextSection': {forward: true, findNext: 'section'}, - 'nextSlider': {forward: true, findNext: 'slider'}, - 'nextTable': {forward: true, - findNext: 'table', - msgId: 'next_table', - category: 'jump_commands'}, - 'nextVisitedLink': {forward: true, - findNext: 'visitedLink', - msgId: 'next_visited_link', - category: 'jump_commands'}, - - - 'previousArticle': {backward: true, - findNext: 'article'}, - - 'previousButton': {backward: true, - findNext: 'button', - msgId: 'previous_button', - category: 'jump_commands'}, - 'previousCheckbox': {backward: true, - findNext: 'checkbox', - msgId: 'previous_checkbox', - category: 'jump_commands'}, - 'previousComboBox': {backward: true, - findNext: 'combobox', - msgId: 'previous_combo_box', - category: 'jump_commands'}, - 'previousControl': {backward: true, findNext: 'control'}, - 'previousEditText': {backward: true, - findNext: 'editText', - msgId: 'previous_edit_text', - category: 'jump_commands'}, - 'previousFormField': {backward: true, - findNext: 'formField', - msgId: 'previous_form_field', - category: 'jump_commands'}, - 'previousGraphic': {backward: true, - findNext: 'graphic', - msgId: 'previous_graphic', - category: 'jump_commands'}, - 'previousHeading': {backward: true, - findNext: 'heading', - msgId: 'previous_heading', - category: 'jump_commands'}, - 'previousHeading1': {backward: true, - findNext: 'heading1', - msgId: 'previous_heading1', - category: 'jump_commands'}, - 'previousHeading2': {backward: true, - findNext: 'heading2', - msgId: 'previous_heading2', - category: 'jump_commands'}, - 'previousHeading3': {backward: true, - findNext: 'heading3', - msgId: 'previous_heading3', - category: 'jump_commands'}, - 'previousHeading4': {backward: true, - findNext: 'heading4', - msgId: 'previous_heading4', - category: 'jump_commands'}, - 'previousHeading5': {backward: true, - findNext: 'heading5', - msgId: 'previous_heading5', - category: 'jump_commands'}, - 'previousHeading6': {backward: true, - findNext: 'heading6', - msgId: 'previous_heading6', - category: 'jump_commands'}, - - 'previousLandmark': {backward: true, - findNext: 'landmark', - msgId: 'previous_landmark', - category: 'jump_commands'}, - 'previousLink': {backward: true, - findNext: 'link', - msgId: 'previous_link', - category: 'jump_commands'}, - 'previousList': {backward: true, - findNext: 'list', - msgId: 'previous_list', - category: 'jump_commands'}, - 'previousListItem': {backward: true, - findNext: 'listItem', - msgId: 'previous_list_item', - category: 'jump_commands'}, - 'previousMath': {backward: true, - findNext: 'math', - msgId: 'previous_math', - category: 'jump_commands'}, - 'previousMedia': {backward: true, - findNext: 'media', - msgId: 'previous_media', - category: 'jump_commands'}, - 'previousRadio': {backward: true, - findNext: 'radio', - msgId: 'previous_radio', - category: 'jump_commands'}, - 'previousSection': {backward: true, findNext: 'section'}, - 'previousSlider': {backward: true, findNext: 'slider'}, - 'previousTable': {backward: true, - findNext: 'table', - msgId: 'previous_table', - category: 'jump_commands'}, - 'previousVisitedLink': {backward: true, - findNext: 'visitedLink', - msgId: 'previous_visited_link', - category: 'jump_commands'}, - - - // Table Actions. - 'announceHeaders': {announce: false, - msgId: 'announce_headers', - category: 'tables'}, - 'speakTableLocation': {announce: false, - msgId: 'speak_table_location', - category: 'tables'}, - 'goToFirstCell': {announce: true, - msgId: 'skip_to_beginning', - category: 'tables'}, - 'goToLastCell': {announce: true, - msgId: 'skip_to_end', - category: 'tables'}, - 'goToRowFirstCell': {announce: true, - msgId: 'skip_to_row_beginning', - category: 'tables'}, - 'goToRowLastCell': {announce: true, - msgId: 'skip_to_row_end', - category: 'tables'}, - 'goToColFirstCell': {announce: true, - msgId: 'skip_to_col_beginning', - category: 'tables'}, - 'goToColLastCell': {announce: true, - msgId: 'skip_to_col_end', - category: 'tables'}, - // These commands are left out of the options page because they involve - // multiple, non-user configurable modifiers. - 'previousRow': {backward: true, announce: true, skipInput: true}, - 'previousCol': {backward: true, announce: true, skipInput: true}, - 'nextRow': {forward: true, announce: true, skipInput: true}, - 'nextCol': {forward: true, announce: true, skipInput: true}, - - // Generic Actions. - 'enterShifter': {announce: true, - msgId: 'enter_content', - category: 'navigation'}, - 'exitShifter': {announce: true, - msgId: 'exit_content', - category: 'navigation'}, - 'exitShifterContent': {announce: true}, - - 'openLongDesc': {announce: false, - msgId: 'open_long_desc', - category: 'information'}, - - 'pauseAllMedia': {announce: false, - msgId: 'pause_all_media', - category: 'information'}, - - // Math specific commands. - 'toggleSemantics': {announce: false, - msgId: 'toggle_semantics', - category: 'information'}, - - // Braille specific commands. - 'routing': {announce: false, - allowEvents: true, - msgId: 'braille_routing', - category: 'braille'}, - 'pan_left': {backward: true, - announce: true, - msgId: 'braille_pan_left', - category: 'braille'}, - 'pan_right': {forward: true, - announce: true, - msgId: 'braille_pan_right', - category: 'braille'}, - 'line_up': {backward: true, - announce: true, - msgId: 'braille_line_up', - category: 'braille'}, - 'line_down': {forward: true, - announce: true, - msgId: 'braille_line_down', - category: 'braille'}, - 'top': {forward: true, - announce: true, - msgId: 'braille_top', - category: 'braille'}, - 'bottom': {backward: true, - announce: true, - msgId: 'braille_bottom', - category: 'braille'}, - - // Developer commands. - 'enableConsoleTts': {announce: false, - msgId: 'enable_tts_log', - category: 'developer'}, - 'toggleBrailleCaptions': {announce: false, - msgId: 'braille_captions', - category: 'developer'}, - - 'startHistoryRecording': {announce: false}, - 'stopHistoryRecording': {announce: false}, - 'autorunner': {announce: false}, - - 'debug': {announce: false}, - - 'nop': {announce: false} -}; - - -/** - * List of find next commands and their associated data. - * @type {Object<{predicate: string, - * forwardError: string, - * backwardError: string}>} - * predicate: The name of the predicate. This must be defined in DomPredicates. - * forwardError: The message id of the error string when moving forward. - * backwardError: The message id of the error string when moving backward. - */ -cvox.CommandStore.NODE_INFO_MAP = { - 'checkbox': {predicate: 'checkboxPredicate', - forwardError: 'no_next_checkbox', - backwardError: 'no_previous_checkbox', - typeMsg: 'role_checkbox'}, - 'radio': {predicate: 'radioPredicate', - forwardError: 'no_next_radio_button', - backwardError: 'no_previous_radio_button', - typeMsg: 'role_radio'}, - 'slider': {predicate: 'sliderPredicate', - forwardError: 'no_next_slider', - backwardError: 'no_previous_slider', - typeMsg: 'role_slider'}, - 'graphic': {predicate: 'graphicPredicate', - forwardError: 'no_next_graphic', - backwardError: 'no_previous_graphic', - typeMsg: 'UNUSED'}, - 'article': {predicate: 'articlePredicate', - forwardError: 'no_next_ARTICLE', - backwardError: 'no_previous_ARTICLE', - typeMsg: 'TAG_ARTICLE'}, - 'button': {predicate: 'buttonPredicate', - forwardError: 'no_next_button', - backwardError: 'no_previous_button', - typeMsg: 'role_button'}, - 'combobox': {predicate: 'comboBoxPredicate', - forwardError: 'no_next_combo_box', - backwardError: 'no_previous_combo_box', - typeMsg: 'role_combobox'}, - 'editText': {predicate: 'editTextPredicate', - forwardError: 'no_next_edit_text', - backwardError: 'no_previous_edit_text', - typeMsg: 'input_type_text'}, - 'heading': {predicate: 'headingPredicate', - forwardError: 'no_next_heading', - backwardError: 'no_previous_heading', - typeMsg: 'role_heading'}, - 'heading1': {predicate: 'heading1Predicate', - forwardError: 'no_next_heading_1', - backwardError: 'no_previous_heading_1'}, - 'heading2': {predicate: 'heading2Predicate', - forwardError: 'no_next_heading_2', - backwardError: 'no_previous_heading_2'}, - 'heading3': {predicate: 'heading3Predicate', - forwardError: 'no_next_heading_3', - backwardError: 'no_previous_heading_3'}, - 'heading4': {predicate: 'heading4Predicate', - forwardError: 'no_next_heading_4', - backwardError: 'no_previous_heading_4'}, - 'heading5': {predicate: 'heading5Predicate', - forwardError: 'no_next_heading_5', - backwardError: 'no_previous_heading_5'}, - 'heading6': {predicate: 'heading6Predicate', - forwardError: 'no_next_heading_6', - backwardError: 'no_previous_heading_6'}, - - 'link': {predicate: 'linkPredicate', - forwardError: 'no_next_link', - backwardError: 'no_previous_link', - typeMsg: 'role_link'}, - 'table': {predicate: 'tablePredicate', - forwardError: 'no_next_table', - backwardError: 'no_previous_table', - typeMsg: 'table_strategy'}, - 'visitedLink': {predicate: 'visitedLinkPredicate', - forwardError: 'no_next_visited_link', - backwardError: 'no_previous_visited_link', - typeMsg: 'role_link'}, - 'list': {predicate: 'listPredicate', - forwardError: 'no_next_list', - backwardError: 'no_previous_list', - typeMsg: 'role_list'}, - 'listItem': {predicate: 'listItemPredicate', - forwardError: 'no_next_list_item', - backwardError: 'no_previous_list_item', - typeMsg: 'role_listitem'}, - 'formField': {predicate: 'formFieldPredicate', - forwardError: 'no_next_form_field', - backwardError: 'no_previous_form_field', - typeMsg: 'role_form'}, - 'landmark': {predicate: 'landmarkPredicate', - forwardError: 'no_next_landmark', - backwardError: 'no_previous_landmark', - typeMsg: 'role_landmark'}, - 'math': {predicate: 'mathPredicate', - forwardError: 'no_next_math', - backwardError: 'no_previous_math', - typeMsg: 'math_expr'}, - 'media': {predicate: 'mediaPredicate', - forwardError: 'no_next_media_widget', - backwardError: 'no_previous_media_widget'}, - 'section': {predicate: 'sectionPredicate', - forwardError: 'no_next_section', - backwardError: 'no_previous_section'}, - 'control': {predicate: 'controlPredicate', - forwardError: 'no_next_control', - backwardError: 'no_previous_control'} -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs deleted file mode 100644 index 66ef0081254..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/command_store_test.unitjs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxCommandStoreUnitTest() {} - -CvoxCommandStoreUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.ChromeVoxUserCommands', - 'cvox.CommandStore', - ] -}; - -TEST_F('CvoxCommandStoreUnitTest', 'TableData', function() { - var categories = cvox.CommandStore.categories(); - assertEquals(10, categories.length); - assertEquals('modifier_keys', categories[0]); - assertEquals('controlling_speech', categories[1]); - assertEquals('navigation', categories[2]); - assertEquals('information', categories[3]); - assertEquals('help_commands', categories[4]); - assertEquals('overview', categories[5]); - assertEquals('jump_commands', categories[6]); - assertEquals('tables', categories[7]); - - assertEquals('stop_speech_key', - cvox.CommandStore.messageForCommand('stopSpeech')); - assertEquals('controlling_speech', - cvox.CommandStore.categoryForCommand('stopSpeech')); - - var controllingSpeechCmds = - cvox.CommandStore.commandsForCategory('controlling_speech'); - assertEquals(11, controllingSpeechCmds.length); - assertEquals('stopSpeech', controllingSpeechCmds[0]); - assertEquals('toggleChromeVox', controllingSpeechCmds[1]); - assertEquals('decreaseTtsRate', controllingSpeechCmds[2]); - assertEquals('increaseTtsRate', controllingSpeechCmds[3]); - assertEquals('decreaseTtsPitch', controllingSpeechCmds[4]); - assertEquals('increaseTtsPitch', controllingSpeechCmds[5]); -}); - - -/** Tests that undefined is returned for bad queries. */ -TEST_F('CvoxCommandStoreUnitTest', 'InvalidQueries', function() { - assertThat(cvox.CommandStore.commandsForCategory('foo'), eqJSON([])); - assertTrue(undefined == cvox.CommandStore.categoryForCommand('foo')); - assertTrue(undefined == cvox.CommandStore.messageForCommand('foo')); -}); - - -/** Tests the validity of every command. */ -TEST_F('CvoxCommandStoreUnitTest', 'CommandValidity', function() { - var categories = cvox.CommandStore.categories(); - for (var i = 0; i < categories.length; i++) { - var commands = cvox.CommandStore.commandsForCategory(categories[i]); - for (j = 0; j < commands.length; j++) { - var command = commands[j]; - assertEquals(command + ' function', - command + ' ' + typeof(cvox.ChromeVoxUserCommands.commands[command])); - } - } -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/composite_tts.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/composite_tts.js deleted file mode 100644 index bba28401525..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/composite_tts.js +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A composite TTS sends allows ChromeVox to use - * multiple TTS engines at the same time. - * - */ - -goog.provide('cvox.CompositeTts'); - -goog.require('cvox.TtsInterface'); - -/** - * A Composite Tts - * @constructor - * @implements {cvox.TtsInterface} - */ -cvox.CompositeTts = function() { - /** - * @type {Array<cvox.TtsInterface>} - * @private - */ - this.ttsEngines_ = []; -}; - - -/** - * Adds a TTS engine to the composite TTS - * @param {cvox.TtsInterface} tts The TTS to add. - * @return {cvox.CompositeTts} this. - */ -cvox.CompositeTts.prototype.add = function(tts) { - this.ttsEngines_.push(tts); - return this; -}; - - -/** - * @override - */ -cvox.CompositeTts.prototype.speak = - function(textString, queueMode, properties) { - this.ttsEngines_.forEach(function(engine) { - engine.speak(textString, queueMode, properties); - }); -}; - - -/** - * Returns true if any of the TTSes are still speaking. - * @override - */ -cvox.CompositeTts.prototype.isSpeaking = function() { - return this.ttsEngines_.some(function(engine) { - return engine.isSpeaking(); - }); -}; - - -/** - * @override - */ -cvox.CompositeTts.prototype.stop = function() { - this.ttsEngines_.forEach(function(engine) { - engine.stop(); - }); -}; - - -/** - * @override - */ -cvox.CompositeTts.prototype.addCapturingEventListener = function(listener) { - this.ttsEngines_.forEach(function(engine) { - engine.addCapturingEventListener(listener); - }); -}; - - -/** - * @override - */ -cvox.CompositeTts.prototype.increaseOrDecreaseProperty = - function(propertyName, increase) { - this.ttsEngines_.forEach(function(engine) { - engine.increaseOrDecreaseProperty(propertyName, increase); - }); -}; - - -/** - * @override - */ -cvox.CompositeTts.prototype.getDefaultProperty = function(property) { - for (var i = 0, engine; engine = this.ttsEngines_[i]; i++) { - var value = engine.getDefaultProperty(property); - if (value) { - return value; - } - } -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor.js deleted file mode 100644 index ce0d9415f0d..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor.js +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Defines the ContentEditableExtractor class. - */ - -goog.provide('cvox.ContentEditableExtractor'); - -goog.require('cvox.Cursor'); -goog.require('cvox.TraverseUtil'); - -/** - * Extracts the text and line break information from a contenteditable region. - * @constructor - */ -cvox.ContentEditableExtractor = function() { - /** - * The extracted, flattened, text. - * @type {string} - * @private - */ - this.text_ = ''; - - /** - * The start cursor/selection index. - * @type {number} - * @private - */ - this.start_ = 0; - - /** - * The end cursor/selection index. - * @type {number} - * @private - */ - this.end_ = 0; - - /** - * Map from line index to a data structure containing the start - * and end index within the line. - * @type {Object<number, {startIndex: number, endIndex: number}>} - * @private - */ - this.lines_ = {}; - - /** - * Map from 0-based character index to 0-based line index. - * @type {Array<number>} - * @private - */ - this.characterToLineMap_ = []; -}; - -/** - * Update the metadata. - * @param {Element} element The DOM element that's contentEditable. - */ -cvox.ContentEditableExtractor.prototype.update = function(element) { - /** - * Map from line index to a data structure containing the start - * and end index within the line. - * @type {Object<number, {startIndex: number, endIndex: number}>} - */ - var lines = {0: {startIndex: 0, endIndex: 0}}; - var startCursor = new cvox.Cursor(element, 0, ''); - var endCursor = startCursor.clone(); - var range = document.createRange(); - var rect; - var lineIndex = 0; - var lastBottom = null; - var text = ''; - var textSize = 0; - var selectionStartIndex = -1; - var selectionEndIndex = -1; - var sel = window.getSelection(); - var selectionStart = new cvox.Cursor(sel.baseNode, sel.baseOffset, ''); - var selectionEnd = new cvox.Cursor(sel.extentNode, sel.extentOffset, ''); - var setStart = false; - var setEnd = false; - while (true) { - var entered = []; - var left = []; - var c = cvox.TraverseUtil.forwardsChar(endCursor, entered, left); - var done = false; - if (!c) { - done = true; - } - for (var i = 0; i < left.length && !done; i++) { - if (left[i] == element) { - done = true; - } - } - if (done) { - break; - } - - range.setStart(startCursor.node, startCursor.index); - range.setEnd(endCursor.node, endCursor.index); - rect = range.getBoundingClientRect(); - if (!rect || rect.width == 0 || rect.height == 0) { - continue; - } - - if (lastBottom !== null && - rect.bottom != lastBottom && - textSize > 0 && - text.substr(-1).match(/\S/) && - c.match(/\S/)) { - text += '\n'; - textSize++; - } - - if (startCursor.node != endCursor.node && endCursor.index > 0) { - range.setStart(endCursor.node, endCursor.index - 1); - rect = range.getBoundingClientRect(); - if (!rect || rect.width == 0 || rect.height == 0) { - continue; - } - } - - if (!setStart && - selectionStartIndex == -1 && - endCursor.node == selectionStart.node && - endCursor.index >= selectionStart.index) { - if (endCursor.index > selectionStart.index) { - selectionStartIndex = textSize; - } else { - setStart = true; - } - } - if (!setEnd && - selectionEndIndex == -1 && - endCursor.node == selectionEnd.node && - endCursor.index >= selectionEnd.index) { - if (endCursor.index > selectionEnd.index) { - selectionEndIndex = textSize; - } else { - setEnd = true; - } - } - - if (lastBottom === null) { - // This is the first character we've successfully measured on this - // line. Save the vertical position but don't do anything else. - lastBottom = rect.bottom; - } else if (rect.bottom != lastBottom) { - lines[lineIndex].endIndex = textSize; - lineIndex++; - lines[lineIndex] = {startIndex: textSize, endIndex: textSize}; - lastBottom = rect.bottom; - } - text += c; - textSize++; - startCursor = endCursor.clone(); - - if (setStart) { - selectionStartIndex = textSize; - setStart = false; - } - if (setEnd) { - selectionEndIndex = textSize; - setEnd = false; - } - } - - // Finish up the last line. - lines[lineIndex].endIndex = textSize; - - // Create a map from character index to line number. - var characterToLineMap = []; - for (var i = 0; i <= lineIndex; i++) { - for (var j = lines[i].startIndex; j <= lines[i].endIndex; j++) { - characterToLineMap[j] = i; - } - } - - // Finish updating fields. - this.text_ = text; - this.characterToLineMap_ = characterToLineMap; - this.lines_ = lines; - - this.start_ = selectionStartIndex >= 0 ? selectionStartIndex : text.length; - this.end_ = selectionEndIndex >= 0 ? selectionEndIndex : text.length; -}; - -/** - * Get the text. - * @return {string} The extracted, flattened, text. - */ -cvox.ContentEditableExtractor.prototype.getText = function() { - return this.text_; -}; - -/** - * @return {number} The start cursor/selection index. - */ -cvox.ContentEditableExtractor.prototype.getStartIndex = function() { - return this.start_; -}; - -/** - * @return {number} The end cursor/selection index. - */ -cvox.ContentEditableExtractor.prototype.getEndIndex = function() { - return this.end_; -}; - -/** - * Get the line number corresponding to a particular index. - * @param {number} index The 0-based character index. - * @return {number} The 0-based line number corresponding to that character. - */ -cvox.ContentEditableExtractor.prototype.getLineIndex = function(index) { - return this.characterToLineMap_[index]; -}; - -/** - * Get the start character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the first character in this line. - */ -cvox.ContentEditableExtractor.prototype.getLineStart = function(index) { - return this.lines_[index].startIndex; -}; - -/** - * Get the end character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the end of this line. - */ -cvox.ContentEditableExtractor.prototype.getLineEnd = function(index) { - return this.lines_[index].endIndex; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs deleted file mode 100644 index c5f0fa3ba57..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/content_editable_extractor_test.unitjs +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxContentEditableExtractorUnitTest() {} - -CvoxContentEditableExtractorUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.ContentEditableExtractor', - ] -}; - -/** - * Helper function to set the selection - * @param {Node} startNode The base/start node of the range. - * @param {number} startOffset The 0-based character index of the start. - * @param {Node} endNode The extent/end node of the range. - * @param {number} endOffset The 0-based character index of the end. - */ -function setSelection(startNode, startOffset, endNode, endOffset) { - var r = document.createRange(); - r.setStart(startNode, startOffset); - r.setEnd(endNode, endOffset); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(r); -} - -TEST_F('CvoxContentEditableExtractorUnitTest', 'EmptyElement', function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" contentEditable="true"></div> - </div> - */}); - - var textbox = $('textbox'); - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('', extractor.getText()); - assertEquals(0, extractor.getStartIndex()); - assertEquals(0, extractor.getEndIndex(0)); - assertEquals(0, extractor.getLineIndex(0)); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(0, extractor.getLineEnd(0)); -}); - -/** - * Test getting text and selections from a single contenteditable node. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'SingleTextNode', function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" contentEditable="true">Hello</div> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('Hello', extractor.getText()); - assertEquals(0, extractor.getLineIndex(0)); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(5, extractor.getLineEnd(0)); - assertEquals(5, extractor.getStartIndex()); - assertEquals(5, extractor.getEndIndex()); - - // Test all possible cursor positions. - for (var i = 0; i <= 5; i++) { - setSelection(textbox.firstChild, i, textbox.firstChild, i); - extractor.update(textbox); - assertEquals(i, extractor.getStartIndex()); - assertEquals(i, extractor.getEndIndex()); - } - - // Test all possible ways to select one character. - for (i = 0; i < 5; i++) { - setSelection(textbox.firstChild, i, textbox.firstChild, i + 1); - extractor.update(textbox); - assertEquals(i, extractor.getStartIndex()); - assertEquals(i + 1, extractor.getEndIndex()); - } - - // Test selecting everything. - setSelection(textbox.firstChild, 0, textbox.firstChild, 5); - extractor.update(textbox); - assertEquals(0, extractor.getStartIndex()); - assertEquals(5, extractor.getEndIndex()); -}); - -/** - * Test getting text and selections from a contenteditable node with - * nonprinted whitespace. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'TextWithWhitespace', - function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" contentEditable="true"> Hello World </div> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('Hello World', extractor.getText()); - assertEquals(0, extractor.getLineIndex(0)); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(11, extractor.getLineEnd(0)); - assertEquals(11, extractor.getStartIndex()); - assertEquals(11, extractor.getEndIndex()); - - // Test all *reasonable* indexes of a selection into this text node - // and the logical index into the text that these should result in. - var expectedIndexMap = { - 0: 0, - 1: 0, - 2: 1, - 3: 2, - 4: 3, - 5: 4, - 6: 5, - // Note: index=7 should never happen - 8: 6, - 9: 7, - 10: 8, - 11: 9, - 12: 10, - 13: 11, - 14: 11 - }; - for (var srcIndex in expectedIndexMap) { - var dstIndex = expectedIndexMap[srcIndex]; - setSelection(textbox.firstChild, srcIndex, textbox.firstChild, srcIndex); - extractor.update(textbox); - assertEquals(dstIndex, extractor.getStartIndex()); - assertEquals(dstIndex, extractor.getEndIndex()); - } -}); - -/** - * Test getting text and selections from a contenteditable node with - * preformatted text. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'Preformatted', function() { - this.loadDoc(function() {/*! - <div> - <pre id="textbox" contentEditable="true">aaaaaaaaaa -bbbbbbbbbb -cccccccccc</pre> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc', extractor.getText()); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(11, extractor.getLineEnd(0)); - assertEquals(11, extractor.getLineStart(1)); - assertEquals(22, extractor.getLineEnd(1)); - assertEquals(22, extractor.getLineStart(2)); - assertEquals(32, extractor.getLineEnd(2)); - - // Test all possible cursor positions. - for (var i = 0; i <= 32; i++) { - setSelection(textbox.firstChild, i, textbox.firstChild, i); - extractor.update(textbox); - assertEquals(i, extractor.getStartIndex()); - assertEquals(i, extractor.getEndIndex()); - } -}); - -/** - * Test getting text and selections from a contenteditable node with - * wrapping. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'WordWrap', function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" - style="width: 1em; word-wrap: normal" - contentEditable="true">One two three</div> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('One\ntwo\nthree', extractor.getText()); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(4, extractor.getLineEnd(0)); - assertEquals(4, extractor.getLineStart(1)); - assertEquals(8, extractor.getLineEnd(1)); - assertEquals(8, extractor.getLineStart(2)); - assertEquals(13, extractor.getLineEnd(2)); - - // Test all possible cursor positions. - for (var i = 0; i <= 13; i++) { - setSelection(textbox.firstChild, i, textbox.firstChild, i); - extractor.update(textbox); - assertEquals(i, extractor.getStartIndex()); - assertEquals(i, extractor.getEndIndex()); - } -}); - -/** - * Test getting text and lines from a contenteditable region - * containing two paragraphs and an explicit line break. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'TwoParas', function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" contentEditable="true"> - <p>One</p> - <p>Two<br>Three</p> - </div> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('One\nTwo\nThree', - extractor.getText()); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(4, extractor.getLineEnd(0)); - assertEquals(4, extractor.getLineStart(1)); - assertEquals(8, extractor.getLineEnd(1)); - assertEquals(8, extractor.getLineStart(2)); - assertEquals(13, extractor.getLineEnd(2)); -}); - -/** - * Test getting text and lines from a contenteditable region - * containing two paragraphs, this time with added whitespace. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'TwoParasWithWhitespace', - function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" contentEditable="true"> - <p> One </p> - <p> Two <br> Three </p> - </div> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('One\nTwo Three', - extractor.getText()); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(4, extractor.getLineEnd(0)); - assertEquals(4, extractor.getLineStart(1)); - assertEquals(8, extractor.getLineEnd(1)); - assertEquals(8, extractor.getLineStart(2)); - assertEquals(13, extractor.getLineEnd(2)); -}); - -/** - * Test getting text and lines from a contenteditable region - * containing some raw text and then some text in a block-level element. - */ -TEST_F('CvoxContentEditableExtractorUnitTest', 'NodePlusElement', function() { - this.loadDoc(function() {/*! - <div> - <div id="textbox" - contentEditable="true">One<div>Two<br>Three</div></div> - </div> - */}); - var textbox = $('textbox'); - - var extractor = new cvox.ContentEditableExtractor(); - extractor.update(textbox); - assertEquals('One\nTwo\nThree', - extractor.getText()); - assertEquals(0, extractor.getLineStart(0)); - assertEquals(4, extractor.getLineEnd(0)); - assertEquals(4, extractor.getLineStart(1)); - assertEquals(8, extractor.getLineEnd(1)); - assertEquals(8, extractor.getLineStart(2)); - assertEquals(13, extractor.getLineEnd(2)); - - var oneTextNode = textbox.firstChild; - assertEquals('One', oneTextNode.data); - var twoTextNode = textbox.firstElementChild.firstChild; - assertEquals('Two', twoTextNode.data); - var threeTextNode = twoTextNode.nextSibling.nextSibling; - assertEquals('Three', threeTextNode.data); - - // End of first line. - setSelection(oneTextNode, 3, oneTextNode, 3); - extractor.update(textbox); - assertEquals(3, extractor.getStartIndex()); - assertEquals(3, extractor.getEndIndex()); - - // Beginning of second line. - setSelection(twoTextNode, 0, twoTextNode, 0); - extractor.update(textbox); - assertEquals(4, extractor.getStartIndex()); - assertEquals(4, extractor.getEndIndex()); - - // End of second line. - setSelection(twoTextNode, 3, twoTextNode, 3); - extractor.update(textbox); - assertEquals(7, extractor.getStartIndex()); - assertEquals(7, extractor.getEndIndex()); - - // Beginning of third line. - setSelection(threeTextNode, 0, threeTextNode, 0); - extractor.update(textbox); - assertEquals(8, extractor.getStartIndex()); - assertEquals(8, extractor.getEndIndex()); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor.js deleted file mode 100644 index 1cf59633647..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor.js +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Simple class to represent a cursor location in the document. - */ - -goog.provide('cvox.Cursor'); - -/** - * A class to represent a cursor location in the document, - * like the start position or end position of a selection range. - * - * Later this may be extended to support "virtual text" for an object, - * like the ALT text for an image. - * - * Note: we cache the text of a particular node at the time we - * traverse into it. Later we should add support for dynamically - * reloading it. - * NOTE: Undefined behavior if node is null - * @param {Node} node The DOM node. - * @param {number} index The index of the character within the node. - * @param {string} text The cached text contents of the node. - * @constructor - */ -cvox.Cursor = function(node, index, text) { - this.node = node; - this.index = index; - this.text = text; -}; - -/** - * @return {!cvox.Cursor} A new cursor pointing to the same location. - */ -cvox.Cursor.prototype.clone = function() { - return new cvox.Cursor(this.node, this.index, this.text); -}; - -/** - * Modify this cursor to point to the location that another cursor points to. - * @param {!cvox.Cursor} otherCursor The cursor to copy from. - */ -cvox.Cursor.prototype.copyFrom = function(otherCursor) { - this.node = otherCursor.node; - this.index = otherCursor.index; - this.text = otherCursor.text; -}; - -/** - * Check for equality. - * @param {!cvox.Cursor} rhs The cursor to compare against. - * @return {boolean} True if equal. - */ -cvox.Cursor.prototype.equals = function(rhs) { - return this.node == rhs.node && - this.index == rhs.index && - this.text == rhs.text; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor_selection.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor_selection.js deleted file mode 100644 index f04351633c1..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor_selection.js +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Simple class to represent a cursor selection. - * A cursor selection is just two cursors; one for the start and one for - * the end of some interval in the document. - */ - -goog.provide('cvox.CursorSelection'); - -goog.require('cvox.Cursor'); -goog.require('cvox.SelectionUtil'); -goog.require('cvox.TraverseUtil'); - - -/** - * If the start node and end node are the same, and the indexes are the same, - * the selection is interpreted to be a node. Otherwise, it is interpreted - * to be a range. - * @param {!cvox.Cursor} start The starting cursor. - * @param {!cvox.Cursor} end The ending cursor. - * @param {boolean=} opt_reverse Whether to make it a reversed selection or - * not. Default is selection is not reversed. If start and end are in the - * wrong order, they will be swapped automatically. - * NOTE: Can't infer automatically whether the selection is reversed because - * for a selection on a single node, the start and end are equal. - * @constructor - */ -cvox.CursorSelection = function(start, end, opt_reverse) { - this.start = start.clone(); - this.end = end.clone(); - - if (opt_reverse == undefined) { - opt_reverse = false; - } - /** @private */ - this.isReversed_ = opt_reverse; - - if ((this.isReversed_ && - this.start.node.compareDocumentPosition(this.end.node) == - cvox.CursorSelection.BEFORE) || - (!this.isReversed_ && - this.end.node.compareDocumentPosition(this.start.node) == - cvox.CursorSelection.BEFORE)) { - var oldStart = this.start; - this.start = this.end; - this.end = oldStart; - } -}; - - -/** - * From http://www.w3schools.com/jsref/met_node_comparedocumentposition.asp - */ -cvox.CursorSelection.BEFORE = 4; - - -/** - * If true, ensures that this selection is reversed. Otherwise, ensures that - * it is not reversed. - * @param {boolean} reversed True to reverse. False to nonreverse. - * @return {!cvox.CursorSelection} For chaining. - */ -cvox.CursorSelection.prototype.setReversed = function(reversed) { - if (reversed == this.isReversed_) { - return this; - } - var oldStart = this.start; - this.start = this.end; - this.end = oldStart; - this.isReversed_ = reversed; - return this; -}; - - -/** - * Returns true if this selection is a reverse selection. - * @return {boolean} true if reversed. - */ -cvox.CursorSelection.prototype.isReversed = function() { - return this.isReversed_; -}; - - -/** - * Returns start if not reversed, end if reversed. - * @return {!cvox.Cursor} start if not reversed, end if reversed. - */ -cvox.CursorSelection.prototype.absStart = function() { - return this.isReversed_ ? this.end : this.start; -}; - -/** - * Returns end if not reversed, start if reversed. - * @return {!cvox.Cursor} end if not reversed, start if reversed. - */ -cvox.CursorSelection.prototype.absEnd = function() { - return this.isReversed_ ? this.start : this.end; -}; - - -/** - * Clones the selection. - * @return {!cvox.CursorSelection} The cloned selection. - */ -cvox.CursorSelection.prototype.clone = function() { - return new cvox.CursorSelection(this.start, this.end, this.isReversed_); -}; - - -/** - * Places a DOM selection around this CursorSelection. - */ -cvox.CursorSelection.prototype.select = function() { - var sel = window.getSelection(); - sel.removeAllRanges(); - this.normalize(); - sel.addRange(this.getRange()); -}; - - -/** - * Creates a new cursor selection that starts and ends at the node. - * Returns null if node is null. - * @param {Node} node The node. - * @return {cvox.CursorSelection} The selection. - */ -cvox.CursorSelection.fromNode = function(node) { - if (!node) { - return null; - } - var text = cvox.TraverseUtil.getNodeText(node); - - return new cvox.CursorSelection( - new cvox.Cursor(node, 0, text), - new cvox.Cursor(node, 0, text)); -}; - - -/** - * Creates a new cursor selection that starts and ends at document.body. - * @return {!cvox.CursorSelection} The selection. - */ -cvox.CursorSelection.fromBody = function() { - return /** @type {!cvox.CursorSelection} */ ( - cvox.CursorSelection.fromNode(document.body)); -}; - -/** - * Returns the text that the selection spans. - * @return {string} Text within the selection. '' if it is a node selection. - */ -cvox.CursorSelection.prototype.getText = function() { - if (this.start.equals(this.end)) { - return cvox.TraverseUtil.getNodeText(this.start.node); - } - return cvox.SelectionUtil.getRangeText(this.getRange()); -}; - -/** - * Returns a range from the given selection. - * @return {Range} The range. - */ -cvox.CursorSelection.prototype.getRange = function() { - var range = document.createRange(); - if (this.isReversed_) { - range.setStart(this.end.node, this.end.index); - range.setEnd(this.start.node, this.start.index); - } else { - range.setStart(this.start.node, this.start.index); - range.setEnd(this.end.node, this.end.index); - } - return range; -}; - -/** - * Check for equality. - * @param {!cvox.CursorSelection} rhs The CursorSelection to compare against. - * @return {boolean} True if equal. - */ -cvox.CursorSelection.prototype.equals = function(rhs) { - return this.start.equals(rhs.start) && this.end.equals(rhs.end); -}; - -/** - * Check for equality regardless of direction. - * @param {!cvox.CursorSelection} rhs The CursorSelection to compare against. - * @return {boolean} True if equal. - */ -cvox.CursorSelection.prototype.absEquals = function(rhs) { - return ((this.start.equals(rhs.start) && this.end.equals(rhs.end)) || - (this.end.equals(rhs.start) && this.start.equals(rhs.end))); -}; - -/** - * Determines if this starts before another CursorSelection in document order. - * If this is reversed, then a reversed document order is checked. - * In the case that this and rhs start at the same position, we return true. - * @param {!cvox.CursorSelection} rhs The selection to compare. - * @return {boolean} True if this is before rhs. - */ -cvox.CursorSelection.prototype.directedBefore = function(rhs) { - var leftToRight = this.start.node.compareDocumentPosition(rhs.start.node) == - cvox.CursorSelection.BEFORE; - return this.start.node == rhs.start.node || - (this.isReversed() ? !leftToRight : leftToRight); -}; -/** - * Normalizes this selection. - * Use this routine to adjust CursorSelection's that have been collapsed due to - * convention such as when a CursorSelection references a node without attention - * to its endpoints. - * The result is to surround the node with this cursor. - * @return {!cvox.CursorSelection} The normalized selection. - */ -cvox.CursorSelection.prototype.normalize = function() { - if (this.absEnd().index == 0 && this.absEnd().node) { - var node = this.absEnd().node; - - // DOM ranges use different conventions when surrounding a node. For - // instance, input nodes endOffset is always 0 while h1's endOffset is 1 - //with both having no children. Use a range to compute the endOffset. - var testRange = document.createRange(); - testRange.selectNodeContents(node); - this.absEnd().index = testRange.endOffset; - } - return this; -}; - -/** - * Collapses to the directed start of the selection. - * @return {!cvox.CursorSelection} For chaining. - */ -cvox.CursorSelection.prototype.collapse = function() { - // Not a selection. - if (this.start.equals(this.end)) { - return this; - } - this.end.copyFrom(this.start); - if (this.start.text.length == 0) { - return this; - } - if (this.isReversed()) { - if (this.end.index > 0) { - this.end.index--; - } - } else { - if (this.end.index < this.end.text.length) { - this.end.index++; - } - } - return this; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs deleted file mode 100644 index d4a72beba4a..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/cursor_selection_test.unitjs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxCursorSelectionUnitTest() {} - -CvoxCursorSelectionUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.CursorSelection' - ] -}; - -TEST_F('CvoxCursorSelectionUnitTest', 'Reverse', function() { - this.loadDoc(function() {/*! - <div> - <p id="a">a</p> - <p id="b">b</p> - </div> - */}); - var a = new cvox.Cursor($('a'), 0, ''); - var b = new cvox.Cursor($('b'), 0, ''); - - var aa = new cvox.CursorSelection(a, a); - assertEquals(false, aa.isReversed()); - aa.setReversed(true); - assertEquals(true, aa.isReversed()); - - var ab = new cvox.CursorSelection(a, b); - assertEquals(false, ab.isReversed()); - ab.setReversed(true); - assertEquals(true, ab.isReversed()); - assertEquals(true, ab.start.equals(b)); - assertEquals(true, ab.end.equals(a)); - ab.setReversed(false); - assertEquals(false, ab.isReversed()); - assertEquals(true, ab.start.equals(a)); - assertEquals(true, ab.end.equals(b)); - - ab = new cvox.CursorSelection(b, a); - assertEquals(false, ab.isReversed()); - assertEquals(true, ab.start.equals(a)); - assertEquals(true, ab.end.equals(b)); - - var ba = new cvox.CursorSelection(b, a, true); - assertEquals(true, ba.isReversed()); - assertEquals(true, ba.start.equals(b)); - assertEquals(true, ba.end.equals(a)); - - ba = new cvox.CursorSelection(a, b, true); - assertEquals(true, ba.isReversed()); - assertEquals(true, ba.start.equals(b)); - assertEquals(true, ba.end.equals(a)); -}); - - -/** Tests correctness of collapsing selections. */ -TEST_F('CvoxCursorSelectionUnitTest', 'Collapse', function() { - this.loadDoc(function() {/*! - <p id='1'>This is a test.</p> - */}); - var text = $('1').firstChild; - var a = new cvox.Cursor(text, 0, 'This is a test.'); - var b = new cvox.Cursor(text, 13, 'This is a test.'); - var c = new cvox.Cursor(text, 5, 'This is a test.'); - var d = new cvox.Cursor(text, 8, 'This is a test.'); - - var aa = new cvox.CursorSelection(a, a).collapse(); - assertEquals(0, aa.start.index); - assertEquals(0, aa.end.index); - - var ab = new cvox.CursorSelection(a, b).collapse(); - assertEquals(0, ab.start.index); - assertEquals(1, ab.end.index); - - var ba = new cvox.CursorSelection(b, a, true).collapse(); - assertEquals(12, ba.absStart().index); - assertEquals(13, ba.absEnd().index); - - var cd = new cvox.CursorSelection(c, d).collapse(); - assertEquals(5, cd.start.index); - assertEquals(6, cd.end.index); - - var dc = new cvox.CursorSelection(d, c, true).collapse(); - assertEquals(7, dc.absStart().index); - assertEquals(8, dc.absEnd().index); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/date_widget.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/date_widget.js deleted file mode 100644 index b494db9282e..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/date_widget.js +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.ChromeVoxHTMLDateWidget'); - -goog.require('Msgs'); - -/** - * @fileoverview Gives the user spoken feedback as they interact with the date - * widget (input type=date). - * - */ - -/** - * A class containing the information needed to speak - * a text change event to the user. - * - * @constructor - * @param {Element} dateElem The time widget element. - * @param {cvox.TtsInterface} tts The TTS object from ChromeVox. - */ -cvox.ChromeVoxHTMLDateWidget = function(dateElem, tts) { - var self = this; - /** - * Currently selected field in the widget. - * @type {number} - * @private - */ - this.pos_ = 0; - var maxpos = 2; - if (dateElem.type == 'month' || dateElem.type == 'week') { - maxpos = 1; - } - /** - * The maximum number of fields in the widget. - * @type {number} - * @private - */ - this.maxPos_ = maxpos; - /** - * The HTML node of the widget. - * @type {Node} - * @private - */ - this.dateElem_ = dateElem; - /** - * A handle to the ChromeVox TTS object. - * @type {Object} - * @private - */ - this.dateTts_ = tts; - /** - * The previous value of the year field. - * @type {number} - * @private - */ - this.pYear_ = -1; - /** - * The previous value of the month field. - * @type {number} - * @private - */ - this.pMonth_ = -1; - /** - * The previous value of the week field. - * @type {number} - * @private - */ - this.pWeek_ = -1; - /** - * The previous value of the day field. - * @type {number} - * @private - */ - this.pDay_ = -1; - - // Use listeners to make this work when running tests inside of ChromeVox. - this.keyListener_ = function(evt) { - self.eventHandler_(evt); - }; - this.blurListener_ = function(evt) { - self.shutdown(); - }; - - // Ensure we have a reasonable value to start with. - if (this.dateElem_.value.length == 0) { - this.forceInitTime_(); - } - - // Move the cursor to the first position so that we are guaranteed to start - // off at the hours position. - for (var i = 0; i < this.maxPos_; i++) { - var evt = document.createEvent('KeyboardEvent'); - evt.initKeyboardEvent( - 'keydown', true, true, window, 'Left', 0, false, false, false, false); - this.dateElem_.dispatchEvent(evt); - evt = document.createEvent('KeyboardEvent'); - evt.initKeyboardEvent( - 'keyup', true, true, window, 'Left', 0, false, false, false, false); - this.dateElem_.dispatchEvent(evt); - } - - this.dateElem_.addEventListener('keydown', this.keyListener_, false); - this.dateElem_.addEventListener('keyup', this.keyListener_, false); - this.dateElem_.addEventListener('blur', this.blurListener_, false); - this.update_(true); -}; - -/** - * Removes the key listeners for the time widget. - * - */ -cvox.ChromeVoxHTMLDateWidget.prototype.shutdown = function() { - this.dateElem_.removeEventListener('blur', this.blurListener_, false); - this.dateElem_.removeEventListener('keydown', this.keyListener_, false); - this.dateElem_.removeEventListener('keyup', this.keyListener_, false); -}; - -/** - * Forces a sensible default value so that there is something there that can - * be inspected with JS. - * @private - */ -cvox.ChromeVoxHTMLDateWidget.prototype.forceInitTime_ = function() { - var currentDate = new Date(); - var valueString = ''; - var yearString = currentDate.getFullYear() + ''; - // Date.getMonth starts at 0, but the value for the HTML5 date widget needs to - // start at 1. - var monthString = currentDate.getMonth() + 1 + ''; - if (monthString.length < 2) { - monthString = '0' + monthString; // Month format is MM. - } - var dayString = currentDate.getDate() + ''; - - switch (this.dateElem_.type) { - case 'month': - valueString = yearString + '-' + monthString; - break; - case 'week': - // Based on info from: http://www.merlyn.demon.co.uk/weekcalc.htm#WNR - currentDate.setHours(0, 0, 0); - // Set to nearest Thursday: current date + 4 - current day number - // Make Sunday's day number 7 - currentDate.setDate( - currentDate.getDate() + 4 - (currentDate.getDay() || 7)); - // Get first day of year - var yearStart = new Date(currentDate.getFullYear(), 0, 1); - // Calculate full weeks to nearest Thursday - var weekString = - Math.ceil((((currentDate - yearStart) / 86400000) + 1) / 7) + ''; - if (weekString.length < 2) { - weekString = '0' + weekString; // Week format is WXX. - } - weekString = 'W' + weekString; - valueString = yearString + '-' + weekString; - break; - default: - valueString = yearString + '-' + monthString + '-' + dayString; - break; - } - this.dateElem_.setAttribute('value', valueString); -}; - -/** - * Ensure that the position stays within bounds. - * @private - */ -cvox.ChromeVoxHTMLDateWidget.prototype.handlePosChange_ = function() { - this.pos_ = Math.max(this.pos_, 0); - this.pos_ = Math.min(this.pos_, this.maxPos_); - // TODO (clchen, dtseng): Make this logic i18n once there is a way to - // determine what the date format actually is. For now, assume that: - // date == mm/dd/yyyy - // week == ww/yyyy - // month == mm/yyyy. - switch (this.pos_) { - case 0: - if (this.dateElem_.type == 'week') { - this.pWeek_ = -1; - } else { - this.pMonth_ = -1; - } - break; - case 1: - if (this.dateElem_.type == 'date') { - this.pDay_ = -1; - } else { - this.pYear_ = -1; - } - break; - case 2: - this.pYear_ = -1; - break; - } -}; - -/** - * Speaks any changes to the control. - * @private - * @param {boolean} shouldSpeakLabel Whether or not to speak the label. - */ -cvox.ChromeVoxHTMLDateWidget.prototype.update_ = function(shouldSpeakLabel) { - var splitDate = this.dateElem_.value.split('-'); - if (splitDate.length < 1) { - this.forceInitTime_(); - return; - } - - var year = -1; - var month = -1; - var week = -1; - var day = -1; - - year = parseInt(splitDate[0], 10); - - if (this.dateElem_.type == 'week') { - week = parseInt(splitDate[1].replace('W', ''), 10); - } else if (this.dateElem_.type == 'date') { - month = parseInt(splitDate[1], 10); - day = parseInt(splitDate[2], 10); - } else { - month = parseInt(splitDate[1], 10); - } - - var changeMessage = ''; - - if (shouldSpeakLabel) { - changeMessage = cvox.DomUtil.getName(this.dateElem_, true, true) + '\n'; - } - - if (week != this.pWeek_) { - changeMessage = changeMessage + - Msgs.getMsg('datewidget_week') + week + '\n'; - this.pWeek_ = week; - } - - if (month != this.pMonth_) { - var monthName = ''; - switch (month) { - case 1: - monthName = Msgs.getMsg('datewidget_january'); - break; - case 2: - monthName = Msgs.getMsg('datewidget_february'); - break; - case 3: - monthName = Msgs.getMsg('datewidget_march'); - break; - case 4: - monthName = Msgs.getMsg('datewidget_april'); - break; - case 5: - monthName = Msgs.getMsg('datewidget_may'); - break; - case 6: - monthName = Msgs.getMsg('datewidget_june'); - break; - case 7: - monthName = Msgs.getMsg('datewidget_july'); - break; - case 8: - monthName = Msgs.getMsg('datewidget_august'); - break; - case 9: - monthName = Msgs.getMsg('datewidget_september'); - break; - case 10: - monthName = Msgs.getMsg('datewidget_october'); - break; - case 11: - monthName = Msgs.getMsg('datewidget_november'); - break; - case 12: - monthName = Msgs.getMsg('datewidget_december'); - break; - } - changeMessage = changeMessage + monthName + '\n'; - this.pMonth_ = month; - } - - if (day != this.pDay_) { - changeMessage = changeMessage + day + '\n'; - this.pDay_ = day; - } - - if (year != this.pYear_) { - changeMessage = changeMessage + year + '\n'; - this.pYear_ = year; - } - - if (changeMessage.length > 0) { - this.dateTts_.speak(changeMessage, 0, null); - } -}; - -/** - * Handles user key events. - * @private - * @param {Event} evt The event to be handled. - */ -cvox.ChromeVoxHTMLDateWidget.prototype.eventHandler_ = function(evt) { - var shouldSpeakLabel = false; - if (evt.type == 'keydown') { - // Handle tab/right arrow - if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) { - this.pos_++; - this.handlePosChange_(); - shouldSpeakLabel = true; - } - // Handle shift+tab/left arrow - if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) { - this.pos_--; - this.handlePosChange_(); - shouldSpeakLabel = true; - } - // For all other cases, fall through and let update_ decide if there are any - // changes that need to be spoken. - } - this.update_(shouldSpeakLabel); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/description_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/description_util.js deleted file mode 100644 index d7bece47b98..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/description_util.js +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A utility class for building NavDescriptions from the dom. - */ - - -goog.provide('cvox.DescriptionUtil'); - -goog.require('cvox.AriaUtil'); -goog.require('cvox.AuralStyleUtil'); -goog.require('cvox.BareObjectWalker'); -goog.require('cvox.CursorSelection'); -goog.require('cvox.DomUtil'); -goog.require('cvox.EarconUtil'); -goog.require('cvox.MathmlStore'); -goog.require('cvox.NavDescription'); -goog.require('cvox.SpeechRuleEngine'); -goog.require('cvox.TraverseMath'); - - -/** - * Lists all Node tagName's who's description is derived from its subtree. - * @type {Object<boolean>} - */ -cvox.DescriptionUtil.COLLECTION_NODE_TYPE = { - 'H1': true, - 'H2': true, - 'H3': true, - 'H4': true, - 'H5': true, - 'H6': true -}; - -/** - * Get a control's complete description in the same format as if you - * navigated to the node. - * @param {Element} control A control. - * @param {Array<Node>=} opt_changedAncestors The changed ancestors that will - * be used to determine what needs to be spoken. If this is not provided, the - * ancestors used to determine what needs to be spoken will just be the control - * itself and its surrounding control if it has one. - * @return {cvox.NavDescription} The description of the control. - */ -cvox.DescriptionUtil.getControlDescription = - function(control, opt_changedAncestors) { - var ancestors = [control]; - if (opt_changedAncestors && (opt_changedAncestors.length > 0)) { - ancestors = opt_changedAncestors; - } else { - var surroundingControl = cvox.DomUtil.getSurroundingControl(control); - if (surroundingControl) { - ancestors = [surroundingControl, control]; - } - } - - var description = cvox.DescriptionUtil.getDescriptionFromAncestors( - ancestors, true, cvox.VERBOSITY_VERBOSE); - - // Use heuristics if the control doesn't otherwise have a name. - if (surroundingControl) { - var name = cvox.DomUtil.getName(surroundingControl); - if (name.length == 0) { - name = cvox.DomUtil.getControlLabelHeuristics(surroundingControl); - if (name.length > 0) { - description.context = name + ' ' + description.context; - } - } - } else { - var name = cvox.DomUtil.getName(control); - if (name.length == 0) { - name = cvox.DomUtil.getControlLabelHeuristics(control); - if (name.length > 0) { - description.text = cvox.DomUtil.collapseWhitespace(name); - } - } - var value = cvox.DomUtil.getValue(control); - if (value.length > 0) { - description.userValue = cvox.DomUtil.collapseWhitespace(value); - } - } - - return description; -}; - - -/** - * Returns a description of a navigation from an array of changed - * ancestor nodes. The ancestors are in order from the highest in the - * tree to the lowest, i.e. ending with the current leaf node. - * - * @param {Array<Node>} ancestorsArray An array of ancestor nodes. - * @param {boolean} recursive Whether or not the element's subtree should - * be used; true by default. - * @param {number} verbosity The verbosity setting. - * @return {cvox.NavDescription} The description of the navigation action. - */ -cvox.DescriptionUtil.getDescriptionFromAncestors = function( - ancestorsArray, recursive, verbosity) { - if (typeof(recursive) === 'undefined') { - recursive = true; - } - var len = ancestorsArray.length; - var context = ''; - var text = ''; - var userValue = ''; - var annotation = ''; - var earcons = []; - var personality = null; - var hint = ''; - - if (len > 0) { - text = cvox.DomUtil.getName(ancestorsArray[len - 1], recursive); - - userValue = cvox.DomUtil.getValue(ancestorsArray[len - 1]); - } - for (var i = len - 1; i >= 0; i--) { - var node = ancestorsArray[i]; - - hint = cvox.DomUtil.getHint(node); - - // Don't speak dialogs here, they're spoken when events occur. - var role = node.getAttribute ? node.getAttribute('role') : null; - if (role == 'alertdialog') { - continue; - } - - var roleText = cvox.DomUtil.getRole(node, verbosity); - - // Use the ancestor closest to the target to be the personality. - if (!personality) { - personality = cvox.AuralStyleUtil.getStyleForNode(node); - } - // TODO(dtseng): Is this needed? - if (i < len - 1 && node.hasAttribute('role')) { - var name = cvox.DomUtil.getName(node, false); - if (name) { - roleText = name + ' ' + roleText; - } - } - if (roleText.length > 0) { - // Since we prioritize reading of context in reading order, only populate - // it for larger ancestry changes. - if (context.length > 0 || - (annotation.length > 0 && node.childElementCount > 1)) { - context = roleText + ' ' + cvox.DomUtil.getState(node, false) + - ' ' + context; - } else { - if (annotation.length > 0) { - annotation += - ' ' + roleText + ' ' + cvox.DomUtil.getState(node, true); - } else { - annotation = roleText + ' ' + cvox.DomUtil.getState(node, true); - } - } - } - var earcon = cvox.EarconUtil.getEarcon(node); - if (earcon != null && earcons.indexOf(earcon) == -1) { - earcons.push(earcon); - } - } - return new cvox.NavDescription({ - context: cvox.DomUtil.collapseWhitespace(context), - text: cvox.DomUtil.collapseWhitespace(text), - userValue: cvox.DomUtil.collapseWhitespace(userValue), - annotation: cvox.DomUtil.collapseWhitespace(annotation), - earcons: earcons, - personality: personality, - hint: cvox.DomUtil.collapseWhitespace(hint) - }); -}; - -/** - * Returns a description of a navigation from an array of changed - * ancestor nodes. The ancestors are in order from the highest in the - * tree to the lowest, i.e. ending with the current leaf node. - * - * @param {Node} prevNode The previous node in navigation. - * @param {Node} node The current node in navigation. - * @param {boolean} recursive Whether or not the element's subtree should - * be used; true by default. - * @param {number} verbosity The verbosity setting. - * @return {!Array<cvox.NavDescription>} The description of the navigation - * action. - */ -cvox.DescriptionUtil.getDescriptionFromNavigation = - function(prevNode, node, recursive, verbosity) { - if (!prevNode || !node) { - return []; - } - - // Specialized math descriptions. - if (cvox.DomUtil.isMath(node) && - !cvox.AriaUtil.isMath(node)) { - return cvox.DescriptionUtil.getMathDescription(node); - } - - // Next, check to see if the current node is a collection type. - if (cvox.DescriptionUtil.COLLECTION_NODE_TYPE[node.tagName]) { - return cvox.DescriptionUtil.getCollectionDescription( - /** @type {!cvox.CursorSelection} */( - cvox.CursorSelection.fromNode(prevNode)), - /** @type {!cvox.CursorSelection} */( - cvox.CursorSelection.fromNode(node))); - } - - // Now, generate a description for all other elements. - var ancestors = cvox.DomUtil.getUniqueAncestors(prevNode, node, true); - var desc = cvox.DescriptionUtil.getDescriptionFromAncestors( - ancestors, recursive, verbosity); - var prevAncestors = cvox.DomUtil.getUniqueAncestors(node, prevNode); - if (cvox.DescriptionUtil.shouldDescribeExit_(prevAncestors)) { - var prevDesc = cvox.DescriptionUtil.getDescriptionFromAncestors( - prevAncestors, recursive, verbosity); - if (prevDesc.context && !desc.context) { - desc.context = - Msgs.getMsg('exited_container', [prevDesc.context]); - } - } - return [desc]; -}; - - -/** - * Returns an array of NavDescriptions that includes everything that would be - * spoken by an object walker while traversing from prevSel to sel. - * It also includes any necessary annotations and context about the set of - * descriptions. This function is here because most (currently all) walkers - * that iterate over non-leaf nodes need this sort of description. - * This is an awkward design, and should be changed in the future. - * @param {!cvox.CursorSelection} prevSel The previous selection. - * @param {!cvox.CursorSelection} sel The selection. - * @return {!Array<!cvox.NavDescription>} The descriptions as described above. - */ -cvox.DescriptionUtil.getCollectionDescription = function(prevSel, sel) { - var descriptions = cvox.DescriptionUtil.getRawDescriptions_(prevSel, sel); - cvox.DescriptionUtil.insertCollectionDescription_(descriptions); - return descriptions; -}; - - -/** - * Used for getting collection descriptions. - * @type {!cvox.BareObjectWalker} - * @private - */ -cvox.DescriptionUtil.subWalker_ = new cvox.BareObjectWalker(); - - -/** - * Returns the descriptions that would be gotten by an object walker. - * @param {!cvox.CursorSelection} prevSel The previous selection. - * @param {!cvox.CursorSelection} sel The selection. - * @return {!Array<!cvox.NavDescription>} The descriptions. - * @private - */ -cvox.DescriptionUtil.getRawDescriptions_ = function(prevSel, sel) { - // Use a object walker in non-smart mode to traverse all of the - // nodes inside the current smart node and return their annotations. - var descriptions = []; - - // We want the descriptions to be in forward order whether or not the - // selection is reversed. - sel = sel.clone().setReversed(false); - var node = cvox.DescriptionUtil.subWalker_.sync(sel).start.node; - - var prevNode = prevSel.end.node; - var curSel = cvox.CursorSelection.fromNode(node); - - if (!curSel) { - return []; - } - - while (cvox.DomUtil.isDescendantOfNode(node, sel.start.node)) { - var ancestors = cvox.DomUtil.getUniqueAncestors(prevNode, node); - // Specialized math descriptions. - if (cvox.DomUtil.isMath(node) && - !cvox.AriaUtil.isMath(node)) { - descriptions = - descriptions.concat(cvox.DescriptionUtil.getMathDescription(node)); - } else { - var description = cvox.DescriptionUtil.getDescriptionFromAncestors( - ancestors, true, cvox.ChromeVox.verbosity); - descriptions.push(description); - } - curSel = cvox.DescriptionUtil.subWalker_.next(curSel); - if (!curSel) { - break; - } - - curSel = /** @type {!cvox.CursorSelection} */ (curSel); - prevNode = node; - node = curSel.start.node; - } - - return descriptions; -}; - -/** - * Returns the full descriptions of the child nodes that would be gotten by an - * object walker. - * @param {?Element} prevnode The previous element if there is one. - * @param {!Element} node The target element. - * @return {!Array<!cvox.NavDescription>} The descriptions. - */ -cvox.DescriptionUtil.getFullDescriptionsFromChildren = - function(prevnode, node) { - var descriptions = []; - if (!node) { - return descriptions; - } - var desc; - if (cvox.DomUtil.isLeafNode(node)) { - var ancestors; - if (prevnode) { - ancestors = cvox.DomUtil.getUniqueAncestors(prevnode, node); - } else { - ancestors = new Array(); - ancestors.push(node); - } - desc = cvox.DescriptionUtil.getDescriptionFromAncestors( - ancestors, true, cvox.ChromeVox.verbosity); - descriptions.push(desc); - return descriptions; - } - var originalNode = node; - var curSel = cvox.CursorSelection.fromNode(node); - if (!curSel) { - return descriptions; - } - node = cvox.DescriptionUtil.subWalker_.sync(curSel).start.node; - curSel = cvox.CursorSelection.fromNode(node); - if (!curSel) { - return descriptions; - } - while (cvox.DomUtil.isDescendantOfNode(node, originalNode)) { - descriptions = descriptions.concat( - cvox.DescriptionUtil.getFullDescriptionsFromChildren(prevnode, node)); - curSel = cvox.DescriptionUtil.subWalker_.next(curSel); - if (!curSel) { - break; - } - curSel = /** @type {!cvox.CursorSelection} */ (curSel); - prevnode = node; - node = curSel.start.node; - } - return descriptions; -}; - - -/** - * Modify the descriptions to say that it is a collection. - * @param {Array<cvox.NavDescription>} descriptions The descriptions. - * @private - */ -cvox.DescriptionUtil.insertCollectionDescription_ = function(descriptions) { - var annotations = cvox.DescriptionUtil.getAnnotations_(descriptions); - // If all of the items have the same annotation, describe it as a - // <annotation> collection with <n> items. Currently only enabled - // for links, but support should be added for any other type that - // makes sense. - if (descriptions.length >= 3 && - descriptions[0].context.length == 0 && - annotations.length == 1 && - annotations[0].length > 0 && - cvox.DescriptionUtil.isAnnotationCollection_(annotations[0])) { - var commonAnnotation = annotations[0]; - var firstContext = descriptions[0].context; - descriptions[0].context = ''; - for (var i = 0; i < descriptions.length; i++) { - descriptions[i].annotation = ''; - } - - descriptions.splice(0, 0, new cvox.NavDescription({ - context: firstContext, - text: '', - annotation: Msgs.getMsg( - 'collection', - [commonAnnotation, - Msgs.getNumber(descriptions.length)]) - })); - } -}; - - -/** - * Pulls the annotations from a description array. - * @param {Array<cvox.NavDescription>} descriptions The descriptions. - * @return {Array<string>} The annotations. - * @private - */ -cvox.DescriptionUtil.getAnnotations_ = function(descriptions) { - var annotations = []; - for (var i = 0; i < descriptions.length; ++i) { - var description = descriptions[i]; - if (annotations.indexOf(description.annotation) == -1) { - // If we have an Internal link collection, call it Link collection. - // NOTE(deboer): The message comparison is a symptom of a bad design. - // I suspect this code belongs elsewhere but I don't know where, yet. - var linkMsg = Msgs.getMsg('role_link'); - if (description.annotation.toLowerCase().indexOf(linkMsg.toLowerCase()) != - -1) { - if (annotations.indexOf(linkMsg) == -1) { - annotations.push(linkMsg); - } - } else { - annotations.push(description.annotation); - } - } - } - return annotations; -}; - - -/** - * Returns true if this annotation should be grouped as a collection, - * meaning that instead of repeating the annotation for each item, we - * just announce <annotation> collection with <n> items at the front. - * - * Currently enabled for links, but could be extended to support other - * roles that make sense. - * - * @param {string} annotation The annotation text. - * @return {boolean} If this annotation should be a collection. - * @private - */ -cvox.DescriptionUtil.isAnnotationCollection_ = function(annotation) { - return (annotation == Msgs.getMsg('role_link')); -}; - -/** - * Determines whether to describe the exit of an ancestor chain. - * @param {Array<Node>} ancestors The ancestors exited during navigation. - * @return {boolean} The result. - * @private - */ -cvox.DescriptionUtil.shouldDescribeExit_ = function(ancestors) { - return ancestors.some(function(node) { - switch (node.tagName) { - case 'TABLE': - case 'MATH': - return true; - } - return cvox.AriaUtil.isLandmark(node); - }); -}; - - -// TODO(sorge): Bad naming...this thing returns *multiple* descriptions. -/** - * Generates a description for a math node. - * @param {!Node} node The given node. - * @return {!Array<cvox.NavDescription>} A list of Navigation descriptions. - */ -cvox.DescriptionUtil.getMathDescription = function(node) { - // TODO (sorge) This function should evantually be removed. Descriptions - // should come directly from the speech rule engine, taking information on - // verbosity etc. into account. - var speechEngine = cvox.SpeechRuleEngine.getInstance(); - var traverse = cvox.TraverseMath.getInstance(); - speechEngine.parameterize(cvox.MathmlStore.getInstance()); - traverse.initialize(node); - var ret = speechEngine.evaluateNode(traverse.activeNode); - if (ret == []) { - return [new cvox.NavDescription({'text': 'empty math'})]; - } - if (cvox.ChromeVox.verbosity == cvox.VERBOSITY_VERBOSE) { - ret[ret.length - 1].annotation = 'math'; - } - ret[0].pushEarcon(cvox.Earcon.MATH); - return ret; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_predicates.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_predicates.js deleted file mode 100644 index 685a8ecf8f4..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_predicates.js +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A bunch of predicates that take as input an array of - * nodes with the unique ancestors of a node. They output true if a - * certain category of node has been found. - * - */ - -goog.provide('cvox.DomPredicates'); - - -/** - * Checkbox. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a checkbox. - */ -cvox.DomPredicates.checkboxPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && - nodes[i].getAttribute('role') == 'checkbox') || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'checkbox')) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Radio button. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a radio button. - */ -cvox.DomPredicates.radioPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'radio') || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'radio')) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Slider. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a slider. - */ -cvox.DomPredicates.sliderPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'slider') || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'range')) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Graphic. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a graphic. - */ -cvox.DomPredicates.graphicPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].tagName == 'IMG' || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'img')) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Button. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a button. - */ -cvox.DomPredicates.buttonPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'button') || - nodes[i].tagName == 'BUTTON' || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'submit') || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'button') || - (nodes[i].tagName == 'INPUT' && nodes[i].type == 'reset')) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Combo box. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a combo box. - */ -cvox.DomPredicates.comboBoxPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && - nodes[i].getAttribute('role') == 'combobox') || - nodes[i].tagName == 'SELECT') { - return nodes[i]; - } - } - return null; -}; - - -/** - * Editable text field. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is an editable text field. - */ -cvox.DomPredicates.editTextPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'textbox') || - nodes[i].tagName == 'TEXTAREA' || - nodes[i].isContentEditable || - (nodes[i].tagName == 'INPUT' && - cvox.DomUtil.isInputTypeText(nodes[i]))) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Heading. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading. - */ -cvox.DomPredicates.headingPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].getAttribute && - nodes[i].getAttribute('role') == 'heading') { - return nodes[i]; - } - switch (nodes[i].tagName) { - case 'H1': - case 'H2': - case 'H3': - case 'H4': - case 'H5': - case 'H6': - return nodes[i]; - } - } - return null; -}; - - -/** - * Heading level 1. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading level 1. - * TODO: handle ARIA headings with ARIA heading levels? - */ -cvox.DomPredicates.heading1Predicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'H1'); -}; - - -/** - * Heading level 2. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading level 2. - */ -cvox.DomPredicates.heading2Predicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'H2'); -}; - - -/** - * Heading level 3. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading level 3. - */ -cvox.DomPredicates.heading3Predicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'H3'); -}; - - -/** - * Heading level 4. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading level 4. - */ -cvox.DomPredicates.heading4Predicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'H4'); -}; - - -/** - * Heading level 5. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading level 5. - */ -cvox.DomPredicates.heading5Predicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'H5'); -}; - - -/** - * Heading level 6. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a heading level 6. - */ -cvox.DomPredicates.heading6Predicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'H6'); -}; - - -/** - * Link. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a link. - */ -cvox.DomPredicates.linkPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'link') || - (nodes[i].tagName == 'A' && nodes[i].href)) { - return nodes[i]; - } - } - return null; -}; - - -/** - * Table. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a data table. - */ -cvox.DomPredicates.tablePredicate = function(nodes) { - // TODO(stoarca): Captions should always be allowed!! - var node = cvox.DomUtil.findTableNodeInList(nodes, {allowCaptions: true}); - if (node && !cvox.DomUtil.isLayoutTable(node)) { - return node; - } else { - return null; - } -}; - -/** - * Table Cell. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a table cell. - */ -cvox.DomPredicates.cellPredicate = function(nodes) { - for (var i = nodes.length - 1; i >= 0; --i) { - var node = nodes[i]; - if (node.tagName == 'TD' || - node.tagName == 'TH' || - (node.getAttribute && node.getAttribute('role') == 'gridcell')) { - return node; - } - } - return null; -}; - - -/** - * Visited link. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a visited link. - */ -cvox.DomPredicates.visitedLinkPredicate = function(nodes) { - for (var i = nodes.length - 1; i >= 0; --i) { - if (cvox.DomPredicates.linkPredicate([nodes[i]]) && - cvox.ChromeVox.visitedUrls[nodes[i].href]) { - return nodes[i]; - } - } -}; - - -/** - * List. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a list. - */ -cvox.DomPredicates.listPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'list') || - nodes[i].tagName == 'UL' || - nodes[i].tagName == 'OL') { - return nodes[i]; - } - } - return null; -}; - - -/** - * List item. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a list item. - */ -cvox.DomPredicates.listItemPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && - nodes[i].getAttribute('role') == 'listitem') || - nodes[i].tagName == 'LI') { - return nodes[i]; - } - } - return null; -}; - - -/** - * Blockquote. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a blockquote. - */ -cvox.DomPredicates.blockquotePredicate = function(nodes) { - return cvox.DomPredicates.containsTagName_(nodes, 'BLOCKQUOTE'); -}; - - -/** - * Form field. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is any type of form field. - */ -cvox.DomPredicates.formFieldPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (cvox.DomUtil.isControl(nodes[i])) { - return nodes[i]; - } - } - return null; -}; - - -/** - * ARIA landmark. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is an ARIA landmark. - */ -cvox.DomPredicates.landmarkPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (cvox.AriaUtil.isLandmark(nodes[i])) { - return nodes[i]; - } - } - return null; -}; - - -/** - * @param {Array} arr Array of nodes. - * @param {string} tagName The name of the tag. - * @return {?Node} Node if obj is in the array. - * @private - */ -cvox.DomPredicates.containsTagName_ = function(arr, tagName) { - var i = arr.length; - while (i--) { - if (arr[i].tagName == tagName) { - return arr[i]; - } - } - return null; -}; - - -/** - * MathML expression - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a math expression. - */ -cvox.DomPredicates.mathPredicate = function(nodes) { - return cvox.DomUtil.findMathNodeInList(nodes); -}; - -/** - * SECTION: A section is anything that indicates a new section. This includes - * headings and landmarks. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is considered a section marker. - */ -cvox.DomPredicates.sectionPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (cvox.DomUtil.isSemanticElt(nodes[i])) { - return nodes[i]; - } - if (cvox.AriaUtil.isLandmark(nodes[i])) { - return nodes[i]; - } - if (nodes[i].getAttribute && - nodes[i].getAttribute('role') == 'heading') { - return nodes[i]; - } - switch (nodes[i].tagName) { - case 'H1': - case 'H2': - case 'H3': - case 'H4': - case 'H5': - case 'H6': - return nodes[i]; - } - } - return null; -}; - -/** - * CONTROL: A control is anything that the user can interact with. This includes - * form fields and links. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is considered a control. - */ -cvox.DomPredicates.controlPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (cvox.DomUtil.isControl(nodes[i])) { - return nodes[i]; - } - if ((nodes[i].getAttribute && nodes[i].getAttribute('role') == 'link') || - (nodes[i].tagName == 'A' && nodes[i].href)) { - return nodes[i]; - } - } - return null; -}; - -/** - * Caption. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a caption. - */ -cvox.DomPredicates.captionPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].tagName == 'CAPTION') { - return nodes[i]; - } - } - return null; -}; - -/** - * Article. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a article. - */ -cvox.DomPredicates.articlePredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if ((nodes[i].getAttribute && - nodes[i].getAttribute('role') == 'article') || - nodes[i].tagName == 'ARTICLE') { - return nodes[i]; - } - } - return null; -}; - -/** - * Media. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a media widget (video or audio). - */ -cvox.DomPredicates.mediaPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].tagName == 'AUDIO' || - nodes[i].tagName == 'VIDEO') { - return nodes[i]; - } - } - return null; -}; - - -/** - * Ordered List. - * @param {Array<Node>} nodes An array of nodes to check. - * @return {?Node} Node in the array that is a ordered list. - */ -cvox.DomPredicates.orderedListPredicate = function(nodes) { - for (var i = 0; i < nodes.length; i++) { - if (nodes[i].tagName == 'OL') { - return nodes[i]; - } - } - return null; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_util.js deleted file mode 100644 index cea00b5f612..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_util.js +++ /dev/null @@ -1,2608 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A collection of JavaScript utilities used to simplify working - * with the DOM. - */ - - -goog.provide('cvox.DomUtil'); - -goog.require('cvox.AbstractTts'); -goog.require('cvox.AriaUtil'); -goog.require('cvox.ChromeVox'); -goog.require('cvox.DomPredicates'); -goog.require('cvox.Memoize'); -goog.require('cvox.NodeState'); -goog.require('cvox.XpathUtil'); - - - -/** - * Create the namespace - * @constructor - */ -cvox.DomUtil = function() { -}; - - -/** - * Note: If you are adding a new mapping, the new message identifier needs a - * corresponding braille message. For example, a message id 'tag_button' - * requires another message 'tag_button_brl' within messages.js. - * @type {Object} - */ -cvox.DomUtil.INPUT_TYPE_TO_INFORMATION_TABLE_MSG = { - 'button' : 'role_button', - 'checkbox' : 'role_checkbox', - 'color' : 'input_type_color', - 'datetime' : 'input_type_datetime', - 'datetime-local' : 'input_type_datetime_local', - 'date' : 'input_type_date', - 'email' : 'input_type_email', - 'file' : 'input_type_file', - 'image' : 'role_button', - 'month' : 'input_type_month', - 'number' : 'input_type_number', - 'password' : 'input_type_password', - 'radio' : 'role_radio', - 'range' : 'role_slider', - 'reset' : 'input_type_reset', - 'search' : 'input_type_search', - 'submit' : 'role_button', - 'tel' : 'input_type_number', - 'text' : 'input_type_text', - 'url' : 'input_type_url', - 'week' : 'input_type_week' -}; - - -/** - * Note: If you are adding a new mapping, the new message identifier needs a - * corresponding braille message. For example, a message id 'tag_button' - * requires another message 'tag_button_brl' within messages.js. - * @type {Object} - */ -cvox.DomUtil.TAG_TO_INFORMATION_TABLE_VERBOSE_MSG = { - 'A' : 'role_link', - 'ARTICLE' : 'tag_article', - 'ASIDE' : 'tag_aside', - 'AUDIO' : 'tag_audio', - 'BUTTON' : 'role_button', - 'FOOTER' : 'tag_footer', - 'H1' : 'tag_h1', - 'H2' : 'tag_h2', - 'H3' : 'tag_h3', - 'H4' : 'tag_h4', - 'H5' : 'tag_h5', - 'H6' : 'tag_h6', - 'HEADER' : 'tag_header', - 'HGROUP' : 'tag_hgroup', - 'LI' : 'tag_li', - 'MARK' : 'tag_mark', - 'NAV' : 'tag_nav', - 'OL' : 'tag_ol', - 'SECTION' : 'tag_section', - 'SELECT' : 'tag_select', - 'TABLE' : 'tag_table', - 'TEXTAREA' : 'tag_textarea', - 'TIME' : 'tag_time', - 'UL' : 'tag_ul', - 'VIDEO' : 'tag_video' -}; - -/** - * ChromeVox does not speak the omitted tags. - * @type {Object} - */ -cvox.DomUtil.TAG_TO_INFORMATION_TABLE_BRIEF_MSG = { - 'AUDIO' : 'tag_audio', - 'BUTTON' : 'role_button', - 'SELECT' : 'tag_select', - 'TABLE' : 'tag_table', - 'TEXTAREA' : 'tag_textarea', - 'VIDEO' : 'tag_video' -}; - -/** - * These tags are treated as text formatters. - * @type {Array<string>} - */ -cvox.DomUtil.FORMATTING_TAGS = - ['B', 'BIG', 'CITE', 'CODE', 'DFN', 'EM', 'I', 'KBD', 'SAMP', 'SMALL', - 'SPAN', 'STRIKE', 'STRONG', 'SUB', 'SUP', 'U', 'VAR']; - -/** - * Determine if the given node is visible on the page. This does not check if - * it is inside the document view-port as some sites try to communicate with - * screen readers with such elements. - * @param {Node} node The node to determine as visible or not. - * @param {{checkAncestors: (boolean|undefined), - checkDescendants: (boolean|undefined)}=} opt_options - * In certain cases, we already have information - * on the context of the node. To improve performance and avoid redundant - * operations, you may wish to turn certain visibility checks off by - * passing in an options object. The following properties are configurable: - * checkAncestors: {boolean=} True if we should check the ancestor chain - * for forced invisibility traits of descendants. True by default. - * checkDescendants: {boolean=} True if we should consider descendants of - * the given node for visible elements. True by default. - * @return {boolean} True if the node is visible. - */ -cvox.DomUtil.isVisible = function(node, opt_options) { - var checkAncestors = true; - var checkDescendants = true; - if (opt_options) { - if (opt_options.checkAncestors !== undefined) { - checkAncestors = opt_options.checkAncestors; - } - if (opt_options.checkDescendants !== undefined) { - checkDescendants = opt_options.checkDescendants; - } - } - - // Generate a unique function name based on the arguments, and - // memoize the result of the internal visibility computation so that - // within the same call stack, we don't need to recompute the visibility - // of the same node. - var fname = 'isVisible-' + checkAncestors + '-' + checkDescendants; - return /** @type {boolean} */ (cvox.Memoize.memoize( - cvox.DomUtil.computeIsVisible_.bind( - this, node, checkAncestors, checkDescendants), fname, node)); -}; - -/** - * Implementation of |cvox.DomUtil.isVisible|. - * @param {Node} node The node to determine as visible or not. - * @param {boolean} checkAncestors True if we should check the ancestor chain - * for forced invisibility traits of descendants. - * @param {boolean} checkDescendants True if we should consider descendants of - * the given node for visible elements. - * @return {boolean} True if the node is visible. - * @private - */ -cvox.DomUtil.computeIsVisible_ = function( - node, checkAncestors, checkDescendants) { - // If the node is an iframe that we can never inject into, consider it hidden. - if (node.tagName == 'IFRAME' && !node.src) { - return false; - } - - // If the node is being forced visible by ARIA, ARIA wins. - if (cvox.AriaUtil.isForcedVisibleRecursive(node)) { - return true; - } - - // Confirm that no subtree containing node is invisible. - if (checkAncestors && - cvox.DomUtil.hasInvisibleAncestor_(node)) { - return false; - } - - // If the node's subtree has a visible node, we declare it as visible. - if (cvox.DomUtil.hasVisibleNodeSubtree_(node, checkDescendants)) { - return true; - } - - return false; -}; - - -/** - * Checks the ancestor chain for the given node for invisibility. If an - * ancestor is invisible and this cannot be overriden by a descendant, - * we return true. If the element is not a descendant of the document - * element it will return true (invisible). - * @param {Node} node The node to check the ancestor chain for. - * @return {boolean} True if a descendant is invisible. - * @private - */ -cvox.DomUtil.hasInvisibleAncestor_ = function(node) { - var ancestor = node; - while (ancestor = ancestor.parentElement) { - var style = document.defaultView.getComputedStyle(ancestor, null); - if (cvox.DomUtil.isInvisibleStyle(style, true)) { - return true; - } - // Once we reach the document element and we haven't found anything - // invisible yet, we're done. If we exit the while loop and never found - // the document element, the element wasn't part of the DOM and thus it's - // invisible. - if (ancestor == document.documentElement) { - return false; - } - } - return true; -}; - - -/** - * Checks for a visible node in the subtree defined by root. - * @param {Node} root The root of the subtree to check. - * @param {boolean} recursive Whether or not to check beyond the root of the - * subtree for visible nodes. This option exists for performance tuning. - * Sometimes we already have information about the descendants, and we do - * not need to check them again. - * @return {boolean} True if the subtree contains a visible node. - * @private - */ -cvox.DomUtil.hasVisibleNodeSubtree_ = function(root, recursive) { - if (!(root instanceof Element)) { - if (!root.parentElement) { - return false; - } - var parentStyle = document.defaultView - .getComputedStyle(root.parentElement, null); - var isVisibleParent = !cvox.DomUtil.isInvisibleStyle(parentStyle); - return isVisibleParent; - } - - var rootStyle = document.defaultView.getComputedStyle(root, null); - var isRootVisible = !cvox.DomUtil.isInvisibleStyle(rootStyle); - if (isRootVisible) { - return true; - } - var isSubtreeInvisible = cvox.DomUtil.isInvisibleStyle(rootStyle, true); - if (!recursive || isSubtreeInvisible) { - return false; - } - - // Carry on with a recursive check of the descendants. - var children = root.childNodes; - for (var i = 0; i < children.length; i++) { - var child = children[i]; - if (cvox.DomUtil.hasVisibleNodeSubtree_(child, recursive)) { - return true; - } - } - return false; -}; - - -/** - * Determines whether or a node is not visible according to any CSS criteria - * that can hide it. - * @param {CSSStyleDeclaration} style The style of the node to determine as - * invsible or not. - * @param {boolean=} opt_strict If set to true, we do not check the visibility - * style attribute. False by default. - * CAUTION: Checking the visibility style attribute can result in returning - * true (invisible) even when an element has have visible descendants. This - * is because an element with visibility:hidden can have descendants that - * are visible. - * @return {boolean} True if the node is invisible. - */ -cvox.DomUtil.isInvisibleStyle = function(style, opt_strict) { - if (!style) { - return false; - } - if (style.display == 'none') { - return true; - } - // Opacity values range from 0.0 (transparent) to 1.0 (fully opaque). - if (parseFloat(style.opacity) == 0) { - return true; - } - // Visibility style tests for non-strict checking. - if (!opt_strict && - (style.visibility == 'hidden' || style.visibility == 'collapse')) { - return true; - } - return false; -}; - - -/** - * Determines whether a control should be announced as disabled. - * - * @param {Node} node The node to be examined. - * @return {boolean} Whether or not the node is disabled. - */ -cvox.DomUtil.isDisabled = function(node) { - if (node.disabled) { - return true; - } - var ancestor = node; - while (ancestor = ancestor.parentElement) { - if (ancestor.tagName == 'FIELDSET' && ancestor.disabled) { - return true; - } - } - return false; -}; - - -/** - * Determines whether a node is an HTML5 semantic element - * - * @param {Node} node The node to be checked. - * @return {boolean} True if the node is an HTML5 semantic element. - */ -cvox.DomUtil.isSemanticElt = function(node) { - if (node.tagName) { - var tag = node.tagName; - if ((tag == 'SECTION') || (tag == 'NAV') || (tag == 'ARTICLE') || - (tag == 'ASIDE') || (tag == 'HGROUP') || (tag == 'HEADER') || - (tag == 'FOOTER') || (tag == 'TIME') || (tag == 'MARK')) { - return true; - } - } - return false; -}; - - -/** - * Determines whether or not a node is a leaf node. - * TODO (adu): This function is doing a lot more than just checking for the - * presence of descendants. We should be more precise in the documentation - * about what we mean by leaf node. - * - * @param {Node} node The node to be checked. - * @param {boolean=} opt_allowHidden Allows hidden nodes during descent. - * @return {boolean} True if the node is a leaf node. - */ -cvox.DomUtil.isLeafNode = function(node, opt_allowHidden) { - // If it's not an Element, then it's a leaf if it has no first child. - if (!(node instanceof Element)) { - return (node.firstChild == null); - } - - // Now we know for sure it's an element. - var element = /** @type {Element} */(node); - if (!opt_allowHidden && - !cvox.DomUtil.isVisible(element, {checkAncestors: false})) { - return true; - } - if (!opt_allowHidden && cvox.AriaUtil.isHidden(element)) { - return true; - } - if (cvox.AriaUtil.isLeafElement(element)) { - return true; - } - switch (element.tagName) { - case 'OBJECT': - case 'EMBED': - case 'VIDEO': - case 'AUDIO': - case 'IFRAME': - case 'FRAME': - return true; - } - - if (!!cvox.DomPredicates.linkPredicate([element])) { - return !cvox.DomUtil.findNode(element, function(node) { - return !!cvox.DomPredicates.headingPredicate([node]); - }); - } - if (cvox.DomUtil.isLeafLevelControl(element)) { - return true; - } - if (!element.firstChild) { - return true; - } - if (cvox.DomUtil.isMath(element)) { - return true; - } - if (cvox.DomPredicates.headingPredicate([element])) { - return !cvox.DomUtil.findNode(element, function(n) { - return !!cvox.DomPredicates.controlPredicate([n]); - }); - } - return false; -}; - - -/** - * Determines whether or not a node is or is the descendant of a node - * with a particular tag or class name. - * - * @param {Node} node The node to be checked. - * @param {?string} tagName The tag to check for, or null if the tag - * doesn't matter. - * @param {?string=} className The class to check for, or null if the class - * doesn't matter. - * @return {boolean} True if the node or one of its ancestor has the specified - * tag. - */ -cvox.DomUtil.isDescendantOf = function(node, tagName, className) { - while (node) { - - if (tagName && className && - (node.tagName && (node.tagName == tagName)) && - (node.className && (node.className == className))) { - return true; - } else if (tagName && !className && - (node.tagName && (node.tagName == tagName))) { - return true; - } else if (!tagName && className && - (node.className && (node.className == className))) { - return true; - } - node = node.parentNode; - } - return false; -}; - - -/** - * Determines whether or not a node is or is the descendant of another node. - * - * @param {Object} node The node to be checked. - * @param {Object} ancestor The node to see if it's a descendant of. - * @return {boolean} True if the node is ancestor or is a descendant of it. - */ -cvox.DomUtil.isDescendantOfNode = function(node, ancestor) { - while (node && ancestor) { - if (node.isSameNode(ancestor)) { - return true; - } - node = node.parentNode; - } - return false; -}; - - -/** - * Remove all whitespace from the beginning and end, and collapse all - * inner strings of whitespace to a single space. - * @param {string} str The input string. - * @return {string} The string with whitespace collapsed. - */ -cvox.DomUtil.collapseWhitespace = function(str) { - return str.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, ''); -}; - -/** - * Gets the base label of a node. I don't know exactly what this is. - * - * @param {Node} node The node to get the label from. - * @param {boolean=} recursive Whether or not the element's subtree - * should be used; true by default. - * @param {boolean=} includeControls Whether or not controls in the subtree - * should be included; true by default. - * @return {string} The base label of the node. - * @private - */ -cvox.DomUtil.getBaseLabel_ = function(node, recursive, includeControls) { - var label = ''; - if (node.hasAttribute) { - if (node.hasAttribute('aria-labelledby')) { - var labelNodeIds = node.getAttribute('aria-labelledby').split(' '); - for (var labelNodeId, i = 0; labelNodeId = labelNodeIds[i]; i++) { - var labelNode = document.getElementById(labelNodeId); - if (labelNode) { - label += ' ' + cvox.DomUtil.getName( - labelNode, true, includeControls, true); - } - } - } else if (node.hasAttribute('aria-label')) { - label = node.getAttribute('aria-label'); - } else if (node.constructor == HTMLImageElement) { - label = cvox.DomUtil.getImageTitle(node); - } else if (node.tagName == 'FIELDSET') { - // Other labels will trump fieldset legend with this implementation. - // Depending on how this works out on the web, we may later switch this - // to appending the fieldset legend to any existing label. - var legends = node.getElementsByTagName('LEGEND'); - label = ''; - for (var legend, i = 0; legend = legends[i]; i++) { - label += ' ' + cvox.DomUtil.getName(legend, true, includeControls); - } - } - - if (label.length == 0 && node && node.id) { - var labelFor = document.querySelector('label[for="' + node.id + '"]'); - if (labelFor) { - label = cvox.DomUtil.getName(labelFor, recursive, includeControls); - } - } - } - return cvox.DomUtil.collapseWhitespace(label); -}; - -/** - * Gets the nearest label in the ancestor chain, if one exists. - * @param {Node} node The node to start from. - * @return {string} The label. - * @private - */ -cvox.DomUtil.getNearestAncestorLabel_ = function(node) { - var label = ''; - var enclosingLabel = node; - while (enclosingLabel && enclosingLabel.tagName != 'LABEL') { - enclosingLabel = enclosingLabel.parentElement; - } - if (enclosingLabel && !enclosingLabel.hasAttribute('for')) { - // Get all text from the label but don't include any controls. - label = cvox.DomUtil.getName(enclosingLabel, true, false); - } - return label; -}; - - -/** - * Gets the name for an input element. - * @param {Node} node The node. - * @return {string} The name. - * @private - */ -cvox.DomUtil.getInputName_ = function(node) { - var label = ''; - if (node.type == 'image') { - label = cvox.DomUtil.getImageTitle(node); - } else if (node.type == 'submit') { - if (node.hasAttribute('value')) { - label = node.getAttribute('value'); - } else { - label = 'Submit'; - } - } else if (node.type == 'reset') { - if (node.hasAttribute('value')) { - label = node.getAttribute('value'); - } else { - label = 'Reset'; - } - } else if (node.type == 'button') { - if (node.hasAttribute('value')) { - label = node.getAttribute('value'); - } - } - return label; -}; - -/** - * Wraps getName_ with marking and unmarking nodes so that infinite loops - * don't occur. This is the ugly way to solve this; getName should not ever - * do a recursive call somewhere above it in the tree. - * @param {Node} node See getName_. - * @param {boolean=} recursive See getName_. - * @param {boolean=} includeControls See getName_. - * @param {boolean=} opt_allowHidden Allows hidden nodes in name computation. - * @return {string} See getName_. - */ -cvox.DomUtil.getName = function( - node, recursive, includeControls, opt_allowHidden) { - if (!node || node.cvoxGetNameMarked == true) { - return ''; - } - node.cvoxGetNameMarked = true; - var ret = - cvox.DomUtil.getName_(node, recursive, includeControls, opt_allowHidden); - node.cvoxGetNameMarked = false; - var prefix = cvox.DomUtil.getPrefixText(node); - return prefix + ret; -}; - -// TODO(dtseng): Seems like this list should be longer... -/** - * Determines if a node has a name obtained from concatinating the names of its - * children. - * @param {!Node} node The node under consideration. - * @param {boolean=} opt_allowHidden Allows hidden nodes in name computation. - * @return {boolean} True if node has name based on children. - * @private - */ -cvox.DomUtil.hasChildrenBasedName_ = function(node, opt_allowHidden) { - if (!!cvox.DomPredicates.linkPredicate([node]) || - !!cvox.DomPredicates.headingPredicate([node]) || - node.tagName == 'BUTTON' || - cvox.AriaUtil.isControlWidget(node) || - !cvox.DomUtil.isLeafNode(node, opt_allowHidden)) { - return true; - } else { - return false; - } -}; - -/** - * Get the name of a node: this includes all static text content and any - * HTML-author-specified label, title, alt text, aria-label, etc. - but - * does not include: - * - the user-generated control value (use getValue) - * - the current state (use getState) - * - the role (use getRole) - * - * Order of precedence: - * Text content if it's a text node. - * aria-labelledby - * aria-label - * alt (for an image) - * title - * label (for a control) - * placeholder (for an input element) - * recursive calls to getName on all children - * - * @param {Node} node The node to get the name from. - * @param {boolean=} recursive Whether or not the element's subtree should - * be used; true by default. - * @param {boolean=} includeControls Whether or not controls in the subtree - * should be included; true by default. - * @param {boolean=} opt_allowHidden Allows hidden nodes in name computation. - * @return {string} The name of the node. - * @private - */ -cvox.DomUtil.getName_ = function( - node, recursive, includeControls, opt_allowHidden) { - if (typeof(recursive) === 'undefined') { - recursive = true; - } - if (typeof(includeControls) === 'undefined') { - includeControls = true; - } - - if (node.constructor == Text) { - return node.data; - } - - var label = cvox.DomUtil.getBaseLabel_(node, recursive, includeControls); - - if (label.length == 0 && cvox.DomUtil.isControl(node)) { - label = cvox.DomUtil.getNearestAncestorLabel_(node); - } - - if (label.length == 0 && node.constructor == HTMLInputElement) { - label = cvox.DomUtil.getInputName_(node); - } - - if (cvox.DomUtil.isInputTypeText(node) && node.hasAttribute('placeholder')) { - var placeholder = node.getAttribute('placeholder'); - if (label.length > 0) { - if (cvox.DomUtil.getValue(node).length > 0) { - return label; - } else { - return label + ' with hint ' + placeholder; - } - } else { - return placeholder; - } - } - - if (label.length > 0) { - return label; - } - - // Fall back to naming via title only if there is no text content. - if (cvox.DomUtil.collapseWhitespace(node.textContent).length == 0 && - node.hasAttribute && - node.hasAttribute('title')) { - return node.getAttribute('title'); - } - - if (!recursive) { - return ''; - } - - if (cvox.AriaUtil.isCompositeControl(node)) { - return ''; - } - if (cvox.DomUtil.hasChildrenBasedName_(node, opt_allowHidden)) { - return cvox.DomUtil.getNameFromChildren( - node, includeControls, opt_allowHidden); - } - return ''; -}; - - -/** - * Get the name from the children of a node, not including the node itself. - * - * @param {Node} node The node to get the name from. - * @param {boolean=} includeControls Whether or not controls in the subtree - * should be included; true by default. - * @param {boolean=} opt_allowHidden Allow hidden nodes in name computation. - * @return {string} The concatenated text of all child nodes. - */ -cvox.DomUtil.getNameFromChildren = function( - node, includeControls, opt_allowHidden) { - if (includeControls == undefined) { - includeControls = true; - } - var name = ''; - var delimiter = ''; - for (var i = 0; i < node.childNodes.length; i++) { - var child = node.childNodes[i]; - var prevChild = node.childNodes[i - 1] || child; - if (!includeControls && cvox.DomUtil.isControl(child)) { - continue; - } - var isVisible = cvox.DomUtil.isVisible(child, {checkAncestors: false}); - if (opt_allowHidden || (isVisible && !cvox.AriaUtil.isHidden(child))) { - delimiter = (prevChild.tagName == 'SPAN' || - child.tagName == 'SPAN' || - child.parentNode.tagName == 'SPAN') ? - '' : ' '; - name += delimiter + cvox.DomUtil.getName(child, true, includeControls); - } - } - - return name; -}; - -/** - * Get any prefix text for the given node. - * This includes list style text for the leftmost leaf node under a listitem. - * @param {Node} node Compute prefix for this node. - * @param {number=} opt_index Starting offset into the given node's text. - * @return {string} Prefix text, if any. - */ -cvox.DomUtil.getPrefixText = function(node, opt_index) { - opt_index = opt_index || 0; - - // Generate list style text. - var ancestors = cvox.DomUtil.getAncestors(node); - var prefix = ''; - var firstListitem = cvox.DomPredicates.listItemPredicate(ancestors); - - var leftmost = firstListitem; - while (leftmost && leftmost.firstChild) { - leftmost = leftmost.firstChild; - } - - // Do nothing if we're not at the leftmost leaf. - if (firstListitem && - firstListitem.parentNode && - opt_index == 0 && - firstListitem.parentNode.tagName == 'OL' && - node == leftmost && - document.defaultView.getComputedStyle(firstListitem.parentNode) - .listStyleType != 'none') { - var items = cvox.DomUtil.toArray(firstListitem.parentNode.children).filter( - function(li) { return li.tagName == 'LI'; }); - var position = items.indexOf(firstListitem) + 1; - // TODO(dtseng): Support all list style types. - if (document.defaultView.getComputedStyle( - firstListitem.parentNode).listStyleType.indexOf('latin') != -1) { - position--; - prefix = String.fromCharCode('A'.charCodeAt(0) + position % 26); - } else { - prefix = position; - } - prefix += '. '; - } - return prefix; -}; - - -/** - * Use heuristics to guess at the label of a control, to be used if one - * is not explicitly set in the DOM. This is useful when a control - * field gets focus, but probably not useful when browsing the page - * element at a time. - * @param {Node} node The node to get the label from. - * @return {string} The name of the control, using heuristics. - */ -cvox.DomUtil.getControlLabelHeuristics = function(node) { - // If the node explicitly has aria-label or title set to '', - // treat it the same way as alt='' and do not guess - just assume - // the web developer knew what they were doing and wanted - // no title/label for that control. - if (node.hasAttribute && - ((node.hasAttribute('aria-label') && - (node.getAttribute('aria-label') == '')) || - (node.hasAttribute('aria-title') && - (node.getAttribute('aria-title') == '')))) { - return ''; - } - - // TODO (clchen, rshearer): Implement heuristics for getting the label - // information from the table headers once the code for getting table - // headers quickly is implemented. - - // If no description has been found yet and heuristics are enabled, - // then try getting the content from the closest node. - var prevNode = cvox.DomUtil.previousLeafNode(node); - var prevTraversalCount = 0; - while (prevNode && (!cvox.DomUtil.hasContent(prevNode) || - cvox.DomUtil.isControl(prevNode))) { - prevNode = cvox.DomUtil.previousLeafNode(prevNode); - prevTraversalCount++; - } - var nextNode = cvox.DomUtil.directedNextLeafNode(node); - var nextTraversalCount = 0; - while (nextNode && (!cvox.DomUtil.hasContent(nextNode) || - cvox.DomUtil.isControl(nextNode))) { - nextNode = cvox.DomUtil.directedNextLeafNode(nextNode); - nextTraversalCount++; - } - var guessedLabelNode; - if (prevNode && nextNode) { - var parentNode = node; - // Count the number of parent nodes until there is a shared parent; the - // label is most likely in the same branch of the DOM as the control. - // TODO (chaitanyag): Try to generalize this algorithm and move it to - // its own function in DOM Utils. - var prevCount = 0; - while (parentNode) { - if (cvox.DomUtil.isDescendantOfNode(prevNode, parentNode)) { - break; - } - parentNode = parentNode.parentNode; - prevCount++; - } - parentNode = node; - var nextCount = 0; - while (parentNode) { - if (cvox.DomUtil.isDescendantOfNode(nextNode, parentNode)) { - break; - } - parentNode = parentNode.parentNode; - nextCount++; - } - guessedLabelNode = nextCount < prevCount ? nextNode : prevNode; - } else { - guessedLabelNode = prevNode || nextNode; - } - if (guessedLabelNode) { - return cvox.DomUtil.collapseWhitespace( - cvox.DomUtil.getValue(guessedLabelNode) + ' ' + - cvox.DomUtil.getName(guessedLabelNode)); - } - - return ''; -}; - - -/** - * Get the text value of a node: the selected value of a select control or the - * current text of a text control. Does not return the state of a checkbox - * or radio button. - * - * Not recursive. - * - * @param {Node} node The node to get the value from. - * @return {string} The value of the node. - */ -cvox.DomUtil.getValue = function(node) { - var activeDescendant = cvox.AriaUtil.getActiveDescendant(node); - if (activeDescendant) { - return cvox.DomUtil.collapseWhitespace( - cvox.DomUtil.getValue(activeDescendant) + ' ' + - cvox.DomUtil.getName(activeDescendant)); - } - - if (node.constructor == HTMLSelectElement) { - node = /** @type {HTMLSelectElement} */(node); - var value = ''; - var start = node.selectedOptions ? node.selectedOptions[0] : null; - var end = node.selectedOptions ? - node.selectedOptions[node.selectedOptions.length - 1] : null; - // TODO(dtseng): Keeping this stateless means we describe the start and end - // of the selection only since we don't know which was added or - // removed. Once we keep the previous selection, we can read the diff. - if (start && end && start != end) { - value = Msgs.getMsg( - 'selected_options_value', [start.text, end.text]); - } else if (start) { - value = start.text + ''; - } - return value; - } - - if (node.constructor == HTMLTextAreaElement) { - return node.value; - } - - if (node.constructor == HTMLInputElement) { - switch (node.type) { - // Returning '' for inputs that are covered by getName. - case 'hidden': - case 'image': - case 'submit': - case 'reset': - case 'button': - case 'checkbox': - case 'radio': - return ''; - case 'password': - return node.value.replace(/./g, 'dot '); - default: - return node.value; - } - } - - if (node.isContentEditable) { - return cvox.DomUtil.getNameFromChildren(node, true); - } - - return ''; -}; - - -/** - * Given an image node, return its title as a string. The preferred title - * is always the alt text, and if that's not available, then the title - * attribute. If neither of those are available, it attempts to construct - * a title from the filename, and if all else fails returns the word Image. - * @param {Node} node The image node. - * @return {string} The title of the image. - */ -cvox.DomUtil.getImageTitle = function(node) { - var text; - if (node.hasAttribute('alt')) { - text = node.alt; - } else if (node.hasAttribute('title')) { - text = node.title; - } else { - var url = node.src; - if (url.substring(0, 4) != 'data') { - var filename = url.substring( - url.lastIndexOf('/') + 1, url.lastIndexOf('.')); - - // Hack to not speak the filename if it's ridiculously long. - if (filename.length >= 1 && filename.length <= 16) { - text = filename + ' Image'; - } else { - text = 'Image'; - } - } else { - text = 'Image'; - } - } - return text; -}; - - -/** - * Search the whole page for any aria-labelledby attributes and collect - * the complete set of ids they map to, so that we can skip elements that - * just label other elements and not double-speak them. We cache this - * result and then throw it away at the next event loop. - * @return {Object<boolean>} Set of all ids that are mapped by aria-labelledby. - */ -cvox.DomUtil.getLabelledByTargets = function() { - if (cvox.labelledByTargets) { - return cvox.labelledByTargets; - } - - // Start by getting all elements with - // aria-labelledby on the page since that's probably a short list, - // then see if any of those ids overlap with an id in this element's - // ancestor chain. - var labelledByElements = document.querySelectorAll('[aria-labelledby]'); - var labelledByTargets = {}; - for (var i = 0; i < labelledByElements.length; ++i) { - var element = labelledByElements[i]; - var attrValue = element.getAttribute('aria-labelledby'); - var ids = attrValue.split(/ +/); - for (var j = 0; j < ids.length; j++) { - labelledByTargets[ids[j]] = true; - } - } - cvox.labelledByTargets = labelledByTargets; - - window.setTimeout(function() { - cvox.labelledByTargets = null; - }, 0); - - return labelledByTargets; -}; - - -/** - * Determines whether or not a node has content. - * - * @param {Node} node The node to be checked. - * @return {boolean} True if the node has content. - */ -cvox.DomUtil.hasContent = function(node) { - // Memoize the result of the internal content computation so that - // within the same call stack, we don't need to redo the computation - // on the same node twice. - return /** @type {boolean} */ (cvox.Memoize.memoize( - cvox.DomUtil.computeHasContent_.bind(this), 'hasContent', node)); -}; - -/** - * Internal implementation of |cvox.DomUtil.hasContent|. - * - * @param {Node} node The node to be checked. - * @return {boolean} True if the node has content. - * @private - */ -cvox.DomUtil.computeHasContent_ = function(node) { - // nodeType:8 == COMMENT_NODE - if (node.nodeType == 8) { - return false; - } - - // Exclude anything in the head - if (cvox.DomUtil.isDescendantOf(node, 'HEAD')) { - return false; - } - - // Exclude script nodes - if (cvox.DomUtil.isDescendantOf(node, 'SCRIPT')) { - return false; - } - - // Exclude noscript nodes - if (cvox.DomUtil.isDescendantOf(node, 'NOSCRIPT')) { - return false; - } - - // Exclude noembed nodes since NOEMBED is deprecated. We treat - // noembed as having not content rather than try to get its content since - // Chrome will return raw HTML content rather than a valid DOM subtree. - if (cvox.DomUtil.isDescendantOf(node, 'NOEMBED')) { - return false; - } - - // Exclude style nodes that have been dumped into the body. - if (cvox.DomUtil.isDescendantOf(node, 'STYLE')) { - return false; - } - - // Check the style to exclude undisplayed/hidden nodes. - if (!cvox.DomUtil.isVisible(node)) { - return false; - } - - // Ignore anything that is hidden by ARIA. - if (cvox.AriaUtil.isHidden(node)) { - return false; - } - - // We need to speak controls, including those with no value entered. We - // therefore treat visible controls as if they had content, and return true - // below. - if (cvox.DomUtil.isControl(node)) { - return true; - } - - // Videos are always considered to have content so that we can navigate to - // and use the controls of the video widget. - if (cvox.DomUtil.isDescendantOf(node, 'VIDEO')) { - return true; - } - // Audio elements are always considered to have content so that we can - // navigate to and use the controls of the audio widget. - if (cvox.DomUtil.isDescendantOf(node, 'AUDIO')) { - return true; - } - - // We want to try to jump into an iframe iff it has a src attribute. - // For right now, we will avoid iframes without any content in their src since - // ChromeVox is not being injected in those cases and will cause the user to - // get stuck. - // TODO (clchen, dmazzoni): Manually inject ChromeVox for iframes without src. - if ((node.tagName == 'IFRAME') && (node.src) && - (node.src.indexOf('javascript:') != 0)) { - return true; - } - - var controlQuery = 'button,input,select,textarea'; - - // Skip any non-control content inside of a label if the label is - // correctly associated with a control, the label text will get spoken - // when the control is reached. - var enclosingLabel = node.parentElement; - while (enclosingLabel && enclosingLabel.tagName != 'LABEL') { - enclosingLabel = enclosingLabel.parentElement; - } - if (enclosingLabel) { - var embeddedControl = enclosingLabel.querySelector(controlQuery); - if (enclosingLabel.hasAttribute('for')) { - var targetId = enclosingLabel.getAttribute('for'); - var targetNode = document.getElementById(targetId); - if (targetNode && - cvox.DomUtil.isControl(targetNode) && - !embeddedControl) { - return false; - } - } else if (embeddedControl) { - return false; - } - } - - // Skip any non-control content inside of a legend if the legend is correctly - // nested within a fieldset. The legend text will get spoken when the fieldset - // is reached. - var enclosingLegend = node.parentElement; - while (enclosingLegend && enclosingLegend.tagName != 'LEGEND') { - enclosingLegend = enclosingLegend.parentElement; - } - if (enclosingLegend) { - var legendAncestor = enclosingLegend.parentElement; - while (legendAncestor && legendAncestor.tagName != 'FIELDSET') { - legendAncestor = legendAncestor.parentElement; - } - var embeddedControl = - legendAncestor && legendAncestor.querySelector(controlQuery); - if (legendAncestor && !embeddedControl) { - return false; - } - } - - if (!!cvox.DomPredicates.linkPredicate([node])) { - return true; - } - - // At this point, any non-layout tables are considered to have content. - // For layout tables, it is safe to consider them as without content since the - // sync operation would select a descendant of a layout table if possible. The - // only instance where |hasContent| gets called on a layout table is if no - // descendants have content (see |AbstractNodeWalker.next|). - if (node.tagName == 'TABLE' && !cvox.DomUtil.isLayoutTable(node)) { - return true; - } - - // Math is always considered to have content. - if (cvox.DomUtil.isMath(node)) { - return true; - } - - if (cvox.DomPredicates.headingPredicate([node])) { - return true; - } - - if (cvox.DomUtil.isFocusable(node)) { - return true; - } - - // Skip anything referenced by another element on the page - // via aria-labelledby. - var labelledByTargets = cvox.DomUtil.getLabelledByTargets(); - var enclosingNodeWithId = node; - while (enclosingNodeWithId) { - if (enclosingNodeWithId.id && - labelledByTargets[enclosingNodeWithId.id]) { - // If we got here, some element on this page has an aria-labelledby - // attribute listing this node as its id. As long as that "some" element - // is not this element, we should return false, indicating this element - // should be skipped. - var attrValue = enclosingNodeWithId.getAttribute('aria-labelledby'); - if (attrValue) { - var ids = attrValue.split(/ +/); - if (ids.indexOf(enclosingNodeWithId.id) == -1) { - return false; - } - } else { - return false; - } - } - enclosingNodeWithId = enclosingNodeWithId.parentElement; - } - - var text = cvox.DomUtil.getValue(node) + ' ' + cvox.DomUtil.getName(node); - var state = cvox.DomUtil.getState(node, true); - if (text.match(/^\s+$/) && state === '') { - // Text only contains whitespace - return false; - } - - return true; -}; - - -/** - * Returns a list of all the ancestors of a given node. The last element - * is the current node. - * - * @param {Node} targetNode The node to get ancestors for. - * @return {Array<Node>} An array of ancestors for the targetNode. - */ -cvox.DomUtil.getAncestors = function(targetNode) { - var ancestors = new Array(); - while (targetNode) { - ancestors.push(targetNode); - targetNode = targetNode.parentNode; - } - ancestors.reverse(); - while (ancestors.length && !ancestors[0].tagName && !ancestors[0].nodeValue) { - ancestors.shift(); - } - return ancestors; -}; - - -/** - * Compares Ancestors of A with Ancestors of B and returns - * the index value in B at which B diverges from A. - * If there is no divergence, the result will be -1. - * Note that if B is the same as A except B has more nodes - * even after A has ended, that is considered a divergence. - * The first node that B has which A does not have will - * be treated as the divergence point. - * - * @param {Object} ancestorsA The array of ancestors for Node A. - * @param {Object} ancestorsB The array of ancestors for Node B. - * @return {number} The index of the divergence point (the first node that B has - * which A does not have in B's list of ancestors). - */ -cvox.DomUtil.compareAncestors = function(ancestorsA, ancestorsB) { - var i = 0; - while (ancestorsA[i] && ancestorsB[i] && (ancestorsA[i] == ancestorsB[i])) { - i++; - } - if (!ancestorsA[i] && !ancestorsB[i]) { - i = -1; - } - return i; -}; - - -/** - * Returns an array of ancestors that are unique for the currentNode when - * compared to the previousNode. Having such an array is useful in generating - * the node information (identifying when interesting node boundaries have been - * crossed, etc.). - * - * @param {Node} previousNode The previous node. - * @param {Node} currentNode The current node. - * @param {boolean=} opt_fallback True returns node's ancestors in the case - * where node's ancestors is a subset of previousNode's ancestors. - * @return {Array<Node>} An array of unique ancestors for the current node - * (inclusive). - */ -cvox.DomUtil.getUniqueAncestors = function( - previousNode, currentNode, opt_fallback) { - var prevAncestors = cvox.DomUtil.getAncestors(previousNode); - var currentAncestors = cvox.DomUtil.getAncestors(currentNode); - var divergence = cvox.DomUtil.compareAncestors(prevAncestors, - currentAncestors); - var diff = currentAncestors.slice(divergence); - return (diff.length == 0 && opt_fallback) ? currentAncestors : diff; -}; - - -/** - * Returns a role message identifier for a node. - * For a localized string, see cvox.DomUtil.getRole. - * @param {Node} targetNode The node to get the role name for. - * @param {number} verbosity The verbosity setting to use. - * @return {string} The role message identifier for the targetNode. - */ -cvox.DomUtil.getRoleMsg = function(targetNode, verbosity) { - var info; - info = cvox.AriaUtil.getRoleNameMsg(targetNode); - if (!info) { - if (targetNode.tagName == 'INPUT') { - info = cvox.DomUtil.INPUT_TYPE_TO_INFORMATION_TABLE_MSG[targetNode.type]; - } else if (targetNode.tagName == 'A' && - cvox.DomUtil.isInternalLink(targetNode)) { - info = 'internal_link'; - } else if (targetNode.tagName == 'A' && - targetNode.getAttribute('href') && - cvox.ChromeVox.visitedUrls[targetNode.href]) { - info = 'visited_link'; - } else if (targetNode.tagName == 'A' && - targetNode.getAttribute('name')) { - info = ''; // Don't want to add any role to anchors. - } else if (targetNode.isContentEditable) { - info = 'input_type_text'; - } else if (cvox.DomUtil.isMath(targetNode)) { - info = 'math_expr'; - } else if (targetNode.tagName == 'TABLE' && - cvox.DomUtil.isLayoutTable(targetNode)) { - info = ''; - } else { - if (verbosity == cvox.VERBOSITY_BRIEF) { - info = - cvox.DomUtil.TAG_TO_INFORMATION_TABLE_BRIEF_MSG[targetNode.tagName]; - } else { - info = cvox.DomUtil.TAG_TO_INFORMATION_TABLE_VERBOSE_MSG[ - targetNode.tagName]; - - if (cvox.DomUtil.hasLongDesc(targetNode)) { - info = 'image_with_long_desc'; - } - - if (!info && targetNode.onclick) { - info = 'clickable'; - } - } - } - } - - return info; -}; - - -/** - * Returns a string to be presented to the user that identifies what the - * targetNode's role is. - * ARIA roles are given priority; if there is no ARIA role set, the role - * will be determined by the HTML tag for the node. - * - * @param {Node} targetNode The node to get the role name for. - * @param {number} verbosity The verbosity setting to use. - * @return {string} The role name for the targetNode. - */ -cvox.DomUtil.getRole = function(targetNode, verbosity) { - var roleMsg = cvox.DomUtil.getRoleMsg(targetNode, verbosity) || ''; - var role = roleMsg && roleMsg != ' ' ? - Msgs.getMsg(roleMsg) : ''; - return role ? role : roleMsg; -}; - - -/** - * Count the number of items in a list node. - * - * @param {Node} targetNode The list node. - * @return {number} The number of items in the list. - */ -cvox.DomUtil.getListLength = function(targetNode) { - var count = 0; - for (var node = targetNode.firstChild; - node; - node = node.nextSibling) { - if (cvox.DomUtil.isVisible(node) && - (node.tagName == 'LI' || - (node.getAttribute && node.getAttribute('role') == 'listitem'))) { - if (node.hasAttribute('aria-setsize')) { - var ariaLength = parseInt(node.getAttribute('aria-setsize'), 10); - if (!isNaN(ariaLength)) { - return ariaLength; - } - } - count++; - } - } - return count; -}; - - -/** - * Returns a NodeState that gives information about the state of the targetNode. - * - * @param {Node} targetNode The node to get the state information for. - * @param {boolean} primary Whether this is the primary node we're - * interested in, where we might want extra information - as - * opposed to an ancestor, where we might be more brief. - * @return {cvox.NodeState} The status information about the node. - */ -cvox.DomUtil.getStateMsgs = function(targetNode, primary) { - var activeDescendant = cvox.AriaUtil.getActiveDescendant(targetNode); - if (activeDescendant) { - return cvox.DomUtil.getStateMsgs(activeDescendant, primary); - } - var info = []; - var role = targetNode.getAttribute ? targetNode.getAttribute('role') : ''; - info = cvox.AriaUtil.getStateMsgs(targetNode, primary); - if (!info) { - info = []; - } - - if (targetNode.tagName == 'INPUT') { - if (!targetNode.hasAttribute('aria-checked')) { - var INPUT_MSGS = { - 'checkbox-true': 'checkbox_checked_state', - 'checkbox-false': 'checkbox_unchecked_state', - 'radio-true': 'radio_selected_state', - 'radio-false': 'radio_unselected_state' }; - var msgId = INPUT_MSGS[targetNode.type + '-' + !!targetNode.checked]; - if (msgId) { - info.push([msgId]); - } - } - } else if (targetNode.tagName == 'SELECT') { - if (targetNode.selectedOptions && targetNode.selectedOptions.length <= 1) { - info.push(['list_position', - Msgs.getNumber(targetNode.selectedIndex + 1), - Msgs.getNumber(targetNode.options.length)]); - } else { - info.push(['selected_options_state', - Msgs.getNumber(targetNode.selectedOptions.length)]); - } - } else if (targetNode.tagName == 'UL' || - targetNode.tagName == 'OL' || - role == 'list') { - info.push(['list_with_items_not_pluralized', - Msgs.getNumber( - cvox.DomUtil.getListLength(targetNode))]); - } - - if (cvox.DomUtil.isDisabled(targetNode)) { - info.push(['aria_disabled_true']); - } - - if (targetNode.accessKey) { - info.push(['access_key', targetNode.accessKey]); - } - - return info; -}; - - -/** - * Returns a string that gives information about the state of the targetNode. - * - * @param {Node} targetNode The node to get the state information for. - * @param {boolean} primary Whether this is the primary node we're - * interested in, where we might want extra information - as - * opposed to an ancestor, where we might be more brief. - * @return {string} The status information about the node. - */ -cvox.DomUtil.getState = function(targetNode, primary) { - return cvox.NodeStateUtil.expand( - cvox.DomUtil.getStateMsgs(targetNode, primary)); -}; - - -/** - * Return whether a node is focusable. This includes nodes whose tabindex - * attribute is set to "-1" explicitly - these nodes are not in the tab - * order, but they should still be focused if the user navigates to them - * using linear or smart DOM navigation. - * - * Note that when the tabIndex property of an Element is -1, that doesn't - * tell us whether the tabIndex attribute is missing or set to "-1" explicitly, - * so we have to check the attribute. - * - * @param {Object} targetNode The node to check if it's focusable. - * @return {boolean} True if the node is focusable. - */ -cvox.DomUtil.isFocusable = function(targetNode) { - if (!targetNode || typeof(targetNode.tabIndex) != 'number') { - return false; - } - - // Workaround for http://code.google.com/p/chromium/issues/detail?id=153904 - if ((targetNode.tagName == 'A') && !targetNode.hasAttribute('href') && - !targetNode.hasAttribute('tabindex')) { - return false; - } - - if (targetNode.tabIndex >= 0) { - return true; - } - - if (targetNode.hasAttribute && - targetNode.hasAttribute('tabindex') && - targetNode.getAttribute('tabindex') == '-1') { - return true; - } - - return false; -}; - - -/** - * Find a focusable descendant of a given node. This includes nodes whose - * tabindex attribute is set to "-1" explicitly - these nodes are not in the - * tab order, but they should still be focused if the user navigates to them - * using linear or smart DOM navigation. - * - * @param {Node} targetNode The node whose descendants to check if focusable. - * @return {Node} The focusable descendant node. Null if no descendant node - * was found. - */ -cvox.DomUtil.findFocusableDescendant = function(targetNode) { - // Search down the descendants chain until a focusable node is found - if (targetNode) { - var focusableNode = - cvox.DomUtil.findNode(targetNode, cvox.DomUtil.isFocusable); - if (focusableNode) { - return focusableNode; - } - } - return null; -}; - - -/** - * Returns the number of focusable nodes in root's subtree. The count does not - * include root. - * - * @param {Node} targetNode The node whose descendants to check are focusable. - * @return {number} The number of focusable descendants. - */ -cvox.DomUtil.countFocusableDescendants = function(targetNode) { - return targetNode ? - cvox.DomUtil.countNodes(targetNode, cvox.DomUtil.isFocusable) : 0; -}; - - -/** - * Checks if the targetNode is still attached to the document. - * A node can become detached because of AJAX changes. - * - * @param {Object} targetNode The node to check. - * @return {boolean} True if the targetNode is still attached. - */ -cvox.DomUtil.isAttachedToDocument = function(targetNode) { - while (targetNode) { - if (targetNode.tagName && (targetNode.tagName == 'HTML')) { - return true; - } - targetNode = targetNode.parentNode; - } - return false; -}; - - -/** - * Dispatches a left click event on the element that is the targetNode. - * Clicks go in the sequence of mousedown, mouseup, and click. - * @param {Node} targetNode The target node of this operation. - * @param {boolean} shiftKey Specifies if shift is held down. - * @param {boolean} callOnClickDirectly Specifies whether or not to directly - * invoke the onclick method if there is one. - * @param {boolean=} opt_double True to issue a double click. - * @param {boolean=} opt_handleOwnEvents Whether to handle the generated - * events through the normal event processing. - */ -cvox.DomUtil.clickElem = function( - targetNode, shiftKey, callOnClickDirectly, opt_double, - opt_handleOwnEvents) { - // If there is an activeDescendant of the targetNode, then that is where the - // click should actually be targeted. - var activeDescendant = cvox.AriaUtil.getActiveDescendant(targetNode); - if (activeDescendant) { - targetNode = activeDescendant; - } - if (callOnClickDirectly) { - var onClickFunction = null; - if (targetNode.onclick) { - onClickFunction = targetNode.onclick; - } - if (!onClickFunction && (targetNode.nodeType != 1) && - targetNode.parentNode && targetNode.parentNode.onclick) { - onClickFunction = targetNode.parentNode.onclick; - } - var keepGoing = true; - if (onClickFunction) { - try { - keepGoing = onClickFunction(); - } catch (exception) { - // Something went very wrong with the onclick method; we'll ignore it - // and just dispatch a click event normally. - } - } - if (!keepGoing) { - // The onclick method ran successfully and returned false, meaning the - // event should not bubble up, so we will return here. - return; - } - } - - // Send a mousedown (or simply a double click if requested). - var evt = document.createEvent('MouseEvents'); - var evtType = opt_double ? 'dblclick' : 'mousedown'; - evt.initMouseEvent(evtType, true, true, document.defaultView, - 1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null); - // Unless asked not to, Mark any events we generate so we don't try to - // process our own events. - evt.fromCvox = !opt_handleOwnEvents; - try { - targetNode.dispatchEvent(evt); - } catch (e) {} - //Send a mouse up - evt = document.createEvent('MouseEvents'); - evt.initMouseEvent('mouseup', true, true, document.defaultView, - 1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null); - evt.fromCvox = !opt_handleOwnEvents; - try { - targetNode.dispatchEvent(evt); - } catch (e) {} - //Send a click - evt = document.createEvent('MouseEvents'); - evt.initMouseEvent('click', true, true, document.defaultView, - 1, 0, 0, 0, 0, false, false, shiftKey, false, 0, null); - evt.fromCvox = !opt_handleOwnEvents; - try { - targetNode.dispatchEvent(evt); - } catch (e) {} - - if (cvox.DomUtil.isInternalLink(targetNode)) { - cvox.DomUtil.syncInternalLink(targetNode); - } -}; - - -/** - * Syncs to an internal link. - * @param {Node} node A link whose href's target we want to sync. - */ -cvox.DomUtil.syncInternalLink = function(node) { - var targetNode; - var targetId = node.href.split('#')[1]; - targetNode = document.getElementById(targetId); - if (!targetNode) { - var nodes = document.getElementsByName(targetId); - if (nodes.length > 0) { - targetNode = nodes[0]; - } - } - if (targetNode) { - // Insert a dummy node to adjust next Tab focus location. - var parent = targetNode.parentNode; - var dummyNode = document.createElement('div'); - dummyNode.setAttribute('tabindex', '-1'); - parent.insertBefore(dummyNode, targetNode); - dummyNode.setAttribute('chromevoxignoreariahidden', 1); - dummyNode.focus(); - cvox.ChromeVox.syncToNode(targetNode, false); - } -}; - - -/** - * Given an HTMLInputElement, returns true if it's an editable text type. - * This includes input type='text' and input type='password' and a few - * others. - * - * @param {Node} node The node to check. - * @return {boolean} True if the node is an INPUT with an editable text type. - */ -cvox.DomUtil.isInputTypeText = function(node) { - if (!node || node.constructor != HTMLInputElement) { - return false; - } - - switch (node.type) { - case 'email': - case 'number': - case 'password': - case 'search': - case 'text': - case 'tel': - case 'url': - case '': - return true; - default: - return false; - } -}; - - -/** - * Given a node, returns true if it's a control. Controls are *not necessarily* - * leaf-level given that some composite controls may have focusable children - * if they are managing focus with tabindex: - * ( http://www.w3.org/TR/2010/WD-wai-aria-practices-20100916/#visualfocus ). - * - * @param {Node} node The node to check. - * @return {boolean} True if the node is a control. - */ -cvox.DomUtil.isControl = function(node) { - if (cvox.AriaUtil.isControlWidget(node) && - cvox.DomUtil.isFocusable(node)) { - return true; - } - if (node.tagName) { - switch (node.tagName) { - case 'BUTTON': - case 'TEXTAREA': - case 'SELECT': - return true; - case 'INPUT': - return node.type != 'hidden'; - } - } - if (node.isContentEditable) { - return true; - } - return false; -}; - - -/** - * Given a node, returns true if it's a leaf-level control. This includes - * composite controls thare are managing focus for children with - * activedescendant, but not composite controls with focusable children: - * ( http://www.w3.org/TR/2010/WD-wai-aria-practices-20100916/#visualfocus ). - * - * @param {Node} node The node to check. - * @return {boolean} True if the node is a leaf-level control. - */ -cvox.DomUtil.isLeafLevelControl = function(node) { - if (cvox.DomUtil.isControl(node)) { - return !(cvox.AriaUtil.isCompositeControl(node) && - cvox.DomUtil.findFocusableDescendant(node)); - } - return false; -}; - - -/** - * Given a node that might be inside of a composite control like a listbox, - * return the surrounding control. - * @param {Node} node The node from which to start looking. - * @return {Node} The surrounding composite control node, or null if none. - */ -cvox.DomUtil.getSurroundingControl = function(node) { - var surroundingControl = null; - if (!cvox.DomUtil.isControl(node) && node.hasAttribute && - node.hasAttribute('role')) { - surroundingControl = node.parentElement; - while (surroundingControl && - !cvox.AriaUtil.isCompositeControl(surroundingControl)) { - surroundingControl = surroundingControl.parentElement; - } - } - return surroundingControl; -}; - - -/** - * Given a node and a function for determining when to stop - * descent, return the next leaf-like node. - * - * @param {!Node} node The node from which to start looking, - * this node *must not* be above document.body. - * @param {boolean} r True if reversed. False by default. - * @param {function(!Node):boolean} isLeaf A function that - * returns true if we should stop descending. - * @return {Node} The next leaf-like node or null if there is no next - * leaf-like node. This function will always return a node below - * document.body and never document.body itself. - */ -cvox.DomUtil.directedNextLeafLikeNode = function(node, r, isLeaf) { - if (node != document.body) { - // if not at the top of the tree, we want to find the next possible - // branch forward in the dom, so we climb up the parents until we find a - // node that has a nextSibling - while (!cvox.DomUtil.directedNextSibling(node, r)) { - if (!node) { - return null; - } - // since node is never above document.body, it always has a parent. - // so node.parentNode will never be null. - node = /** @type {!Node} */(node.parentNode); - if (node == document.body) { - // we've readed the end of the document. - return null; - } - } - if (cvox.DomUtil.directedNextSibling(node, r)) { - // we just checked that next sibling is non-null. - node = /** @type {!Node} */(cvox.DomUtil.directedNextSibling(node, r)); - } - } - // once we're at our next sibling, we want to descend down into it as - // far as the child class will allow - while (cvox.DomUtil.directedFirstChild(node, r) && !isLeaf(node)) { - node = /** @type {!Node} */(cvox.DomUtil.directedFirstChild(node, r)); - } - - // after we've done all that, if we are still at document.body, this must - // be an empty document. - if (node == document.body) { - return null; - } - return node; -}; - - -/** - * Given a node, returns the next leaf node. - * - * @param {!Node} node The node from which to start looking - * for the next leaf node. - * @param {boolean=} reverse True if reversed. False by default. - * @return {Node} The next leaf node. - * Null if there is no next leaf node. - */ -cvox.DomUtil.directedNextLeafNode = function(node, reverse) { - reverse = !!reverse; - return cvox.DomUtil.directedNextLeafLikeNode( - node, reverse, cvox.DomUtil.isLeafNode); -}; - - -/** - * Given a node, returns the previous leaf node. - * - * @param {!Node} node The node from which to start looking - * for the previous leaf node. - * @return {Node} The previous leaf node. - * Null if there is no previous leaf node. - */ -cvox.DomUtil.previousLeafNode = function(node) { - return cvox.DomUtil.directedNextLeafNode(node, true); -}; - - -/** - * Computes the outer most leaf node of a given node, depending on value - * of the reverse flag r. - * @param {!Node} node in the DOM. - * @param {boolean} r True if reversed. False by default. - * @param {function(!Node):boolean} pred Predicate to decide - * what we consider a leaf. - * @return {Node} The outer most leaf node of that node. - */ -cvox.DomUtil.directedFindFirstNode = function(node, r, pred) { - var child = cvox.DomUtil.directedFirstChild(node, r); - while (child) { - if (pred(child)) { - return child; - } else { - var leaf = cvox.DomUtil.directedFindFirstNode(child, r, pred); - if (leaf) { - return leaf; - } - } - child = cvox.DomUtil.directedNextSibling(child, r); - } - return null; -}; - - -/** - * Moves to the deepest node satisfying a given predicate under the given node. - * @param {!Node} node in the DOM. - * @param {boolean} r True if reversed. False by default. - * @param {function(!Node):boolean} pred Predicate deciding what a leaf is. - * @return {Node} The deepest node satisfying pred. - */ -cvox.DomUtil.directedFindDeepestNode = function(node, r, pred) { - var next = cvox.DomUtil.directedFindFirstNode(node, r, pred); - if (!next) { - if (pred(node)) { - return node; - } else { - return null; - } - } else { - return cvox.DomUtil.directedFindDeepestNode(next, r, pred); - } -}; - - -/** - * Computes the next node wrt. a predicate that is a descendant of ancestor. - * @param {!Node} node in the DOM. - * @param {!Node} ancestor of the given node. - * @param {boolean} r True if reversed. False by default. - * @param {function(!Node):boolean} pred Predicate to decide - * what we consider a leaf. - * @param {boolean=} above True if the next node can live in the subtree - * directly above the start node. False by default. - * @param {boolean=} deep True if we are looking for the next node that is - * deepest in the tree. Otherwise the next shallow node is returned. - * False by default. - * @return {Node} The next node in the DOM that satisfies the predicate. - */ -cvox.DomUtil.directedFindNextNode = function( - node, ancestor, r, pred, above, deep) { - above = !!above; - deep = !!deep; - if (!cvox.DomUtil.isDescendantOfNode(node, ancestor) || node == ancestor) { - return null; - } - var next = cvox.DomUtil.directedNextSibling(node, r); - while (next) { - if (!deep && pred(next)) { - return next; - } - var leaf = (deep ? - cvox.DomUtil.directedFindDeepestNode : - cvox.DomUtil.directedFindFirstNode)(next, r, pred); - if (leaf) { - return leaf; - } - if (deep && pred(next)) { - return next; - } - next = cvox.DomUtil.directedNextSibling(next, r); - } - var parent = /** @type {!Node} */(node.parentNode); - if (above && pred(parent)) { - return parent; - } - return cvox.DomUtil.directedFindNextNode( - parent, ancestor, r, pred, above, deep); -}; - - -/** - * Get a string representing a control's value and state, i.e. the part - * that changes while interacting with the control - * @param {Element} control A control. - * @return {string} The value and state string. - */ -cvox.DomUtil.getControlValueAndStateString = function(control) { - var parentControl = cvox.DomUtil.getSurroundingControl(control); - if (parentControl) { - return cvox.DomUtil.collapseWhitespace( - cvox.DomUtil.getValue(control) + ' ' + - cvox.DomUtil.getName(control) + ' ' + - cvox.DomUtil.getState(control, true)); - } else { - return cvox.DomUtil.collapseWhitespace( - cvox.DomUtil.getValue(control) + ' ' + - cvox.DomUtil.getState(control, true)); - } -}; - - -/** - * Determine whether the given node is an internal link. - * @param {Node} node The node to be examined. - * @return {boolean} True if the node is an internal link, false otherwise. - */ -cvox.DomUtil.isInternalLink = function(node) { - if (node.nodeType == 1) { // Element nodes only. - var href = node.getAttribute('href'); - if (href && href.indexOf('#') != -1) { - var path = href.split('#')[0]; - return path == '' || path == window.location.pathname; - } - } - return false; -}; - - -/** - * Get a string containing the currently selected link's URL. - * @param {Node} node The link from which URL needs to be extracted. - * @return {string} The value of the URL. - */ -cvox.DomUtil.getLinkURL = function(node) { - if (node.tagName == 'A') { - if (node.getAttribute('href')) { - if (cvox.DomUtil.isInternalLink(node)) { - return Msgs.getMsg('internal_link'); - } else { - return node.getAttribute('href'); - } - } else { - return ''; - } - } else if (cvox.AriaUtil.getRoleName(node) == - Msgs.getMsg('role_link')) { - return Msgs.getMsg('unknown_link'); - } - - return ''; -}; - - -/** - * Checks if a given node is inside a table and returns the table node if it is - * @param {Node} node The node. - * @param {{allowCaptions: (undefined|boolean)}=} kwargs Optional named args. - * allowCaptions: If true, will return true even if inside a caption. False - * by default. - * @return {Node} If the node is inside a table, the table node. Null if it - * is not. - */ -cvox.DomUtil.getContainingTable = function(node, kwargs) { - var ancestors = cvox.DomUtil.getAncestors(node); - return cvox.DomUtil.findTableNodeInList(ancestors, kwargs); -}; - - -/** - * Extracts a table node from a list of nodes. - * @param {Array<Node>} nodes The list of nodes. - * @param {{allowCaptions: (undefined|boolean)}=} kwargs Optional named args. - * allowCaptions: If true, will return true even if inside a caption. False - * by default. - * @return {Node} The table node if the list of nodes contains a table node. - * Null if it does not. - */ -cvox.DomUtil.findTableNodeInList = function(nodes, kwargs) { - kwargs = kwargs || {allowCaptions: false}; - // Don't include the caption node because it is actually rendered outside - // of the table. - for (var i = nodes.length - 1, node; node = nodes[i]; i--) { - if (node.constructor != Text) { - if (!kwargs.allowCaptions && node.tagName == 'CAPTION') { - return null; - } - if ((node.tagName == 'TABLE') || cvox.AriaUtil.isGrid(node)) { - return node; - } - } - } - return null; -}; - - -/** - * Determines whether a given table is a data table or a layout table - * @param {Node} tableNode The table node. - * @return {boolean} If the table is a layout table, returns true. False - * otherwise. - */ -cvox.DomUtil.isLayoutTable = function(tableNode) { - // TODO(stoarca): Why are we returning based on this inaccurate heuristic - // instead of first trying the better heuristics below? - if (tableNode.rows && (tableNode.rows.length <= 1 || - (tableNode.rows[0].childElementCount == 1))) { - // This table has either 0 or one rows, or only "one" column. - // This is a quick check for column count and may not be accurate. See - // TraverseTable.getW3CColCount_ for a more accurate - // (but more complicated) way to determine column count. - return true; - } - - // These heuristics are adapted from the Firefox data and layout table. - // heuristics: http://asurkov.blogspot.com/2011/10/data-vs-layout-table.html - if (cvox.AriaUtil.isGrid(tableNode)) { - // This table has an ARIA role identifying it as a grid. - // Not a layout table. - return false; - } - if (cvox.AriaUtil.isLandmark(tableNode)) { - // This table has an ARIA landmark role - not a layout table. - return false; - } - - if (tableNode.caption || tableNode.summary) { - // This table has a caption or a summary - not a layout table. - return false; - } - - if ((cvox.XpathUtil.evalXPath('tbody/tr/th', tableNode).length > 0) && - (cvox.XpathUtil.evalXPath('tbody/tr/td', tableNode).length > 0)) { - // This table at least one column and at least one column header. - // Not a layout table. - return false; - } - - if (cvox.XpathUtil.evalXPath('colgroup', tableNode).length > 0) { - // This table specifies column groups - not a layout table. - return false; - } - - if ((cvox.XpathUtil.evalXPath('thead', tableNode).length > 0) || - (cvox.XpathUtil.evalXPath('tfoot', tableNode).length > 0)) { - // This table has header or footer rows - not a layout table. - return false; - } - - if ((cvox.XpathUtil.evalXPath('tbody/tr/td/embed', tableNode).length > 0) || - (cvox.XpathUtil.evalXPath('tbody/tr/td/object', tableNode).length > 0) || - (cvox.XpathUtil.evalXPath('tbody/tr/td/iframe', tableNode).length > 0) || - (cvox.XpathUtil.evalXPath('tbody/tr/td/applet', tableNode).length > 0)) { - // This table contains embed, object, applet, or iframe elements. It is - // a layout table. - return true; - } - - // These heuristics are loosely based on Okada and Miura's "Detection of - // Layout-Purpose TABLE Tags Based on Machine Learning" (2007). - // http://books.google.com/books?id=kUbmdqasONwC&lpg=PA116&ots=Lb3HJ7dISZ&lr&pg=PA116 - - // Increase the points for each heuristic. If there are 3 or more points, - // this is probably a layout table. - var points = 0; - - if (! cvox.DomUtil.hasBorder(tableNode)) { - // This table has no border. - points++; - } - - if (tableNode.rows.length <= 6) { - // This table has a limited number of rows. - points++; - } - - if (cvox.DomUtil.countPreviousTags(tableNode) <= 12) { - // This table has a limited number of previous tags. - points++; - } - - if (cvox.XpathUtil.evalXPath('tbody/tr/td/table', tableNode).length > 0) { - // This table has nested tables. - points++; - } - return (points >= 3); -}; - - -/** - * Count previous tags, which we dfine as the number of HTML tags that - * appear before the given node. - * @param {Node} node The given node. - * @return {number} The number of previous tags. - */ -cvox.DomUtil.countPreviousTags = function(node) { - var ancestors = cvox.DomUtil.getAncestors(node); - return ancestors.length + cvox.DomUtil.countPreviousSiblings(node); -}; - - -/** - * Counts previous siblings, not including text nodes. - * @param {Node} node The given node. - * @return {number} The number of previous siblings. - */ -cvox.DomUtil.countPreviousSiblings = function(node) { - var count = 0; - var prev = node.previousSibling; - while (prev != null) { - if (prev.constructor != Text) { - count++; - } - prev = prev.previousSibling; - } - return count; -}; - - -/** - * Whether a given table has a border or not. - * @param {Node} tableNode The table node. - * @return {boolean} If the table has a border, return true. False otherwise. - */ -cvox.DomUtil.hasBorder = function(tableNode) { - // If .frame contains "void" there is no border. - if (tableNode.frame) { - return (tableNode.frame.indexOf('void') == -1); - } - - // If .border is defined and == "0" then there is no border. - if (tableNode.border) { - if (tableNode.border.length == 1) { - return (tableNode.border != '0'); - } else { - return (tableNode.border.slice(0, -2) != 0); - } - } - - // If .style.border-style is 'none' there is no border. - if (tableNode.style.borderStyle && tableNode.style.borderStyle == 'none') { - return false; - } - - // If .style.border-width is specified in units of length - // ( https://developer.mozilla.org/en/CSS/border-width ) then we need - // to check if .style.border-width starts with 0[px,em,etc] - if (tableNode.style.borderWidth) { - return (tableNode.style.borderWidth.slice(0, -2) != 0); - } - - // If .style.border-color is defined, then there is a border - if (tableNode.style.borderColor) { - return true; - } - return false; -}; - - -/** - * Return the first leaf node, starting at the top of the document. - * @return {Node?} The first leaf node in the document, if found. - */ -cvox.DomUtil.getFirstLeafNode = function() { - var node = document.body; - while (node && node.firstChild) { - node = node.firstChild; - } - while (node && !cvox.DomUtil.hasContent(node)) { - node = cvox.DomUtil.directedNextLeafNode(node); - } - return node; -}; - - -/** - * Finds the first descendant node that matches the filter function, using - * a depth first search. This function offers the most general purpose way - * of finding a matching element. You may also wish to consider - * {@code goog.dom.query} which can express many matching criteria using - * CSS selector expressions. These expressions often result in a more - * compact representation of the desired result. - * This is the findNode function from goog.dom: - * http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/dom/dom.js - * - * @param {Node} root The root of the tree to search. - * @param {function(Node) : boolean} p The filter function. - * @return {Node|undefined} The found node or undefined if none is found. - */ -cvox.DomUtil.findNode = function(root, p) { - var rv = []; - var found = cvox.DomUtil.findNodes_(root, p, rv, true, 10000); - return found ? rv[0] : undefined; -}; - - -/** - * Finds the number of nodes matching the filter. - * @param {Node} root The root of the tree to search. - * @param {function(Node) : boolean} p The filter function. - * @return {number} The number of nodes selected by filter. - */ -cvox.DomUtil.countNodes = function(root, p) { - var rv = []; - cvox.DomUtil.findNodes_(root, p, rv, false, 10000); - return rv.length; -}; - - -/** - * Finds the first or all the descendant nodes that match the filter function, - * using a depth first search. - * @param {Node} root The root of the tree to search. - * @param {function(Node) : boolean} p The filter function. - * @param {Array<Node>} rv The found nodes are added to this array. - * @param {boolean} findOne If true we exit after the first found node. - * @param {number} maxChildCount The max child count. This is used as a kill - * switch - if there are more nodes than this, terminate the search. - * @return {boolean} Whether the search is complete or not. True in case - * findOne is true and the node is found. False otherwise. This is the - * findNodes_ function from goog.dom: - * http://code.google.com/p/closure-library/source/browse/trunk/closure/goog/dom/dom.js. - * @private - */ -cvox.DomUtil.findNodes_ = function(root, p, rv, findOne, maxChildCount) { - if ((root != null) || (maxChildCount == 0)) { - var child = root.firstChild; - while (child) { - if (p(child)) { - rv.push(child); - if (findOne) { - return true; - } - } - maxChildCount = maxChildCount - 1; - if (cvox.DomUtil.findNodes_(child, p, rv, findOne, maxChildCount)) { - return true; - } - child = child.nextSibling; - } - } - return false; -}; - - -/** - * Converts a NodeList into an array - * @param {NodeList} nodeList The nodeList. - * @return {Array} The array of nodes in the nodeList. - */ -cvox.DomUtil.toArray = function(nodeList) { - var nodeArray = []; - for (var i = 0; i < nodeList.length; i++) { - nodeArray.push(nodeList[i]); - } - return nodeArray; -}; - - -/** - * Creates a new element with the same attributes and no children. - * @param {Node|Text} node A node to clone. - * @param {Object<boolean>} skipattrs Set the attribute to true to skip it - * during cloning. - * @return {Node|Text} The cloned node. - */ -cvox.DomUtil.shallowChildlessClone = function(node, skipattrs) { - if (node.nodeName == '#text') { - return document.createTextNode(node.nodeValue); - } - - if (node.nodeName == '#comment') { - return document.createComment(node.nodeValue); - } - - var ret = document.createElement(node.nodeName); - for (var i = 0; i < node.attributes.length; ++i) { - var attr = node.attributes[i]; - if (skipattrs && skipattrs[attr.nodeName]) { - continue; - } - ret.setAttribute(attr.nodeName, attr.nodeValue); - } - return ret; -}; - - -/** - * Creates a new element with the same attributes and clones of children. - * @param {Node|Text} node A node to clone. - * @param {Object<boolean>} skipattrs Set the attribute to true to skip it - * during cloning. - * @return {Node|Text} The cloned node. - */ -cvox.DomUtil.deepClone = function(node, skipattrs) { - var ret = cvox.DomUtil.shallowChildlessClone(node, skipattrs); - for (var i = 0; i < node.childNodes.length; ++i) { - ret.appendChild(cvox.DomUtil.deepClone(node.childNodes[i], skipattrs)); - } - return ret; -}; - - -/** - * Returns either node.firstChild or node.lastChild, depending on direction. - * @param {Node|Text} node The node. - * @param {boolean} reverse If reversed. - * @return {Node|Text} The directed first child or null if the node has - * no children. - */ -cvox.DomUtil.directedFirstChild = function(node, reverse) { - if (reverse) { - return node.lastChild; - } - return node.firstChild; -}; - -/** - * Returns either node.nextSibling or node.previousSibling, depending on - * direction. - * @param {Node|Text} node The node. - * @param {boolean=} reverse If reversed. - * @return {Node|Text} The directed next sibling or null if there are - * no more siblings in that direction. - */ -cvox.DomUtil.directedNextSibling = function(node, reverse) { - if (!node) { - return null; - } - if (reverse) { - return node.previousSibling; - } - return node.nextSibling; -}; - -/** - * Creates a function that sends a click. This is because loop closures - * are dangerous. - * See: http://joust.kano.net/weblog/archive/2005/08/08/ - * a-huge-gotcha-with-javascript-closures/ - * @param {Node} targetNode The target node to click on. - * @return {function()} A function that will click on the given targetNode. - */ -cvox.DomUtil.createSimpleClickFunction = function(targetNode) { - var target = targetNode.cloneNode(true); - return function() { cvox.DomUtil.clickElem(target, false, false); }; -}; - -/** - * Adds a node to document.head if that node has not already been added. - * If document.head does not exist, this will add the node to the body. - * @param {Node} node The node to add. - * @param {string=} opt_id The id of the node to ensure the node is only - * added once. - */ -cvox.DomUtil.addNodeToHead = function(node, opt_id) { - if (opt_id && document.getElementById(opt_id)) { - return; - } - var p = document.head || document.body; - p.appendChild(node); -}; - - -/** - * Checks if a given node is inside a math expressions and - * returns the math node if one exists. - * @param {Node} node The node. - * @return {Node} The math node, if the node is inside a math expression. - * Null if it is not. - */ -cvox.DomUtil.getContainingMath = function(node) { - var ancestors = cvox.DomUtil.getAncestors(node); - return cvox.DomUtil.findMathNodeInList(ancestors); -}; - - -/** - * Extracts a math node from a list of nodes. - * @param {Array<Node>} nodes The list of nodes. - * @return {Node} The math node if the list of nodes contains a math node. - * Null if it does not. - */ -cvox.DomUtil.findMathNodeInList = function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) { - if (cvox.DomUtil.isMath(node)) { - return node; - } - } - return null; -}; - - -/** - * Checks to see wether a node is a math node. - * @param {Node} node The node to be tested. - * @return {boolean} Whether or not a node is a math node. - */ -cvox.DomUtil.isMath = function(node) { - return cvox.DomUtil.isMathml(node) || - cvox.DomUtil.isMathJax(node) || - cvox.DomUtil.isMathImg(node) || - cvox.AriaUtil.isMath(node); -}; - - -/** - * Specifies node classes in which we expect maths expressions a alt text. - * @type {{tex: Array<string>, - * asciimath: Array<string>}} - */ -// These are the classes for which we assume they contain Maths in the ALT or -// TITLE attribute. -// tex: Wikipedia; -// latex: Wordpress; -// numberedequation, inlineformula, displayformula: MathWorld; -cvox.DomUtil.ALT_MATH_CLASSES = { - tex: ['tex', 'latex'], - asciimath: ['numberedequation', 'inlineformula', 'displayformula'] -}; - - -/** - * Composes a query selector string for image nodes with alt math content by - * type of content. - * @param {string} contentType The content type, e.g., tex, asciimath. - * @return {!string} The query elector string. - */ -cvox.DomUtil.altMathQuerySelector = function(contentType) { - var classes = cvox.DomUtil.ALT_MATH_CLASSES[contentType]; - if (classes) { - return classes.map(function(x) {return 'img.' + x;}).join(', '); - } - return ''; -}; - - -/** - * Check if a given node is potentially a math image with alternative text in - * LaTeX. - * @param {Node} node The node to be tested. - * @return {boolean} Whether or not a node has an image with class TeX or LaTeX. - */ -cvox.DomUtil.isMathImg = function(node) { - if (!node || !node.tagName || !node.className) { - return false; - } - if (node.tagName != 'IMG') { - return false; - } - for (var i = 0, className; className = node.classList.item(i); i++) { - className = className.toLowerCase(); - if (cvox.DomUtil.ALT_MATH_CLASSES.tex.indexOf(className) != -1 || - cvox.DomUtil.ALT_MATH_CLASSES.asciimath.indexOf(className) != -1) { - return true; - } - } - return false; -}; - - -/** - * Checks to see whether a node is a MathML node. - * !! This is necessary as Chrome currently does not upperCase Math tags !! - * @param {Node} node The node to be tested. - * @return {boolean} Whether or not a node is a MathML node. - */ -cvox.DomUtil.isMathml = function(node) { - if (!node || !node.tagName) { - return false; - } - return node.tagName.toLowerCase() == 'math'; -}; - - -/** - * Checks to see wether a node is a MathJax node. - * @param {Node} node The node to be tested. - * @return {boolean} Whether or not a node is a MathJax node. - */ -cvox.DomUtil.isMathJax = function(node) { - if (!node || !node.tagName || !node.className) { - return false; - } - - function isSpanWithClass(n, cl) { - return (n.tagName == 'SPAN' && - n.className.split(' ').some(function(x) { - return x.toLowerCase() == cl;})); - }; - if (isSpanWithClass(node, 'math')) { - var ancestors = cvox.DomUtil.getAncestors(node); - return ancestors.some(function(x) {return isSpanWithClass(x, 'mathjax');}); - } - return false; -}; - - -/** - * Computes the id of the math span in a MathJax DOM element. - * @param {string} jaxId The id of the MathJax node. - * @return {string} The id of the span node. - */ -cvox.DomUtil.getMathSpanId = function(jaxId) { - var node = document.getElementById(jaxId + '-Frame'); - if (node) { - var span = node.querySelector('span.math'); - if (span) { - return span.id; - } - } -}; - - -/** - * Returns true if the node has a longDesc. - * @param {Node} node The node to be tested. - * @return {boolean} Whether or not a node has a longDesc. - */ -cvox.DomUtil.hasLongDesc = function(node) { - if (node && node.longDesc) { - return true; - } - return false; -}; - - -/** - * Returns tag name of a node if it has one. - * @param {Node} node A node. - * @return {string} A the tag name of the node. - */ -cvox.DomUtil.getNodeTagName = function(node) { - if (node.nodeType == Node.ELEMENT_NODE) { - return node.tagName; - } - return ''; -}; - - -/** - * Cleaning up a list of nodes to remove empty text nodes. - * @param {NodeList} nodes The nodes list. - * @return {!Array<Node|string|null>} The cleaned up list of nodes. - */ -cvox.DomUtil.purgeNodes = function(nodes) { - return cvox.DomUtil.toArray(nodes). - filter(function(node) { - return node.nodeType != Node.TEXT_NODE || - !node.textContent.match(/^\s+$/);}); -}; - - -/** - * Calculates a hit point for a given node. - * @return {{x:(number), y:(number)}} The position. - */ -cvox.DomUtil.elementToPoint = function(node) { - if (!node) { - return {x: 0, y: 0}; - } - if (node.constructor == Text) { - node = node.parentNode; - } - var r = node.getBoundingClientRect(); - return { - x: r.left + (r.width / 2), - y: r.top + (r.height / 2) - }; -}; - - -/** - * Checks if an input node supports HTML5 selection. - * If the node is not an input element, returns false. - * @param {Node} node The node to check. - * @return {boolean} True if HTML5 selection supported. - */ -cvox.DomUtil.doesInputSupportSelection = function(node) { - return goog.isDef(node) && - node.tagName == 'INPUT' && - node.type != 'email' && - node.type != 'number'; -}; - - -/** - * Gets the hint text for a given element. - * @param {Node} node The target node. - * @return {string} The hint text. - */ -cvox.DomUtil.getHint = function(node) { - var desc = ''; - if (node.hasAttribute) { - if (node.hasAttribute('aria-describedby')) { - var describedByIds = node.getAttribute('aria-describedby').split(' '); - for (var describedById, i = 0; describedById = describedByIds[i]; i++) { - var describedNode = document.getElementById(describedById); - if (describedNode) { - desc += ' ' + cvox.DomUtil.getName( - describedNode, true, true, true); - } - } - } - } - return desc; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs deleted file mode 100644 index b77b36676fa..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/dom_util_test.unitjs +++ /dev/null @@ -1,1578 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxDomUtilUnitTest() {} - -CvoxDomUtilUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.ChromeVox', - 'cvox.DescriptionUtil', - 'cvox.DomUtil', - 'TestMsgs', - ], - - /** @override */ - setUp: function() { - Msgs = TestMsgs; - }, - - asText_: function(node) { - var temp = document.createElement('div'); - temp.appendChild(node); - return temp.innerHTML; - }, - - assertEqualsAsText_: function(node1, node2) { - assertEquals(this.asText_(node1), this.asText_(node2)); - }, - - loadDomUtilTestDoc_: function() { - this.loadDoc(function() {/*! - <style type="text/css"> - #display_none { display: none; } - #visibility_hidden { visibility: hidden; } - #forced_visible { visibility: hidden; } - #visibility_collapse { visibility: collapse; } - #opacity_zero { opacity: 0; } - #opacity_partial { opacity: 0.5; } - #opacity_undefined { } - #nested_visibility_hide { visibility: hidden; } - #nested_visibility_show { visibility: visible; } - #nested_display_none { display: none; } - #nested_display_block { display: block; } - </style> - <form action=""> - - <div id="normal_node">1</div> - <div id="display_none">2</div> - <div id="visibility_hidden">3</div> - <div id="visibility_collapse">3b</div> - <div id="opacity_zero">4</div> - <div id="opacity_partial">4b</div> - <div id="opacity_undefined">5</div> - <select id="select_node"><option>5</option></select> - <textarea id="textarea">6</textarea> - <div id="forced_visible" aria-hidden="false">7</div> - <p id="normal_para">----</p> - <p id="presentation" role="presentation">----</p> - <p id="aria_hidden" aria-hidden="true">----</p> - <p id="only_spaces"> </p> - <p id="only_tabs"> </p> - <p id="only_newlines"> - - </p> - <p id="only_nbsp"> </p> - <p id="other_entity">&</p> - <img id="img"> - <img id="img_alt" alt="tree"> - <img id="img_blankalt" alt=""> - - <input id="check" type="checkbox"> - <input id="check_checked" type="checkbox" checked> - - <span><p id="a">a</p></span> - <span><p id="b">b</p><p id="c">c</p></span> - </form> - - <a id="special_link1" href="http://google.com"><span id="empty_span"></span> - </a> - <a id="special_link2" href="http://google.com"><span>Text content</span></a> - <a id="special_link3"><span></span></a> - - <div id="nested_visibility_hide"> - hide<div id="nested_visibility_show">show</div>me - </div> - <div id="nested_display_none"> - nothing<div id="nested_display_block">will</div>show - </div> - */}); - }, -}; - -TEST_F('CvoxDomUtilUnitTest', 'IsVisible', function() { - this.loadDomUtilTestDoc_(); - - // Simple tests. - var node = $('normal_node'); - assertEquals(true, cvox.DomUtil.isVisible(node)); - node = $('display_none'); - assertEquals(false, cvox.DomUtil.isVisible(node)); - node = $('visibility_hidden'); - assertEquals(false, cvox.DomUtil.isVisible(node)); - node = $('visibility_collapse'); - assertEquals(false, cvox.DomUtil.isVisible(node)); - node = $('opacity_zero'); - assertEquals(false, cvox.DomUtil.isVisible(node)); - node = $('opacity_partial'); - assertEquals(true, cvox.DomUtil.isVisible(node)); - node = $('opacity_undefined'); - assertEquals(true, cvox.DomUtil.isVisible(node)); - node = $('forced_visible'); - assertEquals(true, cvox.DomUtil.isVisible(node)); - - // Nested visibility tests. - node = $('nested_visibility_hide'); - assertEquals(true, cvox.DomUtil.isVisible(node)); // Has visible child. - node = $('nested_visibility_hide').childNodes[0]; - assertEquals(false, cvox.DomUtil.isVisible(node)); // TextNode is invisible. - node = $('nested_visibility_show'); - assertEquals(true, cvox.DomUtil.isVisible(node)); - node = $('nested_visibility_show').childNodes[0]; - assertEquals(true, cvox.DomUtil.isVisible(node)); // TextNode is visible. - node = $('nested_display_block'); - assertEquals(false, cvox.DomUtil.isVisible(node)); - - // Options tests (for performance). - node = $('nested_display_block'); - assertEquals(true, - cvox.DomUtil.isVisible(node, {checkAncestors: false})); - node = $('nested_visibility_hide'); - assertEquals(false, - cvox.DomUtil.isVisible(node, {checkDescendants: false})); - - // Test that an element not part of the DOM is treated as invisible. - var div = document.createElement('div'); - assertEquals(false, cvox.DomUtil.isVisible(div)); - document.body.appendChild(div); - assertEquals(true, cvox.DomUtil.isVisible(div)); -}); - -/** Test determining if a node is a leaf node or not. @export */ -TEST_F('CvoxDomUtilUnitTest', 'IsLeafNode', function() { - this.loadDomUtilTestDoc_(); - - var node = $('normal_node'); - assertEquals(false, cvox.DomUtil.isLeafNode(node)); - node = $('display_none'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('visibility_hidden'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('opacity_zero'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('select_node'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('textarea'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('normal_para'); - assertEquals(false, cvox.DomUtil.isLeafNode(node)); - node = $('aria_hidden'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('special_link1'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('special_link2'); - assertEquals(true, cvox.DomUtil.isLeafNode(node)); - node = $('special_link3'); - assertEquals(false, cvox.DomUtil.isLeafNode(node)); - node = $('nested_visibility_hide'); - assertEquals(false, cvox.DomUtil.isLeafNode(node)); -}); - -/** Test determining if a node has content or not. @export */ -TEST_F('CvoxDomUtilUnitTest', 'HasContent', function() { - this.loadDomUtilTestDoc_(); - - var node = $('normal_node'); - cvox.DomUtil.hasContent(node); - assertEquals(true, cvox.DomUtil.hasContent(node)); - node = $('display_none'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('visibility_hidden'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('opacity_zero'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('select_node'); - assertEquals(true, cvox.DomUtil.hasContent(node)); - node = $('textarea'); - assertEquals(true, cvox.DomUtil.hasContent(node)); - node = $('normal_para'); - assertEquals(true, cvox.DomUtil.hasContent(node)); - // TODO (adu): This test fails. Will inspect. - // node = $('presentation'); - // assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('aria_hidden'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('only_spaces'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('only_tabs'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('only_newlines'); - assertEquals(false, cvox.DomUtil.hasContent(node)); - node = $('other_entity'); - assertEquals(true, cvox.DomUtil.hasContent(node)); - node = $('img'); - assertEquals(true, cvox.DomUtil.hasContent(node)); - node = $('img_alt'); - assertEquals(true, cvox.DomUtil.hasContent(node)); - node = $('img_blankalt'); - assertEquals(false, cvox.DomUtil.hasContent(node)); -}); - -/** Test getting a node's state. @export */ -TEST_F('CvoxDomUtilUnitTest', 'NodeState', function() { - this.loadDomUtilTestDoc_(); - this.appendDoc(function() {/*! - <input id="state1_enabled"> - <input id="state1_disabled" disabled> - <button id="state2_enabled">Button</button> - <button id="state2_disabled" disabled>Button</button> - <textarea id="state3_enabled">Textarea</textarea> - <textarea id="state3_disabled" disabled>Textarea</textarea> - <select id="state4_enabled"><option>Select</option></select> - <select id="state4_disabled" disabled><option>Select</option></select> - <div role="button" id="state5_enabled" tabindex="0">ARIAButton</div> - <div role="button" id="state5_disabled" tabindex="0" disabled>ARIAButton</div> - <fieldset> - <input id="state6_enabled"> - </fieldset> - <fieldset disabled> - <input id="state6_disabled"> - </fieldset> - */}); - var node = $('check'); - assertEquals('not checked', cvox.DomUtil.getState(node, true)); - node = $('check_checked'); - assertEquals('checked', cvox.DomUtil.getState(node, true)); - node = $('state1_enabled'); - assertEquals('', cvox.DomUtil.getState(node, true)); - node = $('state1_disabled'); - assertEquals('Disabled', cvox.DomUtil.getState(node, true)); - node = $('state2_enabled'); - assertEquals('', cvox.DomUtil.getState(node, true)); - node = $('state2_disabled'); - assertEquals('Disabled', cvox.DomUtil.getState(node, true)); - node = $('state3_enabled'); - assertEquals('', cvox.DomUtil.getState(node, true)); - node = $('state3_disabled'); - assertEquals('Disabled', cvox.DomUtil.getState(node, true)); - node = $('state4_enabled'); - assertEquals('1 of 1', cvox.DomUtil.getState(node, true)); - node = $('state4_disabled'); - assertEquals('1 of 1 Disabled', cvox.DomUtil.getState(node, true)); - node = $('state5_enabled'); - assertEquals('', cvox.DomUtil.getState(node, true)); - node = $('state5_disabled'); - assertEquals('', cvox.DomUtil.getState(node, true)); - node = $('state6_enabled'); - assertEquals('', cvox.DomUtil.getState(node, true)); - node = $('state6_disabled'); - assertEquals('Disabled', cvox.DomUtil.getState(node, true)); -}); - -/** Test finding the next/previous leaf node. @export */ -TEST_F('CvoxDomUtilUnitTest', 'LeafNodeTraversal', function() { - this.loadDomUtilTestDoc_(); - - var node = $('a'); - node = cvox.DomUtil.directedNextLeafNode(node); - assertEquals('\n ', node.textContent); - node = cvox.DomUtil.directedNextLeafNode(node); - assertEquals('b', node.textContent); - node = cvox.DomUtil.directedNextLeafNode(node); - assertEquals('c', node.textContent); - node = cvox.DomUtil.previousLeafNode(node); - assertEquals('b', node.textContent); - node = cvox.DomUtil.previousLeafNode(node); - assertEquals('\n ', node.textContent); - node = cvox.DomUtil.previousLeafNode(node); - assertEquals('a', node.textContent); -}); - -/** Test finding the label for controls. @export */ -TEST_F('CvoxDomUtilUnitTest', 'GetLabel', function() { - this.loadDoc(function() {/*! - <fieldset id="Fieldset"> - <legend>This is a legend inside a fieldset</legend> - <div align="right"> - <span> - Username: - </span> - </div> - <input name="Email" id="Email" size="18" value="" type="text"> - <span> - Password: - </span> - <input name="Passwd" id="Passwd" size="18" type="password"> - <input name="PersistentCookie" id="PersistentCookie" type="checkbox"> - <label for="PersistentCookie" id="PersistentCookieLabel"> - Stay signed in - </label> - <input name="signIn" id="signIn" value="Sign in" type="submit"> - <input id="dummyA" size="18" value="" type="text" title=""> - <input id="dummyB" size="18" value="" type="text" aria-label=""> - </fieldset> - */}); - - function getControlText(control) { - var description = cvox.DescriptionUtil.getControlDescription(control); - return cvox.DomUtil.collapseWhitespace( - description.context + ' ' + - description.text + ' ' + - description.userValue + ' ' + - description.annotation); - } - - var fieldsetElement = $('Fieldset'); - assertEquals('This is a legend inside a fieldset', - cvox.DomUtil.getName(fieldsetElement, false, false)); - - var usernameField = $('Email'); - assertEquals('', cvox.DomUtil.getValue(usernameField)); - assertEquals('Username:', - cvox.DomUtil.getControlLabelHeuristics(usernameField)); - assertEquals('Username: Edit text', getControlText(usernameField)); - var passwordField = $('Passwd'); - assertEquals('', cvox.DomUtil.getValue(passwordField)); - assertEquals('Password:', - cvox.DomUtil.getControlLabelHeuristics(passwordField)); - assertEquals('Password: Password edit text', getControlText(passwordField)); - var cookieCheckbox = $('PersistentCookie'); - assertEquals('Stay signed in', cvox.DomUtil.getName(cookieCheckbox)); - assertEquals('Stay signed in Check box not checked', - getControlText(cookieCheckbox)); - var signinButton = $('signIn'); - assertEquals('Sign in', cvox.DomUtil.getName(signinButton)); - assertEquals('Sign in Button', getControlText(signinButton)); - var dummyInputA = $('dummyA'); - assertEquals('', cvox.DomUtil.getName(dummyInputA)); - var dummyInputB = $('dummyB'); - assertEquals('', cvox.DomUtil.getName(dummyInputB)); - - // The heuristic no longer returns 'Stay signed in' as the label for - // the signIn button because 'Stay signed in' is in a label that's - // explicitly associated with another control. - //assertEquals('Stay signed in ', - // cvox.DomUtil.getControlLabelHeuristics(signinButton)); -}); - -/** Test finding the label for controls with a more complex setup. @export */ -TEST_F('CvoxDomUtilUnitTest', 'GetLabelComplex', function() { - this.loadDoc(function() {/*! - <table class="bug-report-table"> - <tbody><tr> - <td class="bug-report-fieldlabel"> - <input id="page-url-checkbox" type="checkbox"> - <span id="page-url-label" i18n-content="page-url">Include this URL:</span> - </td> - <td> - <input id="page-url-text" class="bug-report-field" maxlength="200"> - </td> - </tr> - </tbody></table> - <table id="user-email-table" class="bug-report-table"> - <tbody><tr> - <td class="bug-report-fieldlabel"> - <input id="user-email-checkbox" checked="checked" type="checkbox"> - <span id="user-email-label">Include this email:</span> - </td> - <td> - <label id="user-email-text" class="bug-report-field"></label> - </td> - </tr> - </tbody></table> - <table class="bug-report-table"> - <tbody><tr> - <td class="bug-report-fieldlabel"> - <input id="sys-info-checkbox" checked="checked" type="checkbox"> - <span id="sysinfo-label"> - <a id="sysinfo-url" href="#">Send system information</a> - </span> - </td> - </tr> - </tbody></table> - <table class="bug-report-table"> - <tbody><tr> - <td class="bug-report-fieldlabel"> - <input id="screenshot-checkbox" type="checkbox"> - <span id="screenshot-label-current">Include the current screenshot:</span> - </td> - </tr> - </tbody></table> - */}); - var urlCheckbox = $('page-url-checkbox'); - assertEquals('Include this URL:', - cvox.DomUtil.getControlLabelHeuristics(urlCheckbox)); - var emailCheckbox = $('user-email-checkbox'); - assertEquals('Include this email:', - cvox.DomUtil.getControlLabelHeuristics(emailCheckbox)); - var sysCheckbox = $('sys-info-checkbox'); - assertEquals('Send system information', - cvox.DomUtil.getControlLabelHeuristics(sysCheckbox)); -}); - -/**************************************************************/ - -TEST_F('CvoxDomUtilUnitTest', 'EscapedNames', function() { - this.loadDoc(function() {/*! - <p id="en-title" title="<>"></p> - <p id="en-arialabel" aria-label="<>"></p> - <img id="en-img" title="<>"></img> - <p id="en-double" title="&lt;&gt;"></p> - */}); - assertEquals('<>', cvox.DomUtil.getName( - $('en-title'))); - assertEquals('<>', cvox.DomUtil.getName( - $('en-arialabel'))); - assertEquals('<>', cvox.DomUtil.getName( - $('en-img'))); - assertEquals('<>', cvox.DomUtil.getName( - $('en-double'))); -}); - -/** Test a paragraph with plain text. @export */ -TEST_F('CvoxDomUtilUnitTest', 'SimplePara', function() { - this.loadDoc(function() {/*! - <p id="simplepara">This is a simple paragraph.</p> - */}); - var node = $('simplepara'); - var text = cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(node)); - assertEquals('This is a simple paragraph.', text); -}); - -/** Test a paragraph with nested tags. @export */ -TEST_F('CvoxDomUtilUnitTest', 'NestedPara', function() { - this.loadDoc(function() {/*! - <p id="nestedpara">This is a <b>paragraph</b> with <i>nested</i> tags.</p> - */}); - var node = $('nestedpara'); - var text = cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(node)); - assertEquals('This is a paragraph with nested tags.', text); -}); - -/** - * Test a paragraph with nested tags and varying visibility. - * @export - */ -TEST_F('CvoxDomUtilUnitTest', 'NestedVisibilityPara', function() { - this.loadDoc(function() {/*! - <style type="text/css"> - #nested_visibility_paragraph { } - #nested_visibility_paragraph .hide { visibility: hidden; } - #nested_visibility_paragraph .show { visibility: visible; } - </style> - <p id="nested_visibility_paragraph"> - This is - <span class="hide"> - not - <span class="show"> a sentence.</span> - </span> - </p> - */}); - var node = $('nested_visibility_paragraph'); - var text = cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(node)); - assertEquals('This is a sentence.', text); -}); - -/** Test getting text from an IMG node. @export */ -TEST_F('CvoxDomUtilUnitTest', 'Image', function() { - this.loadDoc(function() {/*! - <img id="img"> - <img id="img_noalt" src="rose.png"> - <img id="img_alt" alt="flower" src="rose.png"> - <img id="img_title" title="a Flower" src="rose.png"> - <img id="img_noalt_long" - src="777777777777777777777777777777777.png"> - */}); - - var node = $('img'); - assertEquals('Image', cvox.DomUtil.getName(node)); - node = $('img_noalt'); - assertEquals('rose Image', cvox.DomUtil.getName(node)); - node = $('img_alt'); - assertEquals('flower', cvox.DomUtil.getName(node)); - node = $('img_title'); - assertEquals('a Flower', cvox.DomUtil.getName(node)); - node = $('img_noalt_long'); - assertEquals('Image', cvox.DomUtil.getName(node)); -}); - -/** Test getting text from a select box. @export */ -TEST_F('CvoxDomUtilUnitTest', 'Select', function() { - this.loadDoc(function() {/*! - <select id="select_noneselected"> - <option>Apple</option> - <option>Banana</option> - <option>Pear</option> - </select> - <select id="select_bananaselected"> - <option>Apple</option> - <option selected>Banana</option> - <option>Pear</option> - </select> - */}); - - $('select_noneselected').selectedIndex = -1; - var node = $('select_noneselected'); - assertEquals('', cvox.DomUtil.getValue(node)); - node = $('select_bananaselected'); - assertEquals('Banana', cvox.DomUtil.getValue(node)); -}); - -/** Test whether funky html causes getName to go into infinite loop. */ -TEST_F('CvoxDomUtilUnitTest', 'GetNameInfiniteLoop', function() { - this.loadDoc(function() {/*! - <div> - <label for="a"> - <p id="a">asdf</p> - </label> - </div> - */}); - // intentionally no asserts; if there is an infinite (recursive) loop, - // the stack will blow up - var node = $('a'); - var label = cvox.DomUtil.getName(node); -}); - -/** Test getting text from an INPUT control. @export */ -TEST_F('CvoxDomUtilUnitTest', 'Input', function() { - this.loadDoc(function() {/*! - <form action=""> - <input id="hidden" type="hidden" value="hidden1"> - <input id="input_img" type="image" src="rose.png"> - <input id="input_img_alt" type="image" alt="flower" src="rose.png"> - <input id="submit" type="submit"> - <input id="submit_withvalue" type="submit" value="Go"> - <input id="reset" type="reset"> - <input id="reset_withvalue" type="reset" value="Stop"> - <input id="button" type="button" value="Button"> - <input id="checkbox" type="checkbox" value="ignore1"> - <input id="checkbox_title" type="checkbox" value="ignore1" title="toggle"> - <input id="radio" type="radio" value="ignore2"> - <input id="password" type="password" value="dragon"> - <input id="text" value="my text"> - <input id="placeholder0" placeholder="Phone number"> - <input id="placeholder1" title="Phone number"> - <input id="placeholder2" title="Phone number" placeholder="xxx-yyy-zzzz"> - <input id="placeholder3" title="Phone number" placeholder="xxx-yyy-zzzz" - value="310-555-1212"> - </form> - */}); - - var node = $('hidden'); - assertEquals('', cvox.DomUtil.getName(node)); - node = $('input_img'); - assertEquals('rose Image', cvox.DomUtil.getName(node)); - node = $('input_img_alt'); - assertEquals('flower', cvox.DomUtil.getName(node)); - node = $('submit'); - assertEquals('Submit', cvox.DomUtil.getName(node)); - node = $('submit_withvalue'); - assertEquals('Go', cvox.DomUtil.getName(node)); - node = $('reset'); - assertEquals('Reset', cvox.DomUtil.getName(node)); - node = $('reset_withvalue'); - assertEquals('Stop', cvox.DomUtil.getName(node)); - node = $('button'); - assertEquals('Button', cvox.DomUtil.getName(node)); - node = $('checkbox'); - assertEquals('', cvox.DomUtil.getName(node)); - node = $('checkbox_title'); - assertEquals('toggle', cvox.DomUtil.getName(node)); - node = $('radio'); - assertEquals('', cvox.DomUtil.getName(node)); - node = $('password'); - assertEquals('dot dot dot dot dot dot ', cvox.DomUtil.getValue(node)); - node = $('text'); - assertEquals('my text', cvox.DomUtil.getValue(node)); - node = $('placeholder0'); - assertEquals('Phone number', cvox.DomUtil.getName(node)); - node = $('placeholder1'); - assertEquals('Phone number', cvox.DomUtil.getName(node)); - node = $('placeholder2'); - assertEquals('xxx-yyy-zzzz', - cvox.DomUtil.getName(node)); - node = $('placeholder3'); - assertEquals('310-555-1212 xxx-yyy-zzzz', - cvox.DomUtil.getValue(node) + ' ' + cvox.DomUtil.getName(node)); -}); - - -/** Test checking if something is a control. @export */ -TEST_F('CvoxDomUtilUnitTest', 'IsControl', function() { - this.loadDoc(function() {/*! - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td> </td> - - <td nowrap="nowrap"> - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td bgcolor="#3366CC"><img alt="" width="1" height="1"></td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td bgcolor="#E5ECF9" nowrap="nowrap"><font color="#000000" - face="arial,sans-serif" size="+1"><b> Preferences</b> - </font></td> - - <td align="right" bgcolor="#E5ECF9" nowrap="nowrap"> - <font color="#000000" face="arial,sans-serif" size="-1"> - <a href="http://www.google.com/accounts/ManageAccount">Google - Account settings</a> | <a href="http://www.google.com/"> - Preferences Help</a> | <a href="/about.html">About - Google</a> </font></td> - </tr> - </tbody> - </table> - </td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="2" cellspacing="0"> - <tbody> - <tr bgcolor="#E5ECF9"> - <td><font face="arial,sans-serif" size="-1"><b>Save</b> your - preferences when finished and <b>return to search</b>.</font></td> - - <td align="right"><font face="arial,sans-serif" size="-1"> - <input value="Save Preferences " name="submit2" type="submit"> - </font></td> - </tr> - </tbody> - </table> - - <h1>Global Preferences</h1><font size="-1">(changes apply to all Google - services)</font><br> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td bgcolor="#CBDCED"><img alt="" width="1" height="2"></td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td width="1" bgcolor="#CBDCED"><img alt="" width="2" height="1"></td> - - <td valign="top" width="175" nowrap="nowrap"> - <br> - - - <h2>Interface Language</h2> - </td> - - <td colspan="2"><br> - <font face="arial,sans-serif" size="-1">Display Google tips and - messages in: <select name="hl"> - <option value="af"> - Afrikaans - </option> - - <option value="ak"> - Akan - </option> - - <option value="sq"> - Albanian - </option> - - <option value="am"> - Amharic - </option> - - <option value="ar"> - Arabic - </option> - </select><br> - If you do not find your native language in the pulldown above, you - can<br> - help Google create it through our - <a href="http://services.google.com/">Google in Your Language - program</a>.<br> - </font></td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="1"></td> - </tr> - - <tr> - <td width="1" bgcolor="#CBDCED"><img alt="" width="2" height="1"></td> - - <td valign="top" width="175" nowrap="nowrap"> - <br> - - - <h2>Search Language</h2> - </td> - - <td> - <br> - <font face="arial,sans-serif" size="-1">Prefer pages written in these - language(s):</font><br> - - <table border="0" cellpadding="5" cellspacing="10"> - <tbody> - <tr> - <td valign="top" nowrap="nowrap"><font face="arial,sans-serif" - size="-1"><label><input name="lr" value="lang_af" - onclick="tick()" id="paf" type="checkbox"> - <span id="taf">Afrikaans</span></label><br> - <label><input name="lr" value="lang_ar" onclick="tick()" - id="par" type="checkbox"> <span id="tar">Arabic</span></label> - <br> - <label><input name="lr" value="lang_hy" onclick="tick()" - id="phy" type="checkbox"> <span id="thy">Armenian</span> - </label><br> - <label><input name="lr" value="lang_be" onclick="tick()" - id="pbe" type="checkbox"> <span id="tbe">Belarusian</span> - </label><br> - <label><input name="lr" value="lang_bg" onclick="tick()" - id="pbg" type="checkbox"> <span id="tbg">Bulgarian</span> - </label><br> - <label><input name="lr" value="lang_ca" onclick="tick()" - id="pca" type="checkbox"> <span id="tca">Catalan</span> - </label><br> - <label><input name="lr" value="lang_zh-CN" onclick="tick()" - id="pzh-CN" type="checkbox"> <span id="tzh-CN"> - Chinese (Simplified)</span></label><br> - <label><input name="lr" value="lang_zh-TW" onclick="tick()" - id="pzh-TW" type="checkbox"> <span id="tzh-TW"> - Chinese (Traditional)</span></label><br> - <label><input name="lr" value="lang_hr" onclick="tick()" - id="phr" type="checkbox"> <span id="thr">Croatian</span> - </label><br> - <label><input name="lr" value="lang_cs" onclick="tick()" - id="pcs" type="checkbox"> <span id="tcs">Czech</span> - </label><br> - <label><input name="lr" value="lang_da" onclick="tick()" - id="pda" type="checkbox"> <span id="tda">Danish</span> - </label><br> - <label><input name="lr" value="lang_nl" onclick="tick()" - id="pnl" type="checkbox"> <span id="tnl">Dutch</span> - </label></font></td> - </tr> - </tbody> - </table> - </td> - </tr> - </tbody> - </table><a name="loc" id="loc"></a> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="1"></td> - </tr> - - <tr> - <td width="1" bgcolor="#CBDCED"><img alt="" width="2" height="1"></td> - - <td valign="top" width="175" nowrap="nowrap"> - <br> - - - <h2>Location</h2> - </td> - - <td> - <br> - - <div style="color: rgb(204, 0, 0); display: none;" id="locerr"> - <span id="lem"><font face="arial,sans-serif" size="-1">The location - <b>X</b> was not recognized.</font></span> - <font face="arial,sans-serif" size="-1"><br> - <br> - Suggestions:<br></font> - - <ul> - <li><font face="arial,sans-serif" size="-1">Make sure all street - and city names are spelled correctly.</font></li> - - <li><font face="arial,sans-serif" size="-1">Make sure the address - included a city and state.</font></li> - - <li><font face="arial,sans-serif" size="-1">Try entering a Zip - code.</font></li> - </ul> - </div> - - <div style="color: rgb(204, 0, 0); display: none;" id="locterr"> - <font face="arial,sans-serif" size="-1">Please enter a valid US - city or zip code<br> - <br></font> - </div> - - <div style="color: rgb(204, 0, 0); display: none;" id="locserr"> - <font face="arial,sans-serif" size="-1">Server error. Please try - again.<br> - <br></font> - </div><font face="arial,sans-serif" size="-1">Use as the default - location in Google Maps, customized search results, and other Google - products:<br> - <input name="uulo" value="1" type="hidden"><input name="muul" - value="4_20" type="hidden"><input name="luul" size="60" value="" - type="text"><br> - This location is saved on this computer. - <a href="/support/websearch/bin/answer.py?answer=35892&hl=en"> - Learn more</a><br> - <br></font> - </td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="1"></td> - </tr> - - <tr> - <td rowspan="2" width="1" bgcolor="#CBDCED"> - <img alt="" width="2" height="1"></td> - - <td width="175" nowrap="nowrap"> - <br> - - <h2>SafeSearch Filtering</h2> - </td> - <td><br> - <font face="arial,sans-serif" size="-1"> - <a href="http://www.google.com/"> - Google's SafeSearch</a> blocks web pages containing explicit sexual - content from appearing in search results.</font></td> - </tr> - <tr valign="top"> - <td width="175" nowrap="nowrap"> </td> - <td> - <div style="margin-bottom: 1.2em; font: smaller arial,sans-serif;"> - <input id="stf" name="safeui" value="on" type="radio"> - <label for="stf">Use strict filtering (Filter both explicit - text and explicit images)</label><br> - <input id="modf" name="safeui" value="images" checked="checked" - type="radio"><label for="modf">Use moderate - filtering (Filter explicit images only - default - behavior)</label><br> - <input id="nof" name="safeui" value="off" type="radio"> - <label for="nof">Do not filter my search results</label> - </div> - <p style="margin-bottom: 1.2em; font-size: smaller;">This will apply - strict filtering to all searches from this computer using Firefox. - <a href="http://www.google.com/">Learn more</a></p> - </td> - </tr> - </tbody> - </table> - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="1"></td> - </tr> - - <tr> - <td width="1" bgcolor="#CBDCED"><img alt="" width="2" height="1"></td> - - <td valign="top" width="175" nowrap="nowrap"> - <br> - - - <h2>Number of Results</h2> - </td> - - <td> <br> - <font face="arial,sans-serif" size="-1">Google's default (10 results) - provides the fastest results.<br> - Display <select name="num"> - <option value="10" selected="selected"> - 10 - </option> - - <option value="20"> - 20 - </option> - - <option value="30"> - 30 - </option> - - <option value="50"> - 50 - </option> - - <option value="100"> - 100 - </option> - </select> results per page.<br> - </font></td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="1"></td> - </tr> - - <tr> - <td width="1" bgcolor="#CBDCED"><img alt="" width="2" height="1"></td> - - <td valign="top" width="175" nowrap="nowrap"> - <br> - - - <h2>Results Window</h2><a name="safeui" id="safeui"> </a> - </td> - - <td> <br> - <font face="arial,sans-serif" size="-1"><input id="nwc" name="newwindow" - value="1" type="checkbox"> <label for="nwc">Open - search results in a new browser window.</label></font><br> - </td> - </tr> - </tbody> - </table> - - <table width="100%" border="0" cellpadding="0" cellspacing="0"> - <tbody> - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="1"></td> - </tr> - - <tr> - <td width="1" bgcolor="#CBDCED"><img alt="" width="2" height="1"></td> - - <td valign="top" width="175" nowrap="nowrap"> - <br> - - - <h2>Google Instant</h2> - </td> - - <td> <br> - <font face="arial,sans-serif" size="-1"><input id="suggon" name="suggon" - value="1" checked="checked" type="radio"><label for="suggon">Use Google - Instant predictions and results appear while typing</label><br> - <input id="suggmid" name="suggon" value="2" type="radio"> - <label for="suggmid">Do not use Google Instant</label><br> - <br> - Signed-in users can remove personalized predictions from their - <a href="/history">Web History</a>. <a href="http://www.google.com/"> - Learn more</a><br> - <br> - </font></td> - </tr> - - <tr> - <td colspan="4" bgcolor="#CBDCED"><img alt="" width="1" height="2"></td> - </tr> - </tbody> - </table><br> - */}); - var submitButton = document.getElementsByName('submit2')[0]; - assertEquals(true, cvox.DomUtil.isControl(submitButton)); - var selectControl = document.getElementsByName('hl')[0]; - assertEquals(true, cvox.DomUtil.isControl(selectControl)); - var checkbox = $('paf'); - assertEquals(true, cvox.DomUtil.isControl(checkbox)); - var textInput = document.getElementsByName('luul')[0]; - assertEquals(true, cvox.DomUtil.isControl(textInput)); - var radioButton = $('suggmid'); - assertEquals(true, cvox.DomUtil.isControl(radioButton)); - var h1Elem = document.getElementsByTagName('h1'); - assertEquals(false, cvox.DomUtil.isControl(h1Elem)); -}); - -/** Test if something is an ARIA control. @export */ -TEST_F('CvoxDomUtilUnitTest', 'IsAriaControl', function() { - this.loadDoc(function() {/*! - <li id="cb1" role="checkbox" tabindex="0" aria-checked="false" - aria-describedby="cond desc1"> - Lettuce - </li> - <li id="larger1" role="button" tabindex="0" aria-pressed="false" - aria-labelledby="larger_label">+</li> - <li id="r1" role="radio" tabindex="-1" aria-checked="false">Thai</li> - <li id="treeitem1" role="treeitem" tabindex="-1">Oranges</li> - */}); - var checkbox = $('cb1'); - assertEquals(true, cvox.DomUtil.isControl(checkbox)); - var button = $('larger1'); - assertEquals(true, cvox.DomUtil.isControl(button)); - var radio = $('r1'); - assertEquals(true, cvox.DomUtil.isControl(radio)); - var treeitem = $('treeitem1'); - assertEquals(false, cvox.DomUtil.isControl(treeitem)); -}); - -/** Test if something is an focusable. @export */ -TEST_F('CvoxDomUtilUnitTest', 'IsFocusable', function() { - this.loadDoc(function() {/*! - <a id="focus_link" href="#">Link</a> - <a id="focus_anchor">Unfocusable anchor</a> - <input id="focus_input" value="Input" /> - <select id="focus_select"><option>Select</option></select> - <button id="focus_button1">Button</button> - <button id="focus_button2" tabindex="-1">Button 2</button> - <button id="focus_button3" tabindex="0">Button 3</button> - <button id="focus_button4" tabindex="1">Button 4</button> - <div id="focus_div1">Div</div> - <div id="focus_div2" tabindex="-1">Div 2</div> - <div id="focus_div3" tabindex="0">Div 3</div> - <div id="focus_div4" tabindex="1">Div 4</div> - */}); - var node; - node = $('focus_link'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_anchor'); - assertEquals(false, cvox.DomUtil.isFocusable(node)); - node = $('focus_input'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_select'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_button1'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_button2'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_button3'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_button4'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_div1'); - assertEquals(false, cvox.DomUtil.isFocusable(node)); - node = $('focus_div2'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_div3'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - node = $('focus_div4'); - assertEquals(true, cvox.DomUtil.isFocusable(node)); - - // Test it with null. - assertEquals(false, cvox.DomUtil.isFocusable(null)); - - // Test it with something that's not an element. - assertEquals(false, cvox.DomUtil.isFocusable(new Object())); - - // Test it with a Text node. - node = $('focus_button1').firstChild; - assertEquals(false, cvox.DomUtil.isFocusable(node)); -}); - -/** Some additional tests for getName function. */ -TEST_F('CvoxDomUtilUnitTest', 'GetName', function() { - this.loadDoc(function() {/*! - <span id="test-span" aria-labelledby="fake-id">Some text</span> - <label id="label1">One</label> - <label id="label3">Label</label> - <div id="test-div" aria-labelledby="label1 label2 label3"></div> - */}); - var node = $('test-span'); - // Makes sure we can deal with invalid ids in aria-labelledby. - var text = cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(node)); - assertEquals('Some text', text); - node = $('test-div'); - text = cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(node)); - assertEquals('One Label', cvox.DomUtil.getName(node)); -}); - -/** Test for getLinkURL. */ -TEST_F('CvoxDomUtilUnitTest', 'GetLinkURL', function() { - this.loadDoc(function() {/*! - <a id="l1" name="nohref">Anchor</a> - <a id="l2" href="">Empty link</a> - <a id="l3" href="#">Link to self</a> - <a id="l4" href="http://google.com">Google</a> - <span id="l5" role="link" onClick="javascript:alert('?')">Something</span> - <div id="l6" role="link">Div with link role</a> - */}); - var node = $('l1'); - assertEquals('', cvox.DomUtil.getLinkURL(node)); - node = $('l2'); - assertEquals('', cvox.DomUtil.getLinkURL(node)); - node = $('l3'); - assertEquals('Internal link', cvox.DomUtil.getLinkURL(node)); - node = $('l4'); - assertEquals('http://google.com', cvox.DomUtil.getLinkURL(node)); - node = $('l5'); - assertEquals('Unknown link', cvox.DomUtil.getLinkURL(node)); - node = $('l6'); - assertEquals('Unknown link', cvox.DomUtil.getLinkURL(node)); -}); - -/** Test for isDisabled. */ -TEST_F('CvoxDomUtilUnitTest', 'IsDisabled', function() { - this.loadDoc(function() {/*! - <input id="button1" type="button" value="Press me!"/> - <input id="button2" type="button" value="Don't touch me!" disabled/> - */}); - var node = $('button1'); - assertEquals(false, cvox.DomUtil.isDisabled(node)); - node = $('button2'); - assertEquals(true, cvox.DomUtil.isDisabled(node)); -}); - -/** Test for a tree with aria-expanded attribute. */ -TEST_F('CvoxDomUtilUnitTest', 'Tree', function() { - this.loadDoc(function() {/*! - <div id=":0" role="tree" aria-selected="false" aria-expanded="true" - aria-level="0" aria-labelledby=":0.label" tabindex="0" - aria-activedescendant=":1"> - <span id=":0.label">Countries</span> - <div class="goog-tree-item" id=":1" role="treeitem" aria-selected="true" - aria-expanded="false" aria-labelledby=":1.label" aria-level="1"> - <span id=":1.label">A</span> - </div> - <div class="goog-tree-item" id=":2" role="treeitem" aria-selected="false" - aria-expanded="false" aria-labelledby=":2.label" aria-level="1"> - <span id=":2.label">B<span> - </div> - <div class="goog-tree-item" id=":3" role="treeitem" aria-selected="false" - aria-expanded="true" aria-labelledby=":3.label" aria-level="1"> - <span id=":3.label">C</span> - <div class="goog-tree-children" role="group"> - <div class="goog-tree-item" id=":3a" role="treeitem" - aria-selected="false" aria-expanded="false" - aria-labelledby=":3a.label" aria-level="2"> - <span id=":3a.label">Chile</span> - </div> - <div class="goog-tree-item" id=":3b" role="treeitem" - aria-selected="false" aria-expanded="false" - aria-labelledby=":3b.label" aria-level="2"> - <span id=":3b.label">China</span> - </div> - <div class="goog-tree-item" id=":3c" role="treeitem" - aria-selected="false" aria-expanded="false" - aria-labelledby=":3c.label" aria-level="2"> - <span id=":3c.label">Christmas Island</span> - </div> - <div class="goog-tree-item" id=":3d" role="treeitem" - aria-selected="false" aria-expanded="false" - aria-labelledby=":3d.label" aria-level="2"> - <span id=":3d.label">Cocos (Keeling) Islands</span> - </div> - </div> - </div> - </div> - */}); - var node = $(':0'); - assertEquals('A Collapsed Selected 1 of 3', - cvox.DomUtil.getControlValueAndStateString(node)); - node = $(':1'); - assertEquals('A Collapsed Selected 1 of 3', - cvox.DomUtil.getControlValueAndStateString(node)); - node = $(':2'); - assertEquals('B Collapsed Not selected 2 of 3', - cvox.DomUtil.getControlValueAndStateString(node)); - node = $(':3'); - assertEquals('C Expanded Not selected 3 of 3', - cvox.DomUtil.getControlValueAndStateString(node)); - node = $(':3b'); - assertEquals('China Collapsed Not selected 2 of 4', - cvox.DomUtil.getControlValueAndStateString(node)); -}); - -/** Test for tables with different border specifications */ -TEST_F('CvoxDomUtilUnitTest', 'TableBorders', function() { - this.loadDoc(function() {/*! - <table id=":0" border="1"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":1" border="0"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":2" border="0px"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":3" frame="box"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":4" frame="void"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":5" style="border-width: medium"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":6" style="border-width: medium; border-style: none"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":7" style="border-color: red"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":8" style="border-style: dotted; border-width: 0px"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":9" style="border-width: 0px"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":10" style="border: 0px"> - <tr> - <td>A</td> - </tr> - </table> - <table id=":11" style="border: 0"> - <tr> - <td>A</td> - </tr> - </table> - */}); - var node = $(':0'); - assertTrue(cvox.DomUtil.hasBorder(node)); - - node = $(':1'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':2'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':3'); - assertTrue(cvox.DomUtil.hasBorder(node)); - - node = $(':4'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':5'); - assertTrue(cvox.DomUtil.hasBorder(node)); - - node = $(':6'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':7'); - assertTrue(cvox.DomUtil.hasBorder(node)); - - node = $(':8'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':9'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':10'); - assertFalse(cvox.DomUtil.hasBorder(node)); - - node = $(':11'); - assertFalse(cvox.DomUtil.hasBorder(node)); -}); - -/** Tests for shallowChildlessClone */ -TEST_F('CvoxDomUtilUnitTest', 'ShallowChildlessClone', function() { - this.loadDoc(function() {/*! - <div id='simple'>asdf</div> - <div id='expectedSimpleClone'>asdf</div> - <div id='oneLevel'><div>asdf</div></div> - <div id='expectedOneLevelClone'><div></div></div> - <div id='withAttrs'><div class="asdf">asdf</div></div> - <div id='expectedWithAttrsClone'><div class="asdf"></div></div> - */}); - - var simple = $('simple').firstChild; - var expectedSimpleClone = $('expectedSimpleClone').firstChild; - var oneLevel = $('oneLevel').firstChild; - var expectedOneLevelClone = $('expectedOneLevelClone').firstChild; - var withAttrs = $('withAttrs').firstChild; - var expectedWithAttrsClone = $('expectedWithAttrsClone').firstChild; - - var simpleClone = cvox.DomUtil.shallowChildlessClone(simple); - this.assertEqualsAsText_(simpleClone, expectedSimpleClone); - - var oneLevelClone = cvox.DomUtil.shallowChildlessClone(oneLevel); - this.assertEqualsAsText_(oneLevelClone, expectedOneLevelClone); - - var withAttrsClone = cvox.DomUtil.shallowChildlessClone(withAttrs); - this.assertEqualsAsText_(withAttrsClone, expectedWithAttrsClone); -}); - -/** Tests for deepClone */ -TEST_F('CvoxDomUtilUnitTest', 'DeepClone', function() { - this.loadDoc(function() {/*! - <div id='simple'>asdf</div> - */}); - var simpleClone = cvox.DomUtil.deepClone($('simple')); - this.assertEqualsAsText_(simpleClone, $('simple')); - - this.loadDoc(function() {/*! - <div id="withAttrs" class="asdf">asdf</div> - */}); - var withAttrsClone = cvox.DomUtil.deepClone($('withAttrs')); - this.assertEqualsAsText_(withAttrsClone, $('withAttrs')); -}); - -/** Tests for findNode */ -TEST_F('CvoxDomUtilUnitTest', 'FindNode', function() { - this.loadDoc(function() {/*! - <div id="root"> - <p id="a">a</p> - <a href="#" id="b">b</a> - </div> - */}); - var f = cvox.DomUtil.findNode; - var node = f($('root'), function(n) {return n.id == 'b';}); - assertEquals('b', node.id); -}); - -/** Tests for getState for a list */ -TEST_F('CvoxDomUtilUnitTest', 'ListLength', function() { - this.loadDoc(function() {/*! - <ul id="ul1"> - <li>A - <li>B - <li>C - </ul> - <ul id="ul2"> - <li aria-setsize="10">A - <li aria-setsize="10">B - <li aria-setsize="10">C - </ul> - */}); - var ul1 = $('ul1'); - assertEquals('with 3 items', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getState(ul1))); - - var ul2 = $('ul2'); - assertEquals('with 10 items', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getState(ul2))); -}); - -/** Tests for hasLongDesc */ -TEST_F('CvoxDomUtilUnitTest', 'HasLongDesc', function() { - this.loadDoc(function() {/*! - <img id="img0" longdesc="desc.html" src="img0.jpg"></img> - <img id="img1" src="img1.jpg"></img> - */}); - var img0 = $('img0'); - assertEquals(true, cvox.DomUtil.hasLongDesc(img0)); - - var img1 = $('img1'); - assertEquals(false, cvox.DomUtil.hasLongDesc(img1)); -}); - -/** Tests for various link leaf types. */ -TEST_F('CvoxDomUtilUnitTest', 'LinkLeaf', function() { - this.loadDoc(function() {/*! - <a id='leaf' href='google.com'><strong>Click</strong><div>here</div></a> - <a id='non-leaf' href='google.com'>Click <h2>here</h2></a> - */}); - var leaf = $('leaf'); - var nonLeaf = $('non-leaf'); - assertTrue(cvox.DomUtil.isLeafNode(leaf)); - assertFalse(cvox.DomUtil.isLeafNode(nonLeaf)); -}); - - -/** Test the value and state of a multiple select. */ -TEST_F('CvoxDomUtilUnitTest', 'MultipleSelectValue', function() { - this.loadDoc(function() {/*! - <select id='cars' multiple> - <option value="volvo">Volvo</option> - <option value="saab">Saab</option> - <option value="opel" selected>Opel</option> - <option value="audi" selected>Audi</option> - </select> - */}); - var cars = $('cars'); - assertEquals('Opel to Audi', cvox.DomUtil.getValue(cars)); - assertEquals('selected 2 items', cvox.DomUtil.getState(cars)); -}); - - -/** - * Test correctness of elementToPoint. - * - * Absolute positioning of the container is used to avoid the window of the - * browser being too small to contain the test elements. - */ -TEST_F('CvoxDomUtilUnitTest', 'ElementToPoint', function() { - this.loadDoc(function() {/*! - <div style="position: absolute; top: 0; left: 0"> - <a id='one' href='#a'>First</a> - <p id='two'>Some text</p> - <ul><li id='three'>LI</li><li>LI2</li></ul> - </div> - */}); - var one = $('one'); - var two = $('two'); - var three = $('three'); - - var oneHitPoint = cvox.DomUtil.elementToPoint(one); - var twoHitPoint = cvox.DomUtil.elementToPoint(two); - var threeHitPoint = cvox.DomUtil.elementToPoint(three); - - assertEquals(one, document.elementFromPoint(oneHitPoint.x, oneHitPoint.y)); - assertEquals(two, document.elementFromPoint(twoHitPoint.x, twoHitPoint.y)); - assertEquals(three, - document.elementFromPoint(threeHitPoint.x, threeHitPoint.y)); -}); - -/** Tests we compute the correct name for hidden aria labelledby nodes. */ -TEST_F('CvoxDomUtilUnitTest', 'HiddenAriaLabelledby', function() { - this.loadDoc(function() {/*! - <span id="acc_name" style="display: none"> - hello world! - </span> - <button id="button" aria-labelledby="acc_name"> - */}); - assertEquals('hello world!', - cvox.DomUtil.getName($('button'))); -}); - -/** Tests that we compute the correct state for accesskeys. */ -TEST_F('CvoxDomUtilUnitTest', 'AccessKey', function() { - this.loadDoc(function() {/*! - <a id='accessKey' href="#f" title="Next page" accesskey="n">Next page</a> - */}); - var a = $('accessKey'); - assertEquals('has access key, n', cvox.DomUtil.getState(a)); -}); - - -/** Tests that we compute the correct name for ordered listitems. */ -TEST_F('CvoxDomUtilUnitTest', 'OrderedListitem', function() { - this.loadDoc(function() {/*! - <ol id="fruits_ol"> - <li id='ol_li1'>apple - <li id='ol_li2'>orange - <li id='ol_li3'>strawberry - <li id='ol_li4'>banana - </ol> - */}); - var li1 = $('ol_li1'); - var li2 = $('ol_li2'); - var li3 = $('ol_li3'); - var li4 = $('ol_li4'); - // Note that whitespace processing happens at a higher layer - // (DescriptionUtil). - assertEquals('1. apple', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li1))); - assertEquals('2. orange', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li2))); - assertEquals('3. strawberry', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li3))); - assertEquals('4. banana', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li4))); - - $('fruits_ol').style.listStyleType = 'lower-latin'; - - assertEquals('A. apple', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li1))); - assertEquals('B. orange', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li2))); - assertEquals('C. strawberry', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li3))); - assertEquals('D. banana', - cvox.DomUtil.collapseWhitespace(cvox.DomUtil.getName(li4))); -}); - -/** Tests a node with title, and textContent containing only whitespace. */ -TEST_F('CvoxDomUtilUnitTest', 'TitleOverridesInnerWhitespace', function() { - this.loadDoc(function() {/*! - <button id="btn1" title="Remove from Chrome"> - <span class="lid"></span> - <span class="can"></span> - </button> - */}); - var btn1 = $('btn1'); - assertEquals('Remove from Chrome', cvox.DomUtil.getName(btn1)); -}); - -/** Test memoization. **/ -TEST_F('CvoxDomUtilUnitTest', 'Memoization', function() { - this.loadDoc(function() {/*! - <div id="container"> - </div> - */}); - - // Nest divs 100 levels deep. - var container = $('container'); - var outer = container; - for (var i = 0; i < 100; i++) { - var inner = document.createElement('div'); - outer.appendChild(inner); - outer = inner; - } - var target = document.createElement('p'); - target.innerHTML = 'Text'; - outer.appendChild(target); - - var iterations = 200; - - function logTime(msg, fn) { - var t0 = new Date(); - fn(); - console.log(msg + ' elapsed time: ' + (new Date() - t0) + ' ms'); - } - - // First, test without memoization. - logTime('No memoization', function() { - container.style.visibility = 'hidden'; - for (var i = 0; i < iterations; i++) { - assertFalse(cvox.DomUtil.isVisible(target)); - } - container.style.visibility = 'visible'; - for (var i = 0; i < iterations; i++) { - assertTrue(cvox.DomUtil.isVisible(target)); - } - }); - - // Now test with memoization enabled. - logTime('With memoization', function() { - cvox.Memoize.scope(function() { - container.style.visibility = 'hidden'; - for (var i = 0; i < iterations; i++) { - assertFalse(cvox.DomUtil.isVisible(target)); - } - }); - cvox.Memoize.scope(function() { - container.style.visibility = 'visible'; - for (var i = 0; i < iterations; i++) { - assertTrue(cvox.DomUtil.isVisible(target)); - } - }); - }); - - // Finally as a sanity check that things are being memoized, turn on - // memoization and show that we get the wrong result if we change the - // DOM and call isVisible again. - cvox.Memoize.scope(function() { - container.style.visibility = 'hidden'; - assertFalse(cvox.DomUtil.isVisible(target)); - - container.style.visibility = 'visible'; - // This should be true! It will return the wrong answer because - // we're deliberately leaving memoization on while modifying the DOM. - assertFalse(cvox.DomUtil.isVisible(target)); - }); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/earcon_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/earcon_util.js deleted file mode 100644 index 51679ab007e..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/earcon_util.js +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Earcon utils. - */ - -goog.provide('cvox.EarconUtil'); - -goog.require('cvox.AbstractEarcons'); -goog.require('cvox.AriaUtil'); -goog.require('cvox.DomUtil'); - -/** - * Returns the id of an earcon to play along with the description for a node. - * - * @param {Node} node The node to get the earcon for. - * @return {cvox.Earcon?} The earcon id, or null if none applies. - */ -cvox.EarconUtil.getEarcon = function(node) { - var earcon = cvox.AriaUtil.getEarcon(node); - if (earcon != null) { - return earcon; - } - - switch (node.tagName) { - case 'BUTTON': - return cvox.Earcon.BUTTON; - case 'A': - if (node.hasAttribute('href')) { - return cvox.Earcon.LINK; - } - break; - case 'IMG': - if (cvox.DomUtil.hasLongDesc(node)) { - return cvox.Earcon.LONG_DESC; - } - break; - case 'LI': - return cvox.Earcon.LIST_ITEM; - case 'SELECT': - return cvox.Earcon.LISTBOX; - case 'TEXTAREA': - return cvox.Earcon.EDITABLE_TEXT; - case 'INPUT': - switch (node.type) { - case 'button': - case 'submit': - case 'reset': - return cvox.Earcon.BUTTON; - case 'checkbox': - case 'radio': - if (node.checked) { - return cvox.Earcon.CHECK_ON; - } else { - return cvox.Earcon.CHECK_OFF; - } - default: - if (cvox.DomUtil.isInputTypeText(node)) { - // 'text', 'password', etc. - return cvox.Earcon.EDITABLE_TEXT; - } - } - } - return null; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text.js deleted file mode 100644 index 9d116d24ed4..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text.js +++ /dev/null @@ -1,649 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.ChromeVoxEditableContentEditable'); -goog.provide('cvox.ChromeVoxEditableElement'); -goog.provide('cvox.ChromeVoxEditableHTMLInput'); -goog.provide('cvox.ChromeVoxEditableTextArea'); -goog.provide('cvox.TextHandlerInterface'); - - -goog.require('cvox.BrailleTextHandler'); -goog.require('cvox.ChromeVoxEditableTextBase'); -goog.require('cvox.ContentEditableExtractor'); -goog.require('cvox.DomUtil'); -goog.require('cvox.EditableTextAreaShadow'); -goog.require('cvox.TextChangeEvent'); -goog.require('cvox.TtsInterface'); - -/** - * @fileoverview Gives the user spoken and braille feedback as they type, - * select text, and move the cursor in editable HTML text controls, including - * multiline controls and contenteditable regions. - * - * The two subclasses, ChromeVoxEditableHTMLInput and - * ChromeVoxEditableTextArea, take a HTML input (type=text) or HTML - * textarea node (respectively) in the constructor, and automatically - * handle retrieving the current state of the control, including - * computing line break information for a textarea using an offscreen - * shadow object. It is the responsibility of the user of these classes to - * trap key and focus events and call the update method as needed. - * - */ - - -/** - * An interface for being notified when the text changes. - * @interface - */ -cvox.TextHandlerInterface = function() {}; - - -/** - * Called when text changes. - * @param {cvox.TextChangeEvent} evt The text change event. - */ -cvox.TextHandlerInterface.prototype.changed = function(evt) {}; - - -/** - * A subclass of ChromeVoxEditableTextBase a text element that's part of - * the webpage DOM. Contains common code shared by both EditableHTMLInput - * and EditableTextArea, but that might not apply to a non-DOM text box. - * @param {Element} node A DOM node which allows text input. - * @param {string} value The string value of the editable text control. - * @param {number} start The 0-based start cursor/selection index. - * @param {number} end The 0-based end cursor/selection index. - * @param {boolean} isPassword Whether the text control if a password field. - * @param {cvox.TtsInterface} tts A TTS object. - * @extends {cvox.ChromeVoxEditableTextBase} - * @constructor - */ -cvox.ChromeVoxEditableElement = function(node, value, start, end, isPassword, - tts) { - goog.base(this, value, start, end, isPassword, tts); - - /** - * An optional handler for braille output. - * @type {cvox.BrailleTextHandler|undefined} - * @private - */ - this.brailleHandler_ = cvox.ChromeVox.braille ? - new cvox.BrailleTextHandler(cvox.ChromeVox.braille) : undefined; - - /** - * The DOM node which allows text input. - * @type {Element} - * @protected - */ - this.node = node; - - /** - * True if the description was just spoken. - * @type {boolean} - * @private - */ - this.justSpokeDescription_ = false; -}; -goog.inherits(cvox.ChromeVoxEditableElement, - cvox.ChromeVoxEditableTextBase); - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.changed = function(evt) { - // Ignore changes to the cursor and selection if they happen immediately - // after the description was just spoken. This avoid double-speaking when, - // for example, a text field is focused and then a moment later the - // contents are selected. If the value changes, though, this change will - // not be ignored. - if (this.justSpokeDescription_ && this.value == evt.value) { - this.value = evt.value; - this.start = evt.start; - this.end = evt.end; - this.justSpokeDescription_ = false; - } - goog.base(this, 'changed', evt); - if (this.lastChangeDescribed) { - this.brailleCurrentLine_(); - } -}; - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.speak = function( - str, opt_triggeredByUser, opt_personality) { - // If there is a node associated with the editable text object, - // make sure that node has focus before speaking it. - if (this.node && (document.activeElement != this.node)) { - return; - } - goog.base(this, 'speak', str, opt_triggeredByUser, opt_personality); -}; - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.moveCursorToNextCharacter = function() { - var node = this.node; - node.selectionEnd++; - node.selectionStart = node.selectionEnd; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.moveCursorToPreviousCharacter = - function() { - var node = this.node; - node.selectionStart--; - node.selectionEnd = node.selectionStart; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.moveCursorToNextWord = function() { - var node = this.node; - var length = node.value.length; - var re = /\W+/gm; - var substring = node.value.substring(node.selectionEnd); - var match = re.exec(substring); - if (match !== null && match.index == 0) { - // Ignore word-breaking sequences right next to the cursor. - match = re.exec(substring); - } - var index = (match === null) ? length : match.index + node.selectionEnd; - node.selectionStart = node.selectionEnd = index; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.moveCursorToPreviousWord = function() { - var node = this.node; - var length = node.value.length; - var re = /\W+/gm; - var substring = node.value.substring(0, node.selectionStart); - var index = 0; - while (re.exec(substring) !== null) { - if (re.lastIndex < node.selectionStart) { - index = re.lastIndex; - } - } - node.selectionStart = node.selectionEnd = index; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.moveCursorToNextParagraph = - function() { - var node = this.node; - var length = node.value.length; - var index = node.selectionEnd >= length ? length : - node.value.indexOf('\n', node.selectionEnd); - if (index < 0) { - index = length; - } - node.selectionStart = node.selectionEnd = index + 1; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableElement.prototype.moveCursorToPreviousParagraph = - function() { - var node = this.node; - var index = node.selectionStart <= 0 ? 0 : - node.value.lastIndexOf('\n', node.selectionStart - 2) + 1; - if (index < 0) { - index = 0; - } - node.selectionStart = node.selectionEnd = index; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - -/** - * Shows the current line on the braille display. - * @private - */ -cvox.ChromeVoxEditableElement.prototype.brailleCurrentLine_ = function() { - if (this.brailleHandler_) { - var lineIndex = this.getLineIndex(this.start); - var line = this.getLine(lineIndex); - // Collapsable whitespace inside the contenteditable is represented - // as non-breaking spaces. This confuses braille input (which relies on - // the text being added to be the same as the text in the input field). - // Since the non-breaking spaces are just an artifact of how - // contenteditable is implemented, normalize to normal spaces instead. - if (this instanceof cvox.ChromeVoxEditableContentEditable) { - line = line.replace(/\u00A0/g, ' '); - } - var lineStart = this.getLineStart(lineIndex); - var start = this.start - lineStart; - var end = Math.min(this.end - lineStart, line.length); - this.brailleHandler_.changed(line, start, end, this.multiline, this.node, - lineStart); - } -}; - -/******************************************/ - - -/** - * A subclass of ChromeVoxEditableElement for an HTMLInputElement. - * @param {HTMLInputElement} node The HTMLInputElement node. - * @param {cvox.TtsInterface} tts A TTS object. - * @extends {cvox.ChromeVoxEditableElement} - * @implements {cvox.TextHandlerInterface} - * @constructor - */ -cvox.ChromeVoxEditableHTMLInput = function(node, tts) { - this.node = node; - this.setup(); - goog.base(this, - node, - node.value, - node.selectionStart, - node.selectionEnd, - node.type === 'password', - tts); -}; -goog.inherits(cvox.ChromeVoxEditableHTMLInput, - cvox.ChromeVoxEditableElement); - - -/** - * Performs setup for this input node. - * This accounts for exception-throwing behavior introduced by crbug.com/324360. - * @override - */ -cvox.ChromeVoxEditableHTMLInput.prototype.setup = function() { - if (!this.node) { - return; - } - if (!cvox.DomUtil.doesInputSupportSelection(this.node)) { - this.originalType = this.node.type; - this.node.type = 'text'; - } -}; - - -/** - * Performs teardown for this input node. - * This accounts for exception-throwing behavior introduced by crbug.com/324360. - * @override - */ -cvox.ChromeVoxEditableHTMLInput.prototype.teardown = function() { - if (this.node && this.originalType) { - this.node.type = this.originalType; - } -}; - - -/** - * Update the state of the text and selection and describe any changes as - * appropriate. - * - * @param {boolean} triggeredByUser True if this was triggered by a user action. - */ -cvox.ChromeVoxEditableHTMLInput.prototype.update = function(triggeredByUser) { - var newValue = this.node.value; - var textChangeEvent = new cvox.TextChangeEvent(newValue, - this.node.selectionStart, - this.node.selectionEnd, - triggeredByUser); - this.changed(textChangeEvent); -}; - - -/******************************************/ - - -/** - * A subclass of ChromeVoxEditableElement for an HTMLTextAreaElement. - * @param {HTMLTextAreaElement} node The HTMLTextAreaElement node. - * @param {cvox.TtsInterface} tts A TTS object. - * @extends {cvox.ChromeVoxEditableElement} - * @implements {cvox.TextHandlerInterface} - * @constructor - */ -cvox.ChromeVoxEditableTextArea = function(node, tts) { - goog.base(this, node, node.value, node.selectionStart, node.selectionEnd, - false /* isPassword */, tts); - this.multiline = true; - - /** - * True if the shadow is up-to-date with the current value of this text area. - * @type {boolean} - * @private - */ - this.shadowIsCurrent_ = false; -}; -goog.inherits(cvox.ChromeVoxEditableTextArea, - cvox.ChromeVoxEditableElement); - - -/** - * An offscreen div used to compute the line numbers. A single div is - * shared by all instances of the class. - * @type {!cvox.EditableTextAreaShadow|undefined} - * @private - */ -cvox.ChromeVoxEditableTextArea.shadow_; - - -/** - * Update the state of the text and selection and describe any changes as - * appropriate. - * - * @param {boolean} triggeredByUser True if this was triggered by a user action. - */ -cvox.ChromeVoxEditableTextArea.prototype.update = function(triggeredByUser) { - if (this.node.value != this.value) { - this.shadowIsCurrent_ = false; - } - var textChangeEvent = new cvox.TextChangeEvent(this.node.value, - this.node.selectionStart, this.node.selectionEnd, triggeredByUser); - this.changed(textChangeEvent); -}; - - -/** - * Get the line number corresponding to a particular index. - * @param {number} index The 0-based character index. - * @return {number} The 0-based line number corresponding to that character. - */ -cvox.ChromeVoxEditableTextArea.prototype.getLineIndex = function(index) { - return this.getShadow().getLineIndex(index); -}; - - -/** - * Get the start character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the first character in this line. - */ -cvox.ChromeVoxEditableTextArea.prototype.getLineStart = function(index) { - return this.getShadow().getLineStart(index); -}; - - -/** - * Get the end character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the end of this line. - */ -cvox.ChromeVoxEditableTextArea.prototype.getLineEnd = function(index) { - return this.getShadow().getLineEnd(index); -}; - - -/** - * Update the shadow object, an offscreen div used to compute line numbers. - * @return {!cvox.EditableTextAreaShadow} The shadow object. - */ -cvox.ChromeVoxEditableTextArea.prototype.getShadow = function() { - var shadow = cvox.ChromeVoxEditableTextArea.shadow_; - if (!shadow) { - shadow = cvox.ChromeVoxEditableTextArea.shadow_ = - new cvox.EditableTextAreaShadow(); - } - if (!this.shadowIsCurrent_) { - shadow.update(this.node); - this.shadowIsCurrent_ = true; - } - return shadow; -}; - - -/** @override */ -cvox.ChromeVoxEditableTextArea.prototype.moveCursorToNextLine = function() { - var node = this.node; - var length = node.value.length; - if (node.selectionEnd >= length) { - return false; - } - var shadow = this.getShadow(); - var lineIndex = shadow.getLineIndex(node.selectionEnd); - var lineStart = shadow.getLineStart(lineIndex); - var offset = node.selectionEnd - lineStart; - var lastLine = (length == 0) ? 0 : shadow.getLineIndex(length - 1); - var newCursorPosition = (lineIndex >= lastLine) ? length : - Math.min(shadow.getLineStart(lineIndex + 1) + offset, - shadow.getLineEnd(lineIndex + 1)); - node.selectionStart = node.selectionEnd = newCursorPosition; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableTextArea.prototype.moveCursorToPreviousLine = function() { - var node = this.node; - if (node.selectionStart <= 0) { - return false; - } - var shadow = this.getShadow(); - var lineIndex = shadow.getLineIndex(node.selectionStart); - var lineStart = shadow.getLineStart(lineIndex); - var offset = node.selectionStart - lineStart; - var newCursorPosition = (lineIndex <= 0) ? 0 : - Math.min(shadow.getLineStart(lineIndex - 1) + offset, - shadow.getLineEnd(lineIndex - 1)); - node.selectionStart = node.selectionEnd = newCursorPosition; - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/******************************************/ - - -/** - * A subclass of ChromeVoxEditableElement for elements that are contentEditable. - * This is also used for a region of HTML with the ARIA role of "textbox", - * so that an author can create a pure-JavaScript editable text object - this - * will work the same as contentEditable as long as the DOM selection is - * updated properly within the textbox when it has focus. - * @param {Element} node The root contentEditable node. - * @param {cvox.TtsInterface} tts A TTS object. - * @extends {cvox.ChromeVoxEditableElement} - * @implements {cvox.TextHandlerInterface} - * @constructor - */ -cvox.ChromeVoxEditableContentEditable = function(node, tts) { - goog.base(this, node, '', 0, 0, false /* isPassword */, tts); - - - /** - * True if the ContentEditableExtractor is current with this field's data. - * @type {boolean} - * @private - */ - this.extractorIsCurrent_ = false; - - var extractor = this.getExtractor(); - this.value = extractor.getText(); - this.start = extractor.getStartIndex(); - this.end = extractor.getEndIndex(); - this.multiline = true; -}; -goog.inherits(cvox.ChromeVoxEditableContentEditable, - cvox.ChromeVoxEditableElement); - -/** - * A helper used to compute the line numbers. A single object is - * shared by all instances of the class. - * @type {!cvox.ContentEditableExtractor|undefined} - * @private - */ -cvox.ChromeVoxEditableContentEditable.extractor_; - - -/** - * Update the state of the text and selection and describe any changes as - * appropriate. - * - * @param {boolean} triggeredByUser True if this was triggered by a user action. - */ -cvox.ChromeVoxEditableContentEditable.prototype.update = - function(triggeredByUser) { - this.extractorIsCurrent_ = false; - var textChangeEvent = new cvox.TextChangeEvent( - this.getExtractor().getText(), - this.getExtractor().getStartIndex(), - this.getExtractor().getEndIndex(), - triggeredByUser); - this.changed(textChangeEvent); -}; - - -/** - * Get the line number corresponding to a particular index. - * @param {number} index The 0-based character index. - * @return {number} The 0-based line number corresponding to that character. - */ -cvox.ChromeVoxEditableContentEditable.prototype.getLineIndex = function(index) { - return this.getExtractor().getLineIndex(index); -}; - - -/** - * Get the start character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the first character in this line. - */ -cvox.ChromeVoxEditableContentEditable.prototype.getLineStart = function(index) { - return this.getExtractor().getLineStart(index); -}; - - -/** - * Get the end character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the end of this line. - */ -cvox.ChromeVoxEditableContentEditable.prototype.getLineEnd = function(index) { - return this.getExtractor().getLineEnd(index); -}; - - -/** - * Update the extractor object, an offscreen div used to compute line numbers. - * @return {!cvox.ContentEditableExtractor} The extractor object. - */ -cvox.ChromeVoxEditableContentEditable.prototype.getExtractor = function() { - var extractor = cvox.ChromeVoxEditableContentEditable.extractor_; - if (!extractor) { - extractor = cvox.ChromeVoxEditableContentEditable.extractor_ = - new cvox.ContentEditableExtractor(); - } - if (!this.extractorIsCurrent_) { - extractor.update(this.node); - this.extractorIsCurrent_ = true; - } - return extractor; -}; - - -/** @override */ -cvox.ChromeVoxEditableContentEditable.prototype.changed = - function(evt) { - if (!evt.triggeredByUser) { - return; - } - // Take over here if we can't describe a change; assume it's a blank line. - if (!this.shouldDescribeChange(evt)) { - this.speak(Msgs.getMsg('text_box_blank'), true); - if (this.brailleHandler_) { - this.brailleHandler_.changed('' /*line*/, 0 /*start*/, 0 /*end*/, - true /*multiline*/, null /*element*/, - evt.start /*lineStart*/); - } - } else { - goog.base(this, 'changed', evt); - } -}; - - -/** @override */ -cvox.ChromeVoxEditableContentEditable.prototype.moveCursorToNextCharacter = - function() { - window.getSelection().modify('move', 'forward', 'character'); - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableContentEditable.prototype.moveCursorToPreviousCharacter = - function() { - window.getSelection().modify('move', 'backward', 'character'); - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** @override */ -cvox.ChromeVoxEditableContentEditable.prototype.moveCursorToNextParagraph = - function() { - window.getSelection().modify('move', 'forward', 'paragraph'); - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - -/** @override */ -cvox.ChromeVoxEditableContentEditable.prototype.moveCursorToPreviousParagraph = - function() { - window.getSelection().modify('move', 'backward', 'paragraph'); - cvox.ChromeVoxEventWatcher.handleTextChanged(true); - return true; -}; - - -/** - * @override - */ -cvox.ChromeVoxEditableContentEditable.prototype.shouldDescribeChange = - function(evt) { - var sel = window.getSelection(); - var cursor = new cvox.Cursor(sel.baseNode, sel.baseOffset, ''); - - // This is a very specific work around because of our buggy content editable - // support. Blank new lines are not captured in the line indexing data - // structures. - // Scenario: given a piece of text like: - // - // Some Title - // - // Description - // Footer - // - // The new lines after Title are not traversed to by TraverseUtil. A root fix - // would make changes there. However, considering the fickle nature of that - // code, we specifically detect for new lines here. - if (Math.abs(this.start - evt.start) != 1 && - this.start == this.end && - evt.start == evt.end && - sel.baseNode == sel.extentNode && - sel.baseOffset == sel.extentOffset && - sel.baseNode.nodeType == Node.ELEMENT_NODE && - sel.baseNode.querySelector('BR') && - cvox.TraverseUtil.forwardsChar(cursor, [], [])) { - // This case detects if the range selection surrounds a new line, - // but there is still content after the new line (like the example - // above after "Title"). In these cases, we "pretend" we're the - // last character so we speak "blank". - return false; - } - - // Otherwise, we should never speak "blank" no matter what (even if - // we're at the end of a content editable). - return true; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow.js deleted file mode 100644 index bd381156c69..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow.js +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Defines the EditableTextAreaShadow class. - */ - -goog.provide('cvox.EditableTextAreaShadow'); - -/** - * Creates a shadow element for an editable text area used to compute line - * numbers. - * @constructor - */ -cvox.EditableTextAreaShadow = function() { - /** - * @type {Element} - * @private - */ - this.shadowElement_ = document.createElement('div'); - - /** - * Map from line index to a data structure containing the start - * and end index within the line. - * @type {Object<number, {startIndex: number, endIndex: number}>} - * @private - */ - this.lines_ = {}; - - /** - * Map from 0-based character index to 0-based line index. - * @type {Array<number>} - * @private - */ - this.characterToLineMap_ = []; -}; - -/** - * Update the shadow element. - * @param {Element} element The textarea element. - */ -cvox.EditableTextAreaShadow.prototype.update = function(element) { - document.body.appendChild(this.shadowElement_); - - while (this.shadowElement_.childNodes.length) { - this.shadowElement_.removeChild(this.shadowElement_.childNodes[0]); - } - this.shadowElement_.style.cssText = - window.getComputedStyle(element, null).cssText; - this.shadowElement_.style.position = 'absolute'; - this.shadowElement_.style.top = -9999; - this.shadowElement_.style.left = -9999; - this.shadowElement_.setAttribute('aria-hidden', 'true'); - - // Add the text to the shadow element, but with an extra character to the - // end so that we can get the bounding box of the last line - we can't - // measure blank lines otherwise. - var text = element.value; - var textNode = document.createTextNode(text + '.'); - this.shadowElement_.appendChild(textNode); - - /** - * For extra speed, try to skip this many characters at a time - if - * none of the characters are newlines and they're all at the same - * vertical position, we don't have to examine each one. If not, - * fall back to moving by one character at a time. - * @const - */ - var SKIP = 8; - - /** - * Map from line index to a data structure containing the start - * and end index within the line. - * @type {Object<number, {startIndex: number, endIndex: number}>} - */ - var lines = {0: {startIndex: 0, endIndex: 0}}; - - var range = document.createRange(); - var offset = 0; - var lastGoodOffset = 0; - var lineIndex = 0; - var lastBottom = null; - var nearNewline = false; - var rect; - while (offset <= text.length) { - range.setStart(textNode, offset); - - // If we're near the end or if there's an explicit newline character, - // don't even try to skip. - if (offset + SKIP > text.length || - text.substr(offset, SKIP).indexOf('\n') >= 0) { - nearNewline = true; - } - - if (nearNewline) { - // Move by one character. - offset++; - range.setEnd(textNode, offset); - rect = range.getBoundingClientRect(); - } else { - // Try to move by |SKIP| characters. - range.setEnd(textNode, offset + SKIP); - rect = range.getBoundingClientRect(); - if (rect.bottom == lastBottom) { - // Great, they all seem to be on the same line. - offset += SKIP; - } else { - // Nope, there might be a newline, better go one at a time to be safe. - if (rect && lastBottom !== null) { - nearNewline = true; - } - offset++; - range.setEnd(textNode, offset); - rect = range.getBoundingClientRect(); - } - } - - if (offset > 0 && text[offset - 1] == '\n') { - // Handle an explicit newline character - that always results in - // a new line. - lines[lineIndex].endIndex = offset - 1; - lineIndex++; - lines[lineIndex] = {startIndex: offset, endIndex: offset}; - lastBottom = null; - nearNewline = false; - lastGoodOffset = offset; - } else if (rect && (lastBottom === null)) { - // This is the first character we've successfully measured on this - // line. Save the vertical position but don't do anything else. - lastBottom = rect.bottom; - } else if (rect && rect.bottom != lastBottom) { - // This character is at a different vertical position, so place an - // implicit newline immediately after the *previous* good character - // we found (which we now know was the last character of the previous - // line). - lines[lineIndex].endIndex = lastGoodOffset; - lineIndex++; - lines[lineIndex] = {startIndex: lastGoodOffset, endIndex: lastGoodOffset}; - lastBottom = rect ? rect.bottom : null; - nearNewline = false; - } - - if (rect) { - lastGoodOffset = offset; - } - } - // Finish up the last line. - lines[lineIndex].endIndex = text.length; - - // Create a map from character index to line number. - var characterToLineMap = []; - for (var i = 0; i <= lineIndex; i++) { - for (var j = lines[i].startIndex; j <= lines[i].endIndex; j++) { - characterToLineMap[j] = i; - } - } - - // Finish updating fields and remove the shadow element. - this.characterToLineMap_ = characterToLineMap; - this.lines_ = lines; - document.body.removeChild(this.shadowElement_); -}; - -/** - * Get the line number corresponding to a particular index. - * @param {number} index The 0-based character index. - * @return {number} The 0-based line number corresponding to that character. - */ -cvox.EditableTextAreaShadow.prototype.getLineIndex = function(index) { - return this.characterToLineMap_[index]; -}; - -/** - * Get the start character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the first character in this line. - */ -cvox.EditableTextAreaShadow.prototype.getLineStart = function(index) { - return this.lines_[index].startIndex; -}; - -/** - * Get the end character index of a line. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the end of this line. - */ -cvox.EditableTextAreaShadow.prototype.getLineEnd = function(index) { - return this.lines_[index].endIndex; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs deleted file mode 100644 index ce874225246..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_area_shadow_test.unitjs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxShadowUnitTest() {} - -CvoxShadowUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.EditableTextAreaShadow' - ] -}; - -TEST_F('CvoxShadowUnitTest', 'MultilineLines', function() { - this.loadDoc(function() {/*! - <div><textarea id="area"> -one - -two - -three -</textarea></div> */}); - - var area = $('area'); - - var shadow = new cvox.EditableTextAreaShadow(); - shadow.update(area); - assertEquals(0, shadow.getLineIndex(0)); - assertEquals(0, shadow.getLineIndex(3)); - assertEquals(1, shadow.getLineIndex(4)); - assertEquals(2, shadow.getLineIndex(5)); - assertEquals(2, shadow.getLineIndex(8)); - assertEquals(3, shadow.getLineIndex(9)); - assertEquals(4, shadow.getLineIndex(10)); - assertEquals(4, shadow.getLineIndex(14)); -}); - -/** - * Test the get line of a multiline textarea with wrapping instead of - * explicit newlines. - * Test disabled due to not being reliable if font size changes. - * See https://codereview.chromium.org/549303004/ - */ -TEST_F('CvoxShadowUnitTest', 'DISABLED_MultilineWrap', function() { - this.loadDoc(function() {/*! - <div><textarea id="area" - cols=4 rows=20>One two thr fou fiv six sev eig</textarea> - </div> */}); - - var area = $('area'); - - var shadow = new cvox.EditableTextAreaShadow(); - shadow.update(area); - for (var i = 0; i < 32; i++) { - assertEquals(Math.floor(i / 4), shadow.getLineIndex(i)); - } -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js deleted file mode 100644 index e1f3c27ba38..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_base.js +++ /dev/null @@ -1,712 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.ChromeVoxEditableTextBase'); -goog.provide('cvox.TextChangeEvent'); -goog.provide('cvox.TypingEcho'); - -goog.require('cvox.AbstractTts'); -goog.require('cvox.ChromeVox'); -goog.require('cvox.TtsInterface'); -goog.require('goog.i18n.MessageFormat'); - - -/** - * @fileoverview Generalized logic for providing spoken feedback when editing - * text fields, both single and multiline fields. - * - * {@code ChromeVoxEditableTextBase} is a generalized class that takes the - * current state in the form of a text string, a cursor start location and a - * cursor end location, and calls a speak method with the resulting text to - * be spoken. This class can be used directly for single line fields or - * extended to override methods that extract lines for multiline fields - * or to provide other customizations. - */ - - -/** - * A class containing the information needed to speak - * a text change event to the user. - * - * @constructor - * @param {string} newValue The new string value of the editable text control. - * @param {number} newStart The new 0-based start cursor/selection index. - * @param {number} newEnd The new 0-based end cursor/selection index. - * @param {boolean} triggeredByUser . - */ -cvox.TextChangeEvent = function(newValue, newStart, newEnd, triggeredByUser) { - this.value = newValue; - this.start = newStart; - this.end = newEnd; - this.triggeredByUser = triggeredByUser; - - // Adjust offsets to be in left to right order. - if (this.start > this.end) { - var tempOffset = this.end; - this.end = this.start; - this.start = tempOffset; - } -}; - - -/** - * A list of typing echo options. - * This defines the way typed characters get spoken. - * CHARACTER: echoes typed characters. - * WORD: echoes a word once a breaking character is typed (i.e. spacebar). - * CHARACTER_AND_WORD: combines CHARACTER and WORD behavior. - * NONE: speaks nothing when typing. - * COUNT: The number of possible echo levels. - * @enum - */ -cvox.TypingEcho = { - CHARACTER: 0, - WORD: 1, - CHARACTER_AND_WORD: 2, - NONE: 3, - COUNT: 4 -}; - - -/** - * @param {number} cur Current typing echo. - * @return {number} Next typing echo. - */ -cvox.TypingEcho.cycle = function(cur) { - return (cur + 1) % cvox.TypingEcho.COUNT; -}; - - -/** - * Return if characters should be spoken given the typing echo option. - * @param {number} typingEcho Typing echo option. - * @return {boolean} Whether the character should be spoken. - */ -cvox.TypingEcho.shouldSpeakChar = function(typingEcho) { - return typingEcho == cvox.TypingEcho.CHARACTER_AND_WORD || - typingEcho == cvox.TypingEcho.CHARACTER; -}; - - -/** - * A class representing an abstracted editable text control. - * @param {string} value The string value of the editable text control. - * @param {number} start The 0-based start cursor/selection index. - * @param {number} end The 0-based end cursor/selection index. - * @param {boolean} isPassword Whether the text control if a password field. - * @param {cvox.TtsInterface} tts A TTS object. - * @constructor - */ -cvox.ChromeVoxEditableTextBase = function(value, start, end, isPassword, tts) { - /** - * Current value of the text field. - * @type {string} - * @protected - */ - this.value = value; - - /** - * 0-based selection start index. - * @type {number} - * @protected - */ - this.start = start; - - /** - * 0-based selection end index. - * @type {number} - * @protected - */ - this.end = end; - - /** - * True if this is a password field. - * @type {boolean} - * @protected - */ - this.isPassword = isPassword; - - /** - * Text-to-speech object implementing speak() and stop() methods. - * @type {cvox.TtsInterface} - * @protected - */ - this.tts = tts; - - /** - * Whether or not the text field is multiline. - * @type {boolean} - * @protected - */ - this.multiline = false; - - /** - * Whether or not the last update to the text and selection was described. - * - * Some consumers of this flag like |ChromeVoxEventWatcher| depend on and - * react to when this flag is false by generating alternative feedback. - * @type {boolean} - */ - this.lastChangeDescribed = false; - -}; - - -/** - * Performs setup for this element. - */ -cvox.ChromeVoxEditableTextBase.prototype.setup = function() {}; - - -/** - * Performs teardown for this element. - */ -cvox.ChromeVoxEditableTextBase.prototype.teardown = function() {}; - - -/** - * Whether or not moving the cursor from one character to another considers - * the cursor to be a block (false) or an i-beam (true). - * - * If the cursor is a block, then the value of the character to the right - * of the cursor index is always read when the cursor moves, no matter what - * the previous cursor location was - this is how PC screenreaders work. - * - * If the cursor is an i-beam, moving the cursor by one character reads the - * character that was crossed over, which may be the character to the left or - * right of the new cursor index depending on the direction. - * - * If the current platform is a Mac, we will use an i-beam cursor. If not, - * then we will use the block cursor. - * - * @type {boolean} - */ -cvox.ChromeVoxEditableTextBase.useIBeamCursor = cvox.ChromeVox.isMac; - - -/** - * Switches on or off typing echo based on events. When set, editable text - * updates for single-character insertions are handled in event watcher's key - * press handler. - * @type {boolean} - */ -cvox.ChromeVoxEditableTextBase.eventTypingEcho = false; - - -/** - * The maximum number of characters that are short enough to speak in response - * to an event. For example, if the user selects "Hello", we will speak - * "Hello, selected", but if the user selects 1000 characters, we will speak - * "text selected" instead. - * - * @type {number} - */ -cvox.ChromeVoxEditableTextBase.prototype.maxShortPhraseLen = 60; - - -/** - * Get the line number corresponding to a particular index. - * Default implementation that can be overridden by subclasses. - * @param {number} index The 0-based character index. - * @return {number} The 0-based line number corresponding to that character. - */ -cvox.ChromeVoxEditableTextBase.prototype.getLineIndex = function(index) { - return 0; -}; - - -/** - * Get the start character index of a line. - * Default implementation that can be overridden by subclasses. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the first character in this line. - */ -cvox.ChromeVoxEditableTextBase.prototype.getLineStart = function(index) { - return 0; -}; - - -/** - * Get the end character index of a line. - * Default implementation that can be overridden by subclasses. - * @param {number} index The 0-based line index. - * @return {number} The 0-based index of the end of this line. - */ -cvox.ChromeVoxEditableTextBase.prototype.getLineEnd = function(index) { - return this.value.length; -}; - - -/** - * Get the full text of the current line. - * @param {number} index The 0-based line index. - * @return {string} The text of the line. - */ -cvox.ChromeVoxEditableTextBase.prototype.getLine = function(index) { - var lineStart = this.getLineStart(index); - var lineEnd = this.getLineEnd(index); - return this.value.substr(lineStart, lineEnd - lineStart); -}; - - -/** - * @param {string} ch The character to test. - * @return {boolean} True if a character is whitespace. - */ -cvox.ChromeVoxEditableTextBase.prototype.isWhitespaceChar = function(ch) { - return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; -}; - - -/** - * @param {string} ch The character to test. - * @return {boolean} True if a character breaks a word, used to determine - * if the previous word should be spoken. - */ -cvox.ChromeVoxEditableTextBase.prototype.isWordBreakChar = function(ch) { - return !!ch.match(/^\W$/); -}; - - -/** - * @param {cvox.TextChangeEvent} evt The new text changed event to test. - * @return {boolean} True if the event, when compared to the previous text, - * should trigger description. - */ -cvox.ChromeVoxEditableTextBase.prototype.shouldDescribeChange = function(evt) { - if (evt.value == this.value && - evt.start == this.start && - evt.end == this.end) { - return false; - } - return true; -}; - - -/** - * Speak text, but if it's a single character, describe the character. - * @param {string} str The string to speak. - * @param {boolean=} opt_triggeredByUser True if the speech was triggered by a - * user action. - * @param {Object=} opt_personality Personality used to speak text. - */ -cvox.ChromeVoxEditableTextBase.prototype.speak = - function(str, opt_triggeredByUser, opt_personality) { - var queueMode = cvox.QueueMode.QUEUE; - if (opt_triggeredByUser === true) { - queueMode = cvox.QueueMode.FLUSH; - } - this.tts.speak(str, queueMode, opt_personality || {}); -}; - - -/** - * Update the state of the text and selection and describe any changes as - * appropriate. - * - * @param {cvox.TextChangeEvent} evt The text change event. - */ -cvox.ChromeVoxEditableTextBase.prototype.changed = function(evt) { - if (!this.shouldDescribeChange(evt)) { - this.lastChangeDescribed = false; - return; - } - - if (evt.value == this.value) { - this.describeSelectionChanged(evt); - } else { - this.describeTextChanged(evt); - } - this.lastChangeDescribed = true; - - this.value = evt.value; - this.start = evt.start; - this.end = evt.end; -}; - - -/** - * Describe a change in the selection or cursor position when the text - * stays the same. - * @param {cvox.TextChangeEvent} evt The text change event. - */ -cvox.ChromeVoxEditableTextBase.prototype.describeSelectionChanged = - function(evt) { - // TODO(deboer): Factor this into two function: - // - one to determine the selection event - // - one to speak - - if (this.isPassword) { - this.speak((new goog.i18n.MessageFormat(Msgs.getMsg('dot')) - .format({'COUNT': 1})), evt.triggeredByUser); - return; - } - if (evt.start == evt.end) { - // It's currently a cursor. - if (this.start != this.end) { - // It was previously a selection, so just announce 'unselected'. - this.speak(Msgs.getMsg('Unselected'), evt.triggeredByUser); - } else if (this.getLineIndex(this.start) != - this.getLineIndex(evt.start)) { - // Moved to a different line; read it. - var lineValue = this.getLine(this.getLineIndex(evt.start)); - if (lineValue == '') { - lineValue = Msgs.getMsg('text_box_blank'); - } else if (/^\s+$/.test(lineValue)) { - lineValue = Msgs.getMsg('text_box_whitespace'); - } - this.speak(lineValue, evt.triggeredByUser); - } else if (this.start == evt.start + 1 || - this.start == evt.start - 1) { - // Moved by one character; read it. - if (!cvox.ChromeVoxEditableTextBase.useIBeamCursor) { - if (evt.start == this.value.length) { - if (cvox.ChromeVox.verbosity == cvox.VERBOSITY_VERBOSE) { - this.speak(Msgs.getMsg('end_of_text_verbose'), - evt.triggeredByUser); - } else { - this.speak(Msgs.getMsg('end_of_text_brief'), - evt.triggeredByUser); - } - } else { - this.speak(this.value.substr(evt.start, 1), - evt.triggeredByUser, - {'phoneticCharacters': evt.triggeredByUser}); - } - } else { - this.speak(this.value.substr(Math.min(this.start, evt.start), 1), - evt.triggeredByUser, - {'phoneticCharacters': evt.triggeredByUser}); - } - } else { - // Moved by more than one character. Read all characters crossed. - this.speak(this.value.substr(Math.min(this.start, evt.start), - Math.abs(this.start - evt.start)), evt.triggeredByUser); - } - } else { - // It's currently a selection. - if (this.start + 1 == evt.start && - this.end == this.value.length && - evt.end == this.value.length) { - // Autocomplete: the user typed one character of autocompleted text. - this.speak(this.value.substr(this.start, 1), evt.triggeredByUser); - this.speak(this.value.substr(evt.start)); - } else if (this.start == this.end) { - // It was previously a cursor. - this.speak(this.value.substr(evt.start, evt.end - evt.start), - evt.triggeredByUser); - this.speak(Msgs.getMsg('selected')); - } else if (this.start == evt.start && this.end < evt.end) { - this.speak(this.value.substr(this.end, evt.end - this.end), - evt.triggeredByUser); - this.speak(Msgs.getMsg('added_to_selection')); - } else if (this.start == evt.start && this.end > evt.end) { - this.speak(this.value.substr(evt.end, this.end - evt.end), - evt.triggeredByUser); - this.speak(Msgs.getMsg('removed_from_selection')); - } else if (this.end == evt.end && this.start > evt.start) { - this.speak(this.value.substr(evt.start, this.start - evt.start), - evt.triggeredByUser); - this.speak(Msgs.getMsg('added_to_selection')); - } else if (this.end == evt.end && this.start < evt.start) { - this.speak(this.value.substr(this.start, evt.start - this.start), - evt.triggeredByUser); - this.speak(Msgs.getMsg('removed_from_selection')); - } else { - // The selection changed but it wasn't an obvious extension of - // a previous selection. Just read the new selection. - this.speak(this.value.substr(evt.start, evt.end - evt.start), - evt.triggeredByUser); - this.speak(Msgs.getMsg('selected')); - } - } -}; - - -/** - * Describe a change where the text changes. - * @param {cvox.TextChangeEvent} evt The text change event. - */ -cvox.ChromeVoxEditableTextBase.prototype.describeTextChanged = function(evt) { - var personality = {}; - if (evt.value.length < this.value.length) { - personality = cvox.AbstractTts.PERSONALITY_DELETED; - } - if (this.isPassword) { - this.speak((new goog.i18n.MessageFormat(Msgs.getMsg('dot')) - .format({'COUNT': 1})), evt.triggeredByUser, personality); - return; - } - - var value = this.value; - var len = value.length; - var newLen = evt.value.length; - var autocompleteSuffix = ''; - // Make a copy of evtValue and evtEnd to avoid changing anything in - // the event itself. - var evtValue = evt.value; - var evtEnd = evt.end; - - // First, see if there's a selection at the end that might have been - // added by autocomplete. If so, strip it off into a separate variable. - if (evt.start < evtEnd && evtEnd == newLen) { - autocompleteSuffix = evtValue.substr(evt.start); - evtValue = evtValue.substr(0, evt.start); - evtEnd = evt.start; - } - - // Now see if the previous selection (if any) was deleted - // and any new text was inserted at that character position. - // This would handle pasting and entering text by typing, both from - // a cursor and from a selection. - var prefixLen = this.start; - var suffixLen = len - this.end; - if (newLen >= prefixLen + suffixLen + (evtEnd - evt.start) && - evtValue.substr(0, prefixLen) == value.substr(0, prefixLen) && - evtValue.substr(newLen - suffixLen) == value.substr(this.end)) { - // However, in a dynamic content editable, defer to authoritative events - // (clipboard, key press) to reduce guess work when observing insertions. - // Only use this logic when observing deletions (and insertion of word - // breakers). - // TODO(dtseng): Think about a more reliable way to do this. - if (!(this instanceof cvox.ChromeVoxEditableContentEditable) || - newLen < len || - this.isWordBreakChar(evt.value[newLen - 1] || '')) { - this.describeTextChangedHelper( - evt, prefixLen, suffixLen, autocompleteSuffix, personality); - } - return; - } - - // Next, see if one or more characters were deleted from the previous - // cursor position and the new cursor is in the expected place. This - // handles backspace, forward-delete, and similar shortcuts that delete - // a word or line. - prefixLen = evt.start; - suffixLen = newLen - evtEnd; - if (this.start == this.end && - evt.start == evtEnd && - evtValue.substr(0, prefixLen) == value.substr(0, prefixLen) && - evtValue.substr(newLen - suffixLen) == - value.substr(len - suffixLen)) { - this.describeTextChangedHelper( - evt, prefixLen, suffixLen, autocompleteSuffix, personality); - return; - } - - // If all else fails, we assume the change was not the result of a normal - // user editing operation, so we'll have to speak feedback based only - // on the changes to the text, not the cursor position / selection. - // First, restore the autocomplete text if any. - evtValue += autocompleteSuffix; - - // Try to do a diff between the new and the old text. If it is a one character - // insertion/deletion at the start or at the end, just speak that character. - if ((evtValue.length == (value.length + 1)) || - ((evtValue.length + 1) == value.length)) { - // The user added text either to the beginning or the end. - if (evtValue.length > value.length) { - if (evtValue.indexOf(value) == 0) { - this.speak(evtValue[evtValue.length - 1], evt.triggeredByUser, - personality); - return; - } else if (evtValue.indexOf(value) == 1) { - this.speak(evtValue[0], evt.triggeredByUser, personality); - return; - } - } - // The user deleted text either from the beginning or the end. - if (evtValue.length < value.length) { - if (value.indexOf(evtValue) == 0) { - this.speak(value[value.length - 1], evt.triggeredByUser, personality); - return; - } else if (value.indexOf(evtValue) == 1) { - this.speak(value[0], evt.triggeredByUser, personality); - return; - } - } - } - - if (this.multiline) { - // Fall back to announce deleted but omit the text that was deleted. - if (evt.value.length < this.value.length) { - this.speak(Msgs.getMsg('text_deleted'), - evt.triggeredByUser, personality); - } - // The below is a somewhat loose way to deal with non-standard - // insertions/deletions. Intentionally skip for multiline since deletion - // announcements are covered above and insertions are non-standard (possibly - // due to auto complete). Since content editable's often refresh content by - // removing and inserting entire chunks of text, this type of logic often - // results in unintended consequences such as reading all text when only one - // character has been entered. - return; - } - - // If the text is short, just speak the whole thing. - if (newLen <= this.maxShortPhraseLen) { - this.describeTextChangedHelper(evt, 0, 0, '', personality); - return; - } - - // Otherwise, look for the common prefix and suffix, but back up so - // that we can speak complete words, to be minimally confusing. - prefixLen = 0; - while (prefixLen < len && - prefixLen < newLen && - value[prefixLen] == evtValue[prefixLen]) { - prefixLen++; - } - while (prefixLen > 0 && !this.isWordBreakChar(value[prefixLen - 1])) { - prefixLen--; - } - - suffixLen = 0; - while (suffixLen < (len - prefixLen) && - suffixLen < (newLen - prefixLen) && - value[len - suffixLen - 1] == evtValue[newLen - suffixLen - 1]) { - suffixLen++; - } - while (suffixLen > 0 && !this.isWordBreakChar(value[len - suffixLen])) { - suffixLen--; - } - - this.describeTextChangedHelper(evt, prefixLen, suffixLen, '', personality); -}; - - -/** - * The function called by describeTextChanged after it's figured out - * what text was deleted, what text was inserted, and what additional - * autocomplete text was added. - * @param {cvox.TextChangeEvent} evt The text change event. - * @param {number} prefixLen The number of characters in the common prefix - * of this.value and newValue. - * @param {number} suffixLen The number of characters in the common suffix - * of this.value and newValue. - * @param {string} autocompleteSuffix The autocomplete string that was added - * to the end, if any. It should be spoken at the end of the utterance - * describing the change. - * @param {Object=} opt_personality Personality to speak the text. - */ -cvox.ChromeVoxEditableTextBase.prototype.describeTextChangedHelper = function( - evt, prefixLen, suffixLen, autocompleteSuffix, opt_personality) { - var len = this.value.length; - var newLen = evt.value.length; - var deletedLen = len - prefixLen - suffixLen; - var deleted = this.value.substr(prefixLen, deletedLen); - var insertedLen = newLen - prefixLen - suffixLen; - var inserted = evt.value.substr(prefixLen, insertedLen); - var utterance = ''; - var triggeredByUser = evt.triggeredByUser; - - if (insertedLen > 1) { - utterance = inserted; - } else if (insertedLen == 1) { - if ((cvox.ChromeVox.typingEcho == cvox.TypingEcho.WORD || - cvox.ChromeVox.typingEcho == cvox.TypingEcho.CHARACTER_AND_WORD) && - this.isWordBreakChar(inserted) && - prefixLen > 0 && - !this.isWordBreakChar(evt.value.substr(prefixLen - 1, 1))) { - // Speak previous word. - var index = prefixLen; - while (index > 0 && !this.isWordBreakChar(evt.value[index - 1])) { - index--; - } - if (index < prefixLen) { - utterance = evt.value.substr(index, prefixLen + 1 - index); - } else { - utterance = inserted; - triggeredByUser = false; // Implies QUEUE_MODE_QUEUE. - } - } else if (cvox.ChromeVox.typingEcho == cvox.TypingEcho.CHARACTER || - cvox.ChromeVox.typingEcho == cvox.TypingEcho.CHARACTER_AND_WORD) { - // This particular case is handled in event watcher. See the key press - // handler for more details. - utterance = cvox.ChromeVoxEditableTextBase.eventTypingEcho ? '' : - inserted; - } - } else if (deletedLen > 1 && !autocompleteSuffix) { - utterance = deleted + ', deleted'; - } else if (deletedLen == 1) { - utterance = deleted; - } - - if (autocompleteSuffix && utterance) { - utterance += ', ' + autocompleteSuffix; - } else if (autocompleteSuffix) { - utterance = autocompleteSuffix; - } - - if (utterance) { - this.speak(utterance, triggeredByUser, opt_personality); - } -}; - - -/** - * Moves the cursor forward by one character. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToNextCharacter = - function() { return false; }; - - -/** - * Moves the cursor backward by one character. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToPreviousCharacter = - function() { return false; }; - - -/** - * Moves the cursor forward by one word. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToNextWord = - function() { return false; }; - - -/** - * Moves the cursor backward by one word. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToPreviousWord = - function() { return false; }; - - -/** - * Moves the cursor forward by one line. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToNextLine = - function() { return false; }; - - -/** - * Moves the cursor backward by one line. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToPreviousLine = - function() { return false; }; - - -/** - * Moves the cursor forward by one paragraph. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToNextParagraph = - function() { return false; }; - - -/** - * Moves the cursor backward by one paragraph. - * @return {boolean} True if the action was handled. - */ -cvox.ChromeVoxEditableTextBase.prototype.moveCursorToPreviousParagraph = - function() { return false; }; - - -/******************************************/ diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs deleted file mode 100644 index a122d096cb8..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/editable_text_test.unitjs +++ /dev/null @@ -1,693 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * A TTS class implementing speak and stop methods intended only for testing. - * @constructor - * @implements cvox.TtsInterface - */ -function TestTts() { - this.strings = []; -} - -/** - * The strings that were spoken since the last call to get(). - * @type {Array<string>} - */ -TestTts.prototype.strings; - -/** - * Returns the list of strings spoken since the last time this method was - * called, and then clears the list. - * @return {Array<string>} The list of strings. - */ -TestTts.prototype.get = function() { - var result = this.strings; - this.strings = []; - return result; -}; - -/** @override */ -TestTts.prototype.speak = function(text, queueMode, properties) { - this.strings.push(text); -}; - -/** @override */ -TestTts.prototype.isSpeaking = function() { - return false; -}; - -/** @override */ -TestTts.prototype.stop = function() { - // Do nothing. -}; - -/** @override */ -TestTts.prototype.increaseOrDecreaseProperty = - function(propertyName, increase) { - // Do nothing. -}; - -/** - * Stores the last braille content. - * @constructor - * @implements cvox.BrailleInterface - */ -function TestBraille() { - this.content = null; -} - -/** @override */ -TestBraille.prototype.write = function(params) { - this.content = params; -}; - -/** - * Asserts the current braille content. - * - * @param {string} text Braille text. - * @param {number=} opt_start Selection start. - * @param {number=} opt_end Selection end. - */ -TestBraille.assertContent = function(text, opt_start, opt_end) { - var c = cvox.ChromeVox.braille.content; - assertTrue(c != null); - opt_start = opt_start !== undefined ? opt_start : -1; - opt_end = opt_end !== undefined ? opt_end : opt_start; - assertEquals(text, c.text.toString()); - assertEquals(opt_start, c.startIndex); - assertEquals(opt_end, c.endIndex); -}; - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxEditableTextUnitTest() {} - -CvoxEditableTextUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.ChromeVoxEditableElement', - 'cvox.ChromeVoxEditableHTMLInput', - 'cvox.ChromeVoxEditableTextBase', - 'cvox.ChromeVoxEventWatcher', - 'cvox.TextChangeEvent', - 'cvox.TtsInterface', - 'cvox.TypingEcho', - ], - - /** @override */ - setUp: function() { - // TODO: These tests are all assuming we used the IBeam cursor. - // We need to add coverage for block cursor. - cvox.ChromeVoxEditableTextBase.useIBeamCursor = true; - cvox.ChromeVox.typingEcho = cvox.TypingEcho.CHARACTER_AND_WORD; - cvox.ChromeVoxEditableTextBase.eventTypingEcho = false; - cvox.ChromeVox.braille = new TestBraille(); - - /** Simple mock. */ - Msgs = {}; - - /** - * Simply return the message id. - * @param {string} msg Message id. - * @return {string} Message id. - */ - Msgs.getMsg = function(msg) { - return msg; - }; - }, - - /** - * Sets up for a cursor movement test. - * @param {string} tagName Desired tag name, "input" or "textarea". - * @return {Object} object containing the editable element, and functions - * to prepare, run the test, and tear down. - * @private - */ - setUpForCursorTest_: function(tagName) { - var element, editable; - switch (tagName) { - case 'input': - element = document.createElement('input'); - editable = new cvox.ChromeVoxEditableHTMLInput(element, new TestTts()); - break; - case 'textarea': - element = document.createElement('textarea'); - editable = new cvox.ChromeVoxEditableTextArea(element, new TestTts()); - break; - default: - throw 'invalid tagName in setUpForCursorTest_'; - } - document.body.appendChild(element); - element.focus(); - - var expect = function(str) { - assertEquals(element.selectionStart, element.selectionEnd); - assertEquals(str, element.value.substring(0, element.selectionStart) + - '|' + element.value.substring(element.selectionEnd)); - }; - return { - editable: editable, - expect: expect, - prepare: function(str) { - var position = str.indexOf('|'); - var value = str.substring(0, position) + str.substring(position + 1); - element.value = value; - element.selectionStart = element.selectionEnd = position; - editable.update(true /* triggeredByUser */); - expect(str); - }, - tearDown: function() { - document.body.removeChild(element); - } - }; - } -}; - -TEST_F('CvoxEditableTextUnitTest', 'CursorNavigation', function() { - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('Hello', 0, 0, false, tts); - - obj.changed(new cvox.TextChangeEvent('Hello', 1, 1)); - obj.changed(new cvox.TextChangeEvent('Hello', 2, 2)); - obj.changed(new cvox.TextChangeEvent('Hello', 3, 3)); - obj.changed(new cvox.TextChangeEvent('Hello', 4, 4)); - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - obj.changed(new cvox.TextChangeEvent('Hello', 4, 4)); - obj.changed(new cvox.TextChangeEvent('Hello', 3, 3)); - assertEqualStringArrays(['H', 'e', 'l', 'l', 'o', - 'o', 'l'], tts.get()); - obj.changed(new cvox.TextChangeEvent('Hello', 0, 0)); - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - assertEqualStringArrays(['Hel', 'Hello'], tts.get()); -}); - -/** Test typing words. */ -TEST_F('CvoxEditableTextUnitTest', 'TypingWords', function() { - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts); - obj.changed(new cvox.TextChangeEvent('H', 1, 1)); - obj.changed(new cvox.TextChangeEvent('He', 2, 2)); - obj.changed(new cvox.TextChangeEvent('Hel', 3, 3)); - obj.changed(new cvox.TextChangeEvent('Hell', 4, 4)); - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6)); - obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8)); - obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10)); - obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11)); - obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12)); - obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13)); - assertEqualStringArrays(['H', 'e', 'l', 'l', 'o', 'Hello,', - ' ', - 'W', 'o', 'r', 'l', 'd', 'World.'], - tts.get()); - - // Backspace - obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12)); - obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10)); - assertEqualStringArrays(['.', 'd', 'l'], tts.get()); - - // Forward-delete - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 9, 9)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 8, 8)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, or', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, r', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7)); - assertEqualStringArrays(['r', 'o', 'W', 'W', 'o', 'r'], tts.get()); - - // Clear all - obj.changed(new cvox.TextChangeEvent('', 0, 0)); - assertEqualStringArrays(['Hello, , deleted'], tts.get()); - - // Paste / insert a whole word - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - assertEqualStringArrays(['Hello'], tts.get()); - obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12)); - assertEqualStringArrays([', World'], tts.get()); -}); - -/** Test selection. */ -TEST_F('CvoxEditableTextUnitTest', 'Selection', function() { - var tts = new TestTts(); - var obj = - new cvox.ChromeVoxEditableTextBase('Hello, world.', 0, 0, false, tts); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 1)); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 2)); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 3)); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 4)); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 5)); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 6)); - assertEqualStringArrays(['H', 'selected', - 'e', 'added_to_selection', - 'l', 'added_to_selection', - 'l', 'added_to_selection', - 'o', 'added_to_selection', - ',', 'added_to_selection'], - tts.get()); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 0, 12)); - assertEqualStringArrays([' world', 'added_to_selection'], - tts.get()); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 1, 12)); - assertEqualStringArrays(['H', 'removed_from_selection'], - tts.get()); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 2, 5)); - assertEqualStringArrays(['llo', 'selected'], - tts.get()); - obj.changed(new cvox.TextChangeEvent('Hello, world.', 2, 2)); - assertEqualStringArrays(['Unselected'], - tts.get()); -}); - - -/** Test multi-line text. */ -TEST_F('CvoxEditableTextUnitTest', 'MultiLineText', function() { - var str = 'This string\nspans\nfive lines.\n \n'; - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableElement(null, str, 0, 0, false, tts); - obj.multiline = true; - obj.getLineIndex = function(index) { - if (index >= 33) { - return 4; - } else if (index >= 30) { - return 3; - } else if (index >= 18) { - return 2; - } else if (index >= 12) { - return 1; - } else { - return 0; - } - }; - obj.getLineStart = function(index) { - return [0, 12, 18, 30, 33][index]; - }; - obj.getLineEnd = function(index) { - return [11, 17, 29, 32, 33][index]; - }; - assertEquals('This string', obj.getLine(0)); - obj.changed(new cvox.TextChangeEvent(str, 12, 12)); - assertEqualStringArrays(['spans'], tts.get()); - TestBraille.assertContent('spans', 0); - obj.changed(new cvox.TextChangeEvent(str, 18, 18)); - assertEqualStringArrays(['five lines.'], tts.get()); - TestBraille.assertContent('five lines.', 0); - obj.changed(new cvox.TextChangeEvent(str, 30, 30)); - assertEqualStringArrays(['text_box_whitespace'], tts.get()); - TestBraille.assertContent(' ', 0); - obj.changed(new cvox.TextChangeEvent(str, 33, 33)); - assertEqualStringArrays(['text_box_blank'], tts.get()); - TestBraille.assertContent('', 0); - obj.changed(new cvox.TextChangeEvent(str, 0, 1)); - assertEqualStringArrays(['T', 'selected'], tts.get()); - TestBraille.assertContent('This string', 0, 1); - obj.changed(new cvox.TextChangeEvent(str, 0, 12)); - assertEqualStringArrays(['his string\n', 'added_to_selection'], - tts.get()); - // Newline stripped, thus 11, not 12. - TestBraille.assertContent('This string', 0, 11); - obj.changed(new cvox.TextChangeEvent(str, 0, str.length)); - assertEqualStringArrays([str.substr(12), 'added_to_selection'], - tts.get()); - TestBraille.assertContent('This string', 0, 11); - obj.changed(new cvox.TextChangeEvent(str, 12, 19)); - assertEqualStringArrays(['spans\nf', 'selected'], tts.get()); - TestBraille.assertContent('spans', 0, 5); -}); - - -/** - * Test autocomplete; suppose a user is typing "google.com/firefox" into an - * address bar, and it's being autocompleted. Sometimes it's autocompleted - * as they type, sometimes there's a short delay. - */ -TEST_F('CvoxEditableTextUnitTest', 'Autocomplete', function() { - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts); - - // User types 'g' - obj.changed(new cvox.TextChangeEvent('g', 1, 1)); - assertEqualStringArrays(['g'], tts.get()); - - // The rest of 'google.com' is autocompleted and automatically selected. - obj.changed(new cvox.TextChangeEvent('google.com', 1, 10)); - assertEqualStringArrays(['oogle.com, oogle.com'], tts.get()); - - // The user doesn't realize it and types a few more characters of 'google.com' - // and this changes the selection (unselecting) as the user types them. - obj.changed(new cvox.TextChangeEvent('google.com', 2, 10)); - assertEqualStringArrays(['o', 'ogle.com'], tts.get()); - obj.changed(new cvox.TextChangeEvent('google.com', 3, 10)); - assertEqualStringArrays(['o', 'gle.com'], tts.get()); - obj.changed(new cvox.TextChangeEvent('google.com', 4, 10)); - assertEqualStringArrays(['g', 'le.com'], tts.get()); - - // The user presses right-arrow, which fully unselects the remaining text. - obj.changed(new cvox.TextChangeEvent('google.com', 10, 10)); - assertEqualStringArrays(['Unselected'], tts.get()); - - // The user types '/' - obj.changed(new cvox.TextChangeEvent('google.com/', 11, 11)); - assertEqualStringArrays(['com/'], tts.get()); - - // The user types 'f', and 'finance' is autocompleted - obj.changed(new cvox.TextChangeEvent('google.com/finance', 12, 18)); - assertEqualStringArrays(['finance, inance'], tts.get()); - - // The user types 'i' - obj.changed(new cvox.TextChangeEvent('google.com/finance', 13, 18)); - assertEqualStringArrays(['i', 'nance'], tts.get()); - - // The user types 'r', now 'firefox' is autocompleted - obj.changed(new cvox.TextChangeEvent('google.com/firefox', 14, 18)); - assertEqualStringArrays(['refox, efox'], tts.get()); - - // The user presses right-arrow to accept the completion. - obj.changed(new cvox.TextChangeEvent('google.com/firefox', 18, 18)); - assertEqualStringArrays(['Unselected'], tts.get()); -}); - - -/** - * Test a few common scenarios where text is replaced. - */ -TEST_F('CvoxEditableTextUnitTest', 'ReplacingText', function() { - // Initial value is Alabama. - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('Alabama', 0, 0, false, tts); - - // Entire text replaced with Alaska. - obj.changed(new cvox.TextChangeEvent('Alaska', 0, 0)); - assertEqualStringArrays(['Alaska'], tts.get()); - - // Entire text selected. - obj.changed(new cvox.TextChangeEvent('Alaska', 0, 6)); - assertEqualStringArrays(['Alaska', 'selected'], tts.get()); - - // Entire text replaced with Arizona. - obj.changed(new cvox.TextChangeEvent('Arizona', 7, 7)); - assertEqualStringArrays(['Arizona'], tts.get()); - - // Entire text selected. - obj.changed(new cvox.TextChangeEvent('Arizona', 0, 7)); - assertEqualStringArrays(['Arizona', 'selected'], tts.get()); - - // Click between 'r' and 'i'. - obj.changed(new cvox.TextChangeEvent('Arizona', 2, 2)); - assertEqualStringArrays(['Unselected'], tts.get()); - - // Next character removed from selection. - obj.changed(new cvox.TextChangeEvent('Arizona', 2, 7)); - assertEqualStringArrays(['izona', 'selected'], tts.get()); - - // Selection replaced with "kansas" to make Arkansas. This time it - // says "kansas" because the deleted text was selected. - obj.changed(new cvox.TextChangeEvent('Arkansas', 8, 8)); - assertEqualStringArrays(['kansas'], tts.get()); -}); - - -/** - * Test feedback when text changes in a long sentence. - */ -TEST_F('CvoxEditableTextUnitTest', 'ReplacingLongText', function() { - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase( - 'I love deadlines. I like the whooshing sound they make as they fly by.', - 0, 0, false, tts); - - // Change the whole sentence without moving the cursor. It should speak - // only the part that changed, but it should speak whole words. - obj.changed(new cvox.TextChangeEvent( - 'I love deadlines. I love the whooshing sounds they make as they fly by.', - 0, 0)); - assertEqualStringArrays(['love the whooshing sounds'], tts.get()); -}); - -/** Tests character echo. */ -TEST_F('CvoxEditableTextUnitTest', 'CharacterEcho', function() { - cvox.ChromeVox.typingEcho = cvox.TypingEcho.CHARACTER; - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts); - obj.changed(new cvox.TextChangeEvent('H', 1, 1)); - obj.changed(new cvox.TextChangeEvent('He', 2, 2)); - obj.changed(new cvox.TextChangeEvent('Hel', 3, 3)); - obj.changed(new cvox.TextChangeEvent('Hell', 4, 4)); - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6)); - obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8)); - obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10)); - obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11)); - obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12)); - obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13)); - assertEqualStringArrays( - ['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '.'], - tts.get()); -}); - - -/** Tests word echo. */ -TEST_F('CvoxEditableTextUnitTest', 'WordEcho', function() { - cvox.ChromeVox.typingEcho = cvox.TypingEcho.WORD; - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts); - obj.changed(new cvox.TextChangeEvent('H', 1, 1)); - obj.changed(new cvox.TextChangeEvent('He', 2, 2)); - obj.changed(new cvox.TextChangeEvent('Hel', 3, 3)); - obj.changed(new cvox.TextChangeEvent('Hell', 4, 4)); - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6)); - obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8)); - obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10)); - obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11)); - obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12)); - obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13)); - assertEqualStringArrays( - ['Hello,', 'World.'], - tts.get()); -}); - - -/** Tests no echo. */ -TEST_F('CvoxEditableTextUnitTest', 'NoEcho', function() { - cvox.ChromeVox.typingEcho = cvox.TypingEcho.NONE; - var tts = new TestTts(); - var obj = new cvox.ChromeVoxEditableTextBase('', 0, 0, false, tts); - obj.changed(new cvox.TextChangeEvent('H', 1, 1)); - obj.changed(new cvox.TextChangeEvent('He', 2, 2)); - obj.changed(new cvox.TextChangeEvent('Hel', 3, 3)); - obj.changed(new cvox.TextChangeEvent('Hell', 4, 4)); - obj.changed(new cvox.TextChangeEvent('Hello', 5, 5)); - obj.changed(new cvox.TextChangeEvent('Hello,', 6, 6)); - obj.changed(new cvox.TextChangeEvent('Hello, ', 7, 7)); - obj.changed(new cvox.TextChangeEvent('Hello, W', 8, 8)); - obj.changed(new cvox.TextChangeEvent('Hello, Wo', 9, 9)); - obj.changed(new cvox.TextChangeEvent('Hello, Wor', 10, 10)); - obj.changed(new cvox.TextChangeEvent('Hello, Worl', 11, 11)); - obj.changed(new cvox.TextChangeEvent('Hello, World', 12, 12)); - obj.changed(new cvox.TextChangeEvent('Hello, World.', 13, 13)); - assertEqualStringArrays( - [], - tts.get()); -}); - -/** Tests cursor movement in an input field by character. */ -TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByCharacter', function() { - var test = this.setUpForCursorTest_('input'); - var editable = test.editable, prepare = test.prepare, expect = test.expect; - try { - // Moving near the beginning of the text. - prepare('|"Hello," says Sally.'); - editable.moveCursorToPreviousCharacter(); - expect('|"Hello," says Sally.'); - editable.moveCursorToNextCharacter(); - expect('"|Hello," says Sally.'); - editable.moveCursorToNextCharacter(); - expect('"H|ello," says Sally.'); - - // Moving near the end of the text. - prepare('"Hello," says Sally|.'); - editable.moveCursorToPreviousCharacter(); - expect('"Hello," says Sall|y.'); - editable.moveCursorToNextCharacter(); - expect('"Hello," says Sally|.'); - editable.moveCursorToNextCharacter(); - expect('"Hello," says Sally.|'); - editable.moveCursorToNextCharacter(); - expect('"Hello," says Sally.|'); - } finally { - test.tearDown(); - } -}); - -/** Tests cursor movement in an input field by word. */ -TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByWord', function() { - var test = this.setUpForCursorTest_('input'); - var editable = test.editable, prepare = test.prepare, expect = test.expect; - try { - // Moving forward. - prepare('"He|llo," says Sally.'); - editable.moveCursorToNextWord(); - expect('"Hello|," says Sally.'); - editable.moveCursorToNextWord(); - expect('"Hello," says| Sally.'); - editable.moveCursorToNextWord(); - expect('"Hello," says Sally|.'); - editable.moveCursorToNextWord(); - expect('"Hello," says Sally.|'); - editable.moveCursorToNextWord(); - expect('"Hello," says Sally.|'); - - // Moving backward. - prepare('"Hello," says S|ally.'); - editable.moveCursorToPreviousWord(); - expect('"Hello," says |Sally.'); - editable.moveCursorToPreviousWord(); - expect('"Hello," |says Sally.'); - editable.moveCursorToPreviousWord(); - expect('"|Hello," says Sally.'); - editable.moveCursorToPreviousWord(); - expect('|"Hello," says Sally.'); - editable.moveCursorToPreviousWord(); - expect('|"Hello," says Sally.'); - } finally { - test.tearDown(); - } -}); - -/** Tests that character and word movement still work in <textarea>. */ -TEST_F('CvoxEditableTextUnitTest', 'CursorMovementTextArea', function() { - var test = this.setUpForCursorTest_('textarea'); - var editable = test.editable, prepare = test.prepare, expect = test.expect; - try { - prepare('|Hello, Larry.\nHello, Sergey.'); - editable.moveCursorToNextCharacter(); - expect('H|ello, Larry.\nHello, Sergey.'); - editable.moveCursorToNextWord(); - expect('Hello|, Larry.\nHello, Sergey.'); - editable.moveCursorToNextWord(); - expect('Hello, Larry|.\nHello, Sergey.'); - editable.moveCursorToNextWord(); - expect('Hello, Larry.\nHello|, Sergey.'); - editable.moveCursorToNextCharacter(); - expect('Hello, Larry.\nHello,| Sergey.'); - editable.moveCursorToPreviousWord(); - expect('Hello, Larry.\n|Hello, Sergey.'); - editable.moveCursorToPreviousCharacter(); - expect('Hello, Larry.|\nHello, Sergey.'); - } finally { - test.tearDown(); - } -}); - -/** Tests that line navigation works. */ -TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByLine', function() { - var test = this.setUpForCursorTest_('textarea'); - var editable = test.editable, prepare = test.prepare, expect = test.expect; - try { - prepare('123\n1234\n1234|5\n\nHi'); - editable.moveCursorToPreviousLine(); - expect('123\n1234|\n12345\n\nHi'); - editable.moveCursorToPreviousLine(); - expect('123|\n1234\n12345\n\nHi'); - editable.moveCursorToNextLine(); - expect('123\n123|4\n12345\n\nHi'); - editable.moveCursorToNextLine(); - expect('123\n1234\n123|45\n\nHi'); - editable.moveCursorToNextLine(); - expect('123\n1234\n12345\n|\nHi'); - editable.moveCursorToNextLine(); - expect('123\n1234\n12345\n\n|Hi'); - editable.moveCursorToNextLine(); - expect('123\n1234\n12345\n\nHi|'); - - prepare('foo|bar'); - editable.moveCursorToPreviousLine(); - expect('|foobar'); - editable.moveCursorToPreviousLine(); - expect('|foobar'); - editable.moveCursorToNextLine(); - expect('foobar|'); - editable.moveCursorToNextLine(); - expect('foobar|'); - } finally { - test.tearDown(); - } -}); - -/** Tests that paragraph navigation works. */ -TEST_F('CvoxEditableTextUnitTest', 'CursorMovementByParagraph', function() { - var test = this.setUpForCursorTest_('textarea'); - var editable = test.editable, prepare = test.prepare, expect = test.expect; - try { - prepare('Para|graph 1\nParagraph 2\nParagraph 3'); - editable.moveCursorToNextParagraph(); - expect('Paragraph 1\n|Paragraph 2\nParagraph 3'); - editable.moveCursorToNextParagraph(); - expect('Paragraph 1\nParagraph 2\n|Paragraph 3'); - editable.moveCursorToNextParagraph(); - expect('Paragraph 1\nParagraph 2\nParagraph 3|'); - editable.moveCursorToPreviousParagraph(); - expect('Paragraph 1\nParagraph 2\n|Paragraph 3'); - editable.moveCursorToPreviousParagraph(); - expect('Paragraph 1\n|Paragraph 2\nParagraph 3'); - editable.moveCursorToPreviousParagraph(); - expect('|Paragraph 1\nParagraph 2\nParagraph 3'); - } finally { - test.tearDown(); - } -}); - -/** Tests normalization of TextChangeEvent's */ -TEST_F('CvoxEditableTextUnitTest', 'TextChangeEvent', function() { - var event1 = new cvox.TextChangeEvent('foo', 0, 1, true); - var event2 = new cvox.TextChangeEvent('foo', 1, 0, true); - var event3 = new cvox.TextChangeEvent('foo', 1, 1, true); - - assertEquals(0, event1.start); - assertEquals(1, event1.end); - - assertEquals(0, event2.start); - assertEquals(1, event2.end); - - assertEquals(1, event3.start); - assertEquals(1, event3.end); -}); - -TEST_F('CvoxEditableTextUnitTest', 'ContentEditableBraille', function() { - this.loadDoc(function() {/*! - <div id='1' contenteditable='true'> - Some text.<br><br> - After blank line. - </div> - */}); - var element = $('1'); - element.focus(); - var editable = new cvox.ChromeVoxEditableContentEditable( - element, new TestTts()); - var firstLine = 'Some text.\n'; - for (var i = 0; i < firstLine.length; ++i) { - editable.update(true); - TestBraille.assertContent(firstLine, i, i); - window.getSelection().modify('move', 'forward', 'character'); - } - // We should have crossed the line break to the second line which is blank. - editable.update(true); - TestBraille.assertContent('', 0, 0); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/externs.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/externs.js deleted file mode 100644 index 6ae51f423b0..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/externs.js +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -Document.prototype.documentElement.innerWidth; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/find_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/find_util.js deleted file mode 100644 index a5ac8a6b371..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/find_util.js +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Utilities for finding DOM nodes and CursorSelection's. - */ - - -goog.provide('cvox.FindUtil'); - -goog.require('cvox.BareObjectWalker'); -goog.require('cvox.CursorSelection'); - - -/** - * @type {!cvox.BareObjectWalker} - * @private - */ -cvox.FindUtil.objectWalker_ = new cvox.BareObjectWalker(); - - -/** - * Finds the next selection that matches the predicate function starting from - * sel. Undefined if the nodes in sel are not attached to the document. - * @param {!cvox.CursorSelection} sel The selection from which to start. - * @param {function(Array<Node>):Node} predicate A function taking a - * unique ancestor tree and outputting Node if the ancestor tree matches - * the desired node to find. - * @param {boolean=} opt_initialNode Whether to start the search from node - * (true), or the next node (false); defaults to false. - * @return {cvox.CursorSelection} The selection that was found. - * null if end of document reached. - */ -cvox.FindUtil.findNext = function(sel, predicate, opt_initialNode) { - var r = sel.isReversed(); - var cur = new cvox.CursorSelection(sel.absStart(), sel.absStart()) - .setReversed(r); - - // We may have been sync'ed into a subtree of the current predicate match. - // Find our ancestor that matches the predicate. - var ancestor; - if (ancestor = predicate(cvox.DomUtil.getAncestors(cur.start.node))) { - cur = cvox.CursorSelection.fromNode(ancestor).setReversed(r); - if (opt_initialNode) { - return cur; - } - } - - while (cur) { - // Use ObjectWalker's traversal which guarantees us a stable iteration of - // the DOM including returning null at page bounds. - cur = cvox.FindUtil.objectWalker_.next(cur); - var retNode = null; - if (!cur || - (retNode = predicate(cvox.DomUtil.getAncestors(cur.start.node)))) { - return retNode ? cvox.CursorSelection.fromNode(retNode) : null; - } - - // Iframes require inter-frame messaging. - if (cur.start.node.tagName == 'IFRAME') { - return cur; - } - } - return null; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs deleted file mode 100644 index 707fdcb331e..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/find_util_test.unitjs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxFindUtilUnitTest() {} - -CvoxFindUtilUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.FindUtil', - ] -}; - -TEST_F('CvoxFindUtilUnitTest', 'Links', function() { - this.loadDoc(function() {/*! - <div> - <p id="before">Before</p> - <h2>Chapter 1</h2> - <h2>Chapter 2</h2> - <a href="#c3" id="c3" name="chapter_3"></a> - <h2 id="c3_2">Chapter 3</h2> - <h2 id="c4">Chapter 4</h2> - <h2>Chapter 5</h2> - <h2>Chapter 6</h2> - <a href='#c7' id="c7" name="chapter_7"><h2 id="c7_2">Chapter 7</h2></a> - <h2 id="c8">Chapter 8</h2> - <p id="after">After</p> - </div> - */}); - - var sel = cvox.CursorSelection.fromNode($('before')); - - var ret = cvox.FindUtil.findNext(sel, cvox.DomPredicates.linkPredicate); - assertEquals('c3', ret.start.node.id); - ret = cvox.FindUtil.findNext(ret, cvox.DomPredicates.linkPredicate); - assertEquals('c7', ret.start.node.id); - - ret.setReversed(true); - ret = cvox.FindUtil.findNext(ret, cvox.DomPredicates.linkPredicate); - assertEquals('c3', ret.start.node.id); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/focus_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/focus_util.js deleted file mode 100644 index 4c00595a954..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/focus_util.js +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A collection of JavaScript utilities used to manage focus - * within a document. - */ - - -goog.provide('cvox.FocusUtil'); - - -/** - * Utilities for managing focus. - * @constructor - */ -cvox.FocusUtil = function() { -}; - -/** - * Maps whether an input element of specified type accepts text selection or - * not. True if the element does accept text selection, false if it does not. - * This can be used to determine whether a visitor to that element should - * provide interactive text editing to the user. - * From the W3C table of possible type keywords: - * http://www.w3.org/TR/html5/the-input-element.html#attr-input-type - * - * TODO(dmazzoni): merge this with cvox.DomUtil.isInputTypeText - * - * @type {Object} - */ -cvox.FocusUtil.INPUT_TYPE_ACCEPTS_SELECTION_TABLE = { - 'hidden' : false, - 'text' : true, - 'search' : true, - 'tel' : true, - 'url' : true, - 'email' : true, - 'password' : true, - 'datetime' : false, - 'date' : false, - 'month' : false, - 'week' : false, - 'time' : false, - 'datetime-local' : false, - 'number' : false, - 'range' : false, - 'color' : false, - 'checkbox' : false, - 'radio' : false, - 'file' : false, - 'submit' : false, - 'image' : false, - 'reset' : false, - 'button' : false -}; - -/** - * Checks if the currently focused element is a field that accepts text input - * (This can include text fields and selectors) - * - * @return {boolean} True if the currently focused element accepts text input. - */ -cvox.FocusUtil.isFocusInTextInputField = function() { - var activeElement = document.activeElement; - - if (!activeElement) { - return false; - } - - if (activeElement.isContentEditable) { - return true; - } - - if (activeElement.getAttribute('role') == 'textbox') { - return true; - } - - if (activeElement.getAttribute('readOnly') == 'true') { - return false; - } - - if (activeElement.tagName === 'TEXTAREA' || - activeElement.tagName === 'SELECT') { - return true; - } - - if (activeElement.tagName === 'INPUT') { - if (!activeElement.hasAttribute('type')) { - return true; - } else { - var activeType = activeElement.getAttribute('type').toLowerCase(); - return cvox.FocusUtil.INPUT_TYPE_ACCEPTS_SELECTION_TABLE[activeType]; - } - } - return false; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js deleted file mode 100644 index f0a29d87c43..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/focuser.js +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Implements the setFocus function. - */ - -goog.provide('cvox.Focuser'); - -goog.require('cvox.ChromeVoxEventSuspender'); -goog.require('cvox.DomUtil'); - - -/** - * Sets the browser focus to the targetNode or its closest ancestor that is - * focusable. - * - * @param {Node} targetNode The node to move the browser focus to. - * @param {boolean=} opt_focusDescendants Whether or not we check descendants - * of the target node to see if they are focusable. If true, sets focus on the - * first focusable descendant. If false, only sets focus on the targetNode or - * its closest ancestor. Default is false. - */ -cvox.Focuser.setFocus = function(targetNode, opt_focusDescendants) { - // Save the selection because Chrome will lose it if there's a focus or blur. - var sel = window.getSelection(); - var range; - if (sel.rangeCount > 0) { - range = sel.getRangeAt(0); - } - // Blur the currently-focused element if the target node is not a descendant. - if (document.activeElement && - !cvox.DomUtil.isDescendantOfNode(targetNode, document.activeElement)) { - document.activeElement.blur(); - } - - // Video elements should always be focusable. - if (targetNode && (targetNode.constructor == HTMLVideoElement)) { - if (!cvox.DomUtil.isFocusable(targetNode)) { - targetNode.setAttribute('tabIndex', 0); - } - } - - if (opt_focusDescendants && !cvox.DomUtil.isFocusable(targetNode)) { - var focusableDescendant = cvox.DomUtil.findFocusableDescendant(targetNode); - if (focusableDescendant) { - targetNode = focusableDescendant; - } - } else { - // Search up the parent chain until a focusable node is found. - while (targetNode && !cvox.DomUtil.isFocusable(targetNode)) { - targetNode = targetNode.parentNode; - } - } - - // If we found something focusable, focus it - otherwise, blur it. - if (cvox.DomUtil.isFocusable(targetNode)) { - // Don't let the instance of ChromeVox in the parent focus iframe children - // - instead, let the instance of ChromeVox in the iframe focus itself to - // avoid getting trapped in iframes that have no ChromeVox in them. - // This self focusing is performed by calling window.focus() in - // cvox.NavigationManager.prototype.addInterframeListener_ - if (targetNode.tagName != 'IFRAME') { - // setTimeout must be used because there's a bug (in Chrome, I think) - // with .focus() which causes the page to be redrawn incorrectly if - // not in setTimeout. - if (cvox.ChromeVoxEventSuspender.areEventsSuspended()) { - if (cvox.Focuser.shouldEnterSuspendEvents_(targetNode)) { - cvox.ChromeVoxEventSuspender.enterSuspendEvents(); - } - window.setTimeout(function() { - targetNode.focus(); - cvox.ChromeVoxEventSuspender.exitSuspendEvents(); - }, 0); - } - else { - window.setTimeout(function() { - targetNode.focus(); - }, 0); - } - } - } else if (document.activeElement && - document.activeElement.tagName != 'BODY') { - document.activeElement.blur(); - } - - // Restore the selection, unless the focused item is a text box. - if (cvox.DomUtil.isInputTypeText(targetNode)) { - targetNode.select(); - } else if (range) { - sel.removeAllRanges(); - sel.addRange(range); - } -}; - -/** - * Rules for whether or not enterSuspendEvents should be called. - * In general, we should not enterSuspendEvents if the targetNode will get some - * special handlers attached when a focus event is received for it; otherwise, - * the special handlers will not get attached. - * - * @param {Node} targetNode The node that is being focused. - * @return {boolean} True if enterSuspendEvents should be called. - */ -cvox.Focuser.shouldEnterSuspendEvents_ = function(targetNode){ - if (targetNode.constructor && targetNode.constructor == HTMLVideoElement) { - return false; - } - if (targetNode.hasAttribute) { - switch (targetNode.getAttribute('type')) { - case 'time': - case 'date': - case 'week': - case 'month': - return false; - } - } - return true; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/group_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/group_util.js deleted file mode 100644 index d2a8ff1959b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/group_util.js +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Some utilities for defining what groups are. - */ - - -goog.provide('cvox.GroupUtil'); - -goog.require('cvox.AriaUtil'); -goog.require('cvox.DomUtil'); - - -/** - * If a node contains more characters than this, it should not be treated - * as a leaf node by the smart navigation algorithm. - * - * This number was determined by looking at the average number of - * characters in a paragraph: - * http://www.fullondesign.co.uk/design/usability/ - * 285-how-many-characters-per-a-page-is-normal.htm - * and then trying it out on a few popular websites (CNN, BBC, - * Google Search, etc.) and making sure it made sense. - * @type {number} - * @private - * @const - */ -cvox.GroupUtil.MAX_CHARCOUNT_ = 1500; - - -/** - * If a node contains any of these elements, it should not be treated - * as a leaf node by the smart navigation algorithm. - * @type {string} - * @private - * @const - */ -cvox.GroupUtil.BREAKOUT_SELECTOR_ = 'blockquote,' + - 'button,' + - 'code,' + - 'form,' + - 'frame,' + - 'h1,' + - 'h2,' + - 'h3,' + - 'h4,' + - 'h5,' + - 'h6,' + - 'hr,' + - 'iframe,' + - 'input,' + - 'object,' + - 'ol,' + - 'p,' + - 'pre,' + - 'select,' + - 'table,' + - 'tr,' + - 'ul,' + - 'math,' + - // This takes care of MathJax expressions. - 'span.math,' + -// TODO (sorge) Do we want to group all math or only display math? -// '[mode="display"],' + - // Aria widget roles - '[role~="alert ' + - 'alertdialog ' + - 'button ' + - 'checkbox ' + - 'combobox ' + - 'dialog ' + - 'log ' + - 'marquee ' + - 'menubar ' + - 'progressbar ' + - 'radio ' + - 'radiogroup ' + - 'scrollbar ' + - 'slider ' + - 'spinbutton ' + - 'status ' + - 'tab ' + - 'tabpanel ' + - 'textbox ' + - 'toolbar ' + - 'tooltip ' + - 'treeitem ' + - // Aria structure roles - 'article ' + - 'document ' + - 'group ' + - 'heading ' + - 'img ' + - 'list ' + - 'math ' + - 'region ' + - 'row ' + - 'separator"]'; - - -/** - * Returns true if this is a leaf node for groups. - * true for a node => true for all child nodes - * true if node has no children - * @param {!Node} node The node to check. - * @return {boolean} true if this is at the "leaf node" level or lower - * for this granularity. - */ -cvox.GroupUtil.isLeafNode = function(node) { - // TODO (stoarca): Write test to make sure that this function satisfies - // the restriction given above. - if (node.tagName == 'LABEL') { - return cvox.DomUtil.isLeafNode(node); - } - if (cvox.DomUtil.isLeafNode(node)) { - return true; - } - - if (!cvox.DomUtil.isSemanticElt(node)) { - var breakingNodes = node.querySelectorAll( - cvox.GroupUtil.BREAKOUT_SELECTOR_); - - for (var i = 0; i < breakingNodes.length; ++i) { - if (cvox.DomUtil.hasContent(breakingNodes[i])) { - return false; - } - } - } - - if (cvox.AriaUtil.isCompositeControl(node) && - !cvox.DomUtil.isFocusable(node)) { - return false; - } - - var content = cvox.DomUtil.collapseWhitespace( - cvox.DomUtil.getValue(node) + ' ' + - cvox.DomUtil.getName(node)); - if (content.length > cvox.GroupUtil.MAX_CHARCOUNT_) { - return false; - } - - if (content.replace(/\s/g, '') === '') { - // Text only contains whitespace - return false; - } - - return true; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/interframe.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/interframe.js deleted file mode 100644 index 8d7b7fc117f..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/interframe.js +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Tools for interframe communication. To use this class, every - * window that wants to communicate with its child iframes should enumerate - * them using document.getElementsByTagName('iframe'), create an ID to - * associate with that iframe, then call cvox.Interframe.sendIdToIFrame - * on each of them. Then use cvox.Interframe.sendMessageToIFrame to send - * messages to that iframe and cvox.Interframe.addListener to receive - * replies. When a reply is received, it will automatically contain the ID of - * that iframe as a parameter. - * - */ - -goog.provide('cvox.Interframe'); - -goog.require('cvox.ChromeVoxJSON'); -goog.require('cvox.DomUtil'); - -/** - * @constructor - */ -cvox.Interframe = function() { -}; - -/** - * The prefix of all interframe messages. - * @type {string} - * @const - */ -cvox.Interframe.IF_MSG_PREFIX = 'cvox.INTERFRAME:'; - -/** - * The message used to set the ID of a child frame so that it can send replies - * to its parent frame. - * @type {string} - * @const - */ -cvox.Interframe.SET_ID = 'cvox.INTERFRAME_SET_ID'; - -/** - * The message used by a child frame to acknowledge an id was set (sent to its - * parent frame. - * @type {string} - * @const - */ -cvox.Interframe.ACK_SET_ID = 'cvox.INTERFRAME_ACK_SET_ID'; - -/** - * The ID of this window (relative to its parent farme). - * @type {number|string|undefined} - */ -cvox.Interframe.id; - -/** - * Array of functions that have been registered as listeners to interframe - * messages send to this window. - * @type {Array<function(Object)>} - */ -cvox.Interframe.listeners = []; - -/** - * Maps an id to a function which gets called when a frame first sends an ack - * for a set id msg. - @dict {!Object<number|string, function()>} - * @private - */ -cvox.Interframe.idToCallback_ = {}; - -/** - * Flag for unit testing. When false, skips over iframe.contentWindow check - * in sendMessageToIframe. This is needed because in the wild, ChromeVox may - * not have access to iframe.contentWindow due to the same-origin security - * policy. There is no reason to set this outside of a test. - * @type {boolean} - */ -cvox.Interframe.allowAccessToIframeContentWindow = true; - -/** - * Initializes the cvox.Interframe module. (This is called automatically.) - */ -cvox.Interframe.init = function() { - cvox.Interframe.messageListener = function(event) { - if (typeof event.data === 'string' && - event.data.indexOf(cvox.Interframe.IF_MSG_PREFIX) == 0) { - var suffix = event.data.substr(cvox.Interframe.IF_MSG_PREFIX.length); - var message = /** @type {Object} */ ( - cvox.ChromeVoxJSON.parse(suffix)); - if (message['command'] == cvox.Interframe.SET_ID) { - cvox.Interframe.id = message['id']; - message['command'] = cvox.Interframe.ACK_SET_ID; - cvox.Interframe.sendMessageToParentWindow(message); - } else if (message['command'] == cvox.Interframe.ACK_SET_ID) { - cvox.Interframe.id = message['id']; - var callback = cvox.Interframe.idToCallback_[cvox.Interframe.id]; - callback(); - } - for (var i = 0, listener; listener = cvox.Interframe.listeners[i]; i++) { - listener(message); - } - } - return false; - }; - window.addEventListener('message', cvox.Interframe.messageListener, true); -}; - -/** - * Unregister the main window event listener. Intended for clean unit testing; - * normally there's no reason to call this outside of a test. - */ -cvox.Interframe.shutdown = function() { - window.removeEventListener('message', cvox.Interframe.messageListener, true); -}; - -/** - * Register a function to listen to all interframe communication messages. - * Messages from a child frame will have a parameter 'id' that you assigned - * when you called cvox.Interframe.sendIdToIFrame. - * @param {function(Object)} listener The listener function. - */ -cvox.Interframe.addListener = function(listener) { - cvox.Interframe.listeners.push(listener); -}; - -/** - * Send a message to another window. - * @param {Object} message The message to send. - * @param {Window} window The window to receive the message. - */ -cvox.Interframe.sendMessageToWindow = function(message, window) { - var encodedMessage = cvox.Interframe.IF_MSG_PREFIX + - cvox.ChromeVoxJSON.stringify(message, null, null); - window.postMessage(encodedMessage, '*'); -}; - -/** - * Send a message to another iframe. - * @param {Object} message The message to send. The message must have an 'id' - * parameter in order to be sent. - * @param {HTMLIFrameElement} iframe The iframe to send the message to. - */ -cvox.Interframe.sendMessageToIFrame = function(message, iframe) { - if (cvox.Interframe.allowAccessToIframeContentWindow && - iframe.contentWindow) { - cvox.Interframe.sendMessageToWindow(message, iframe.contentWindow); - return; - } - - // A content script can't access window.parent, but the page can, so - // inject a tiny bit of javascript into the page. - var encodedMessage = cvox.Interframe.IF_MSG_PREFIX + - cvox.ChromeVoxJSON.stringify(message, null, null); - var script = document.createElement('script'); - script.type = 'text/javascript'; - - // TODO: Make this logic more like makeNodeReference_ inside api.js - // (line 126) so we can use an attribute instead of a classname - if (iframe.hasAttribute('id') && - document.getElementById(iframe.id) == iframe) { - // Ideally, try to send it based on the iframe's existing id. - script.innerHTML = - 'document.getElementById(decodeURI(\'' + - encodeURI(iframe.id) + '\')).contentWindow.postMessage(decodeURI(\'' + - encodeURI(encodedMessage) + '\'), \'*\');'; - } else { - // If not, add a style name and send it based on that. - var styleName = 'cvox_iframe' + message['id']; - if (iframe.className === '') { - iframe.className = styleName; - } else if (iframe.className.indexOf(styleName) == -1) { - iframe.className += ' ' + styleName; - } - - script.innerHTML = - 'document.getElementsByClassName(decodeURI(\'' + - encodeURI(styleName) + - '\'))[0].contentWindow.postMessage(decodeURI(\'' + - encodeURI(encodedMessage) + '\'), \'*\');'; - } - - // Remove the script so we don't leave any clutter. - document.head.appendChild(script); - window.setTimeout(function() { - document.head.removeChild(script); - }, 1000); -}; - -/** - * Send a message to the parent window of this window, if any. If the parent - * assigned this window an ID, sends back the ID in the reply automatically. - * @param {Object} message The message to send. - */ -cvox.Interframe.sendMessageToParentWindow = function(message) { - if (!cvox.Interframe.isIframe()) { - return; - } - - message['sourceId'] = cvox.Interframe.id; - if (window.parent) { - cvox.Interframe.sendMessageToWindow(message, window.parent); - return; - } - - // A content script can't access window.parent, but the page can, so - // use window.location.href to execute a simple line of javascript in - // the page context. - var encodedMessage = cvox.Interframe.IF_MSG_PREFIX + - cvox.ChromeVoxJSON.stringify(message, null, null); - window.location.href = - 'javascript:window.parent.postMessage(\'' + - encodeURI(encodedMessage) + '\', \'*\');'; -}; - -/** - * Send the given ID to a child iframe. - * @param {number|string} id The ID you want to receive in replies from - * this iframe. - * @param {HTMLIFrameElement} iframe The iframe to assign. - * @param {function()=} opt_callback Called when a ack msg arrives from the - *frame. - */ -cvox.Interframe.sendIdToIFrame = function(id, iframe, opt_callback) { - if (opt_callback) { - cvox.Interframe.idToCallback_[id] = opt_callback; - } - var message = {'command': cvox.Interframe.SET_ID, 'id': id}; - cvox.Interframe.sendMessageToIFrame(message, iframe); -}; - -/** - * Returns true if inside iframe - * @return {boolean} true if inside iframe. - */ -cvox.Interframe.isIframe = function() { - return (window != window.parent); -}; - -cvox.Interframe.init(); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/key_sequence.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/key_sequence.js deleted file mode 100644 index 0a6f3d51c01..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/key_sequence.js +++ /dev/null @@ -1,633 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A JavaScript class that represents a sequence of keys entered - * by the user. - */ - - -goog.provide('cvox.KeySequence'); - -goog.require('cvox.ChromeVox'); -goog.require('cvox.PlatformFilter'); - - -/** - * A class to represent a sequence of keys entered by a user or affiliated with - * a ChromeVox command. - * This class can represent the data from both types of key sequences: - * - * COMMAND KEYS SPECIFIED IN A KEYMAP: - * - Two discrete keys (at most): [Down arrow], [A, A] or [O, W] etc. Can - * specify one or both. - * - Modifiers (like ctrl, alt, meta, etc) - * - Whether or not the ChromeVox modifier key is required with the command. - * - * USER INPUT: - * - Two discrete keys (at most): [Down arrow], [A, A] or [O, W] etc. - * - Modifiers (like ctlr, alt, meta, etc) - * - Whether or not the ChromeVox modifier key was active when the keys were - * entered. - * - Whether or not a prefix key was entered before the discrete keys. - * - Whether sticky mode was active. - * @param {Event|Object} originalEvent The original key event entered by a user. - * The originalEvent may or may not have parameters stickyMode and keyPrefix - * specified. We will also accept an event-shaped object. - * @param {boolean=} opt_cvoxModifier Whether or not the ChromeVox modifier key - * is active. If not specified, we will try to determine whether the modifier - * was active by looking at the originalEvent. - * @param {boolean=} opt_skipStripping Skips stripping of ChromeVox modifiers - * from key events when the cvox modifiers are set. Defaults to false. - * @param {boolean=} opt_doubleTap Whether this is triggered via double tap. - * @constructor - */ -cvox.KeySequence = function( - originalEvent, opt_cvoxModifier, opt_skipStripping, opt_doubleTap) { - /** @type {boolean} */ - this.doubleTap = !!opt_doubleTap; - - /** @type {cvox.PlatformFilter} */ - this.platformFilter; - - if (opt_cvoxModifier == undefined) { - this.cvoxModifier = this.isCVoxModifierActive(originalEvent); - } else { - this.cvoxModifier = opt_cvoxModifier; - } - this.stickyMode = !!originalEvent['stickyMode']; - this.prefixKey = !!originalEvent['keyPrefix']; - this.skipStripping = !!opt_skipStripping; - - if (this.stickyMode && this.prefixKey) { - throw 'Prefix key and sticky mode cannot both be enabled: ' + originalEvent; - } - - var event = this.resolveChromeOSSpecialKeys_(originalEvent); - - // TODO (rshearer): We should take the user out of sticky mode if they - // try to use the CVox modifier or prefix key. - - /** - * Stores the key codes and modifiers for the keys in the key sequence. - * TODO(rshearer): Consider making this structure an array of minimal - * keyEvent-like objects instead so we don't have to worry about what happens - * when ctrlKey.length is different from altKey.length. - * - * NOTE: If a modifier key is pressed by itself, we will store the keyCode - * *and* set the appropriate modKey to be true. This mirrors the way key - * events are created on Mac and Windows. For example, if the Meta key was - * pressed by itself, the keys object will have: - * {metaKey: [true], keyCode:[91]} - * - * @type {Object} - */ - this.keys = { - ctrlKey: [], - searchKeyHeld: [], - altKey: [], - altGraphKey: [], - shiftKey: [], - metaKey: [], - keyCode: [] - }; - - this.extractKey_(event); -}; - - -// TODO(dtseng): This is incomplete; pull once we have appropriate libs. -/** - * Maps a keypress keycode to a keydown or keyup keycode. - * @type {Object<number, number>} - */ -cvox.KeySequence.KEY_PRESS_CODE = { - 39: 222, - 44: 188, - 45: 189, - 46: 190, - 47: 191, - 59: 186, - 91: 219, - 92: 220, - 93: 221 -}; - -/** - * A cache of all key sequences that have been set as double-tappable. We need - * this cache because repeated key down computations causes ChromeVox to become - * less responsive. This list is small so we currently use an array. - * @type {!Array<cvox.KeySequence>} - */ -cvox.KeySequence.doubleTapCache = []; - - -/** - * Adds an additional key onto the original sequence, for use when the user - * is entering two shortcut keys. This happens when the user presses a key, - * releases it, and then presses a second key. Those two keys together are - * considered part of the sequence. - * @param {Event|Object} additionalKeyEvent The additional key to be added to - * the original event. Should be an event or an event-shaped object. - * @return {boolean} Whether or not we were able to add a key. Returns false - * if there are already two keys attached to this event. - */ -cvox.KeySequence.prototype.addKeyEvent = function(additionalKeyEvent) { - if (this.keys.keyCode.length > 1) { - return false; - } - this.extractKey_(additionalKeyEvent); - return true; -}; - - -/** - * Check for equality. Commands are matched based on the actual key codes - * involved and on whether or not they both require a ChromeVox modifier key. - * - * If sticky mode or a prefix is active on one of the commands but not on - * the other, then we try and match based on key code first. - * - If both commands have the same key code and neither of them have the - * ChromeVox modifier active then we have a match. - * - Next we try and match with the ChromeVox modifier. If both commands have - * the same key code, and one of them has the ChromeVox modifier and the other - * has sticky mode or an active prefix, then we also have a match. - * @param {!cvox.KeySequence} rhs The key sequence to compare against. - * @return {boolean} True if equal. - */ -cvox.KeySequence.prototype.equals = function(rhs) { - // Check to make sure the same keys with the same modifiers were pressed. - if (!this.checkKeyEquality_(rhs)) { - return false; - } - - if (this.doubleTap != rhs.doubleTap) { - return false; - } - - // So now we know the actual keys are the same. - // If they both have the ChromeVox modifier, or they both don't have the - // ChromeVox modifier, then they are considered equal. - if (this.cvoxModifier === rhs.cvoxModifier) { - return true; - } - - // So only one of them has the ChromeVox modifier. If the one that doesn't - // have the ChromeVox modifier has sticky mode or the prefix key then the - // keys are still considered equal. - var unmodified = this.cvoxModifier ? rhs : this; - return unmodified.stickyMode || unmodified.prefixKey; -}; - - -/** - * Utility method that extracts the key code and any modifiers from a given - * event and adds them to the object map. - * @param {Event|Object} keyEvent The keyEvent or event-shaped object to extract - * from. - * @private - */ -cvox.KeySequence.prototype.extractKey_ = function(keyEvent) { - for (var prop in this.keys) { - if (prop == 'keyCode') { - var keyCode; - // TODO (rshearer): This is temporary until we find a library that can - // convert between ASCII charcodes and keycodes. - if (keyEvent.type == 'keypress' && keyEvent[prop] >= 97 && - keyEvent[prop] <= 122) { - // Alphabetic keypress. Convert to the upper case ASCII code. - keyCode = keyEvent[prop] - 32; - } else if (keyEvent.type == 'keypress') { - keyCode = cvox.KeySequence.KEY_PRESS_CODE[keyEvent[prop]]; - } - this.keys[prop].push(keyCode || keyEvent[prop]); - } else { - if (this.isKeyModifierActive(keyEvent, prop)) { - this.keys[prop].push(true); - } else { - this.keys[prop].push(false); - } - } - } - if (this.cvoxModifier) { - this.rationalizeKeys_(); - } -}; - - -/** - * Rationalizes the key codes and the ChromeVox modifier for this keySequence. - * This means we strip out the key codes and key modifiers stored for this - * KeySequence that are also present in the ChromeVox modifier. For example, if - * the ChromeVox modifier keys are Ctrl+Alt, and we've determined that the - * ChromeVox modifier is active (meaning the user has pressed Ctrl+Alt), we - * don't want this.keys.ctrlKey = true also because that implies that this - * KeySequence involves the ChromeVox modifier and the ctrl key being held down - * together, which doesn't make any sense. - * @private - */ -cvox.KeySequence.prototype.rationalizeKeys_ = function() { - if (this.skipStripping) { - return; - } - - // TODO (rshearer): This is a hack. When the modifier key becomes customizable - // then we will not have to deal with strings here. - var modifierKeyCombo = cvox.ChromeVox.modKeyStr.split(/\+/g); - - var index = this.keys.keyCode.length - 1; - // For each modifier that is part of the CVox modifier, remove it from keys. - if (modifierKeyCombo.indexOf('Ctrl') != -1) { - this.keys.ctrlKey[index] = false; - } - if (modifierKeyCombo.indexOf('Alt') != -1) { - this.keys.altKey[index] = false; - } - if (modifierKeyCombo.indexOf('Shift') != -1) { - this.keys.shiftKey[index] = false; - } - var metaKeyName = this.getMetaKeyName_(); - if (modifierKeyCombo.indexOf(metaKeyName) != -1) { - if (metaKeyName == 'Search') { - this.keys.searchKeyHeld[index] = false; - // TODO(dmazzoni): http://crbug.com/404763 Get rid of the code that - // tracks the search key and just use meta everywhere. - this.keys.metaKey[index] = false; - } else if (metaKeyName == 'Cmd' || metaKeyName == 'Win') { - this.keys.metaKey[index] = false; - } - } -}; - - -/** - * Get the user-facing name for the meta key (keyCode = 91), which varies - * depending on the platform. - * @return {string} The user-facing string name for the meta key. - * @private - */ -cvox.KeySequence.prototype.getMetaKeyName_ = function() { - if (cvox.ChromeVox.isChromeOS) { - return 'Search'; - } else if (cvox.ChromeVox.isMac) { - return 'Cmd'; - } else { - return 'Win'; - } -}; - - -/** - * Utility method that checks for equality of the modifiers (like shift and alt) - * and the equality of key codes. - * @param {!cvox.KeySequence} rhs The key sequence to compare against. - * @return {boolean} True if the modifiers and key codes in the key sequence are - * the same. - * @private - */ -cvox.KeySequence.prototype.checkKeyEquality_ = function(rhs) { - for (var i in this.keys) { - for (var j = this.keys[i].length; j--;) { - if (this.keys[i][j] !== rhs.keys[i][j]) - return false; - } - } - return true; -}; - - -/** - * Gets first key code - * @return {number} The first key code. - */ -cvox.KeySequence.prototype.getFirstKeyCode = function() { - return this.keys.keyCode[0]; -}; - - -/** - * Gets the number of keys in the sequence. Should be 1 or 2. - * @return {number} The number of keys in the sequence. - */ -cvox.KeySequence.prototype.length = function() { - return this.keys.keyCode.length; -}; - - - -/** - * Checks if the specified key code represents a modifier key, i.e. Ctrl, Alt, - * Shift, Search (on ChromeOS) or Meta. - * - * @param {number} keyCode key code. - * @return {boolean} true if it is a modifier keycode, false otherwise. - */ -cvox.KeySequence.prototype.isModifierKey = function(keyCode) { - // Shift, Ctrl, Alt, Search/LWin - return keyCode == 16 || keyCode == 17 || keyCode == 18 || keyCode == 91 || - keyCode == 93; -}; - - -/** - * Determines whether the Cvox modifier key is active during the keyEvent. - * @param {Event|Object} keyEvent The keyEvent or event-shaped object to check. - * @return {boolean} Whether or not the modifier key was active during the - * keyEvent. - */ -cvox.KeySequence.prototype.isCVoxModifierActive = function(keyEvent) { - // TODO (rshearer): Update this when the modifier key becomes customizable - var modifierKeyCombo = cvox.ChromeVox.modKeyStr.split(/\+/g); - - // For each modifier that is held down, remove it from the combo. - // If the combo string becomes empty, then the user has activated the combo. - if (this.isKeyModifierActive(keyEvent, 'ctrlKey')) { - modifierKeyCombo = modifierKeyCombo.filter(function(modifier) { - return modifier != 'Ctrl'; - }); - } - if (this.isKeyModifierActive(keyEvent, 'altKey')) { - modifierKeyCombo = modifierKeyCombo.filter(function(modifier) { - return modifier != 'Alt'; - }); - } - if (this.isKeyModifierActive(keyEvent, 'shiftKey')) { - modifierKeyCombo = modifierKeyCombo.filter(function(modifier) { - return modifier != 'Shift'; - }); - } - if (this.isKeyModifierActive(keyEvent, 'metaKey') || - this.isKeyModifierActive(keyEvent, 'searchKeyHeld')) { - var metaKeyName = this.getMetaKeyName_(); - modifierKeyCombo = modifierKeyCombo.filter(function(modifier) { - return modifier != metaKeyName; - }); - } - return (modifierKeyCombo.length == 0); -}; - - -/** - * Determines whether a particular key modifier (for example, ctrl or alt) is - * active during the keyEvent. - * @param {Event|Object} keyEvent The keyEvent or Event-shaped object to check. - * @param {string} modifier The modifier to check. - * @return {boolean} Whether or not the modifier key was active during the - * keyEvent. - */ -cvox.KeySequence.prototype.isKeyModifierActive = function(keyEvent, modifier) { - // We need to check the key event modifier and the keyCode because Linux will - // not set the keyEvent.modKey property if it is the modKey by itself. - // This bug filed as crbug.com/74044 - switch (modifier) { - case 'ctrlKey': - return (keyEvent.ctrlKey || keyEvent.keyCode == 17); - break; - case 'altKey': - return (keyEvent.altKey || (keyEvent.keyCode == 18)); - break; - case 'shiftKey': - return (keyEvent.shiftKey || (keyEvent.keyCode == 16)); - break; - case 'metaKey': - return (keyEvent.metaKey || (keyEvent.keyCode == 91)); - break; - case 'searchKeyHeld': - return ((cvox.ChromeVox.isChromeOS && keyEvent.keyCode == 91) || - keyEvent['searchKeyHeld']); - break; - } - return false; -}; - -/** - * Returns if any modifier is active in this sequence. - * @return {boolean} The result. - */ -cvox.KeySequence.prototype.isAnyModifierActive = function() { - for (var modifierType in this.keys) { - for (var i = 0; i < this.length(); i++) { - if (this.keys[modifierType][i] && modifierType != 'keyCode') { - return true; - } - } - } - return false; -}; - - -/** - * Creates a KeySequence event from a generic object. - * @param {Object} sequenceObject The object. - * @return {cvox.KeySequence} The created KeySequence object. - */ -cvox.KeySequence.deserialize = function(sequenceObject) { - var firstSequenceEvent = {}; - - firstSequenceEvent['stickyMode'] = (sequenceObject.stickyMode == undefined) ? - false : sequenceObject.stickyMode; - firstSequenceEvent['prefixKey'] = (sequenceObject.prefixKey == undefined) ? - false : sequenceObject.prefixKey; - - - var secondKeyPressed = sequenceObject.keys.keyCode.length > 1; - var secondSequenceEvent = {}; - - for (var keyPressed in sequenceObject.keys) { - firstSequenceEvent[keyPressed] = sequenceObject.keys[keyPressed][0]; - if (secondKeyPressed) { - secondSequenceEvent[keyPressed] = sequenceObject.keys[keyPressed][1]; - } - } - - var keySeq = new cvox.KeySequence(firstSequenceEvent, - sequenceObject.cvoxModifier, true, sequenceObject.doubleTap); - if (secondKeyPressed) { - cvox.ChromeVox.sequenceSwitchKeyCodes.push( - new cvox.KeySequence(firstSequenceEvent, sequenceObject.cvoxModifier)); - keySeq.addKeyEvent(secondSequenceEvent); - } - - if (sequenceObject.doubleTap) { - cvox.KeySequence.doubleTapCache.push(keySeq); - } - - return keySeq; -}; - - -/** - * Creates a KeySequence event from a given string. The string should be in the - * standard key sequence format described in keyUtil.keySequenceToString and - * used in the key map JSON files. - * @param {string} keyStr The string representation of a key sequence. - * @return {!cvox.KeySequence} The created KeySequence object. - */ -cvox.KeySequence.fromStr = function(keyStr) { - var sequenceEvent = {}; - var secondSequenceEvent = {}; - - var secondKeyPressed; - if (keyStr.indexOf('>') == -1) { - secondKeyPressed = false; - } else { - secondKeyPressed = true; - } - - var cvoxPressed = false; - sequenceEvent['stickyMode'] = false; - sequenceEvent['prefixKey'] = false; - - var tokens = keyStr.split('+'); - for (var i = 0; i < tokens.length; i++) { - var seqs = tokens[i].split('>'); - for (var j = 0; j < seqs.length; j++) { - if (seqs[j].charAt(0) == '#') { - var keyCode = parseInt(seqs[j].substr(1), 10); - if (j > 0) { - secondSequenceEvent['keyCode'] = keyCode; - } else { - sequenceEvent['keyCode'] = keyCode; - } - } - var keyName = seqs[j]; - if (seqs[j].length == 1) { - // Key is A/B/C...1/2/3 and we don't need to worry about setting - // modifiers. - if (j > 0) { - secondSequenceEvent['keyCode'] = seqs[j].charCodeAt(0); - } else { - sequenceEvent['keyCode'] = seqs[j].charCodeAt(0); - } - } else { - // Key is a modifier key - if (j > 0) { - cvox.KeySequence.setModifiersOnEvent_(keyName, secondSequenceEvent); - if (keyName == 'Cvox') { - cvoxPressed = true; - } - } else { - cvox.KeySequence.setModifiersOnEvent_(keyName, sequenceEvent); - if (keyName == 'Cvox') { - cvoxPressed = true; - } - } - } - } - } - var keySeq = new cvox.KeySequence(sequenceEvent, cvoxPressed); - if (secondKeyPressed) { - keySeq.addKeyEvent(secondSequenceEvent); - } - return keySeq; -}; - - -/** - * Utility method for populating the modifiers on an event object that will be - * used to create a KeySequence. - * @param {string} keyName A particular modifier key name (such as 'Ctrl'). - * @param {Object} seqEvent The event to populate. - * @private - */ -cvox.KeySequence.setModifiersOnEvent_ = function(keyName, seqEvent) { - if (keyName == 'Ctrl') { - seqEvent['ctrlKey'] = true; - seqEvent['keyCode'] = 17; - } else if (keyName == 'Alt') { - seqEvent['altKey'] = true; - seqEvent['keyCode'] = 18; - } else if (keyName == 'Shift') { - seqEvent['shiftKey'] = true; - seqEvent['keyCode'] = 16; - } else if (keyName == 'Search') { - seqEvent['searchKeyHeld'] = true; - seqEvent['keyCode'] = 91; - } else if (keyName == 'Cmd') { - seqEvent['metaKey'] = true; - seqEvent['keyCode'] = 91; - } else if (keyName == 'Win') { - seqEvent['metaKey'] = true; - seqEvent['keyCode'] = 91; - } else if (keyName == 'Insert') { - seqEvent['keyCode'] = 45; - } -}; - - -/** - * Used to resolve special ChromeOS keys (see link for more detail). - * http://crbug.com/162268 - * @param {Object} originalEvent The event. - * @return {Object} The resolved event. - * @private - */ -cvox.KeySequence.prototype.resolveChromeOSSpecialKeys_ = - function(originalEvent) { - if (!this.cvoxModifier || this.stickyMode || this.prefixKey || - !cvox.ChromeVox.isChromeOS) { - return originalEvent; - } - var evt = {}; - for (var key in originalEvent) { - evt[key] = originalEvent[key]; - } - switch (evt['keyCode']) { - case 33: // Page up. - evt['keyCode'] = 38; // Up arrow. - break; - case 34: // Page down. - evt['keyCode'] = 40; // Down arrow. - break; - case 35: // End. - evt['keyCode'] = 39; // Right arrow. - break; - case 36: // Home. - evt['keyCode'] = 37; // Left arrow. - break; - case 45: // Insert. - evt['keyCode'] = 190; // Period. - break; - case 46: // Delete. - evt['keyCode'] = 8; // Backspace. - break; - case 112: // F1. - evt['keyCode'] = 49; // 1. - break; - case 113: // F2. - evt['keyCode'] = 50; // 2. - break; - case 114: // F3. - evt['keyCode'] = 51; // 3. - break; - case 115: // F4. - evt['keyCode'] = 52; // 4. - break; - case 116: // F5. - evt['keyCode'] = 53; // 5. - break; - case 117: // F6. - evt['keyCode'] = 54; // 6. - break; - case 118: // F7. - evt['keyCode'] = 55; // 7. - break; - case 119: // F8. - evt['keyCode'] = 56; // 8. - break; - case 120: // F9. - evt['keyCode'] = 57; // 9. - break; - case 121: // F10. - evt['keyCode'] = 48; // 0. - break; - case 122: // F11 - evt['keyCode'] = 189; // Hyphen. - break; - case 123: // F12 - evt['keyCode'] = 187; // Equals. - break; - } - return evt; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs deleted file mode 100644 index 14419b5595c..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/key_sequence_test.unitjs +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxKeySequenceUnitTest() {} - -CvoxKeySequenceUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.ChromeVox', - 'cvox.KeySequence', - ], - - /** - * Create mock event object. - * @param {number} keyCode The event key code (i.e. 13 for Enter). - * @param {{altGraphKey: boolean=, - * altKey: boolean=, - * ctrlKey: boolean=, - * metaKey: boolean=, - * searchKeyHeld: boolean=, - * shiftKey: boolean=, - * stickyMode: boolean=, - * prefixKey: boolean=}} eventParams The parameters on the event. - * altGraphKey: Whether or not the altGraph key was held down. - * altKey: Whether or not the alt key was held down. - * ctrlKey: Whether or not the ctrl key was held down. - * metaKey: Whether or not the meta key was held down. - * searchKeyHeld: Whether or not the search key was held down. - * shiftKey: Whether or not the shift key was held down. - * stickyMode: Whether or not sticky mode is enabled. - * prefixKey: Whether or not the prefix key was entered. - * @return {Object} The mock event. - */ - createMockEvent: function(keyCode, eventParams) { - var mockEvent = {}; - mockEvent.keyCode = keyCode; - - if (eventParams == null) { - return mockEvent; - } - if (eventParams.hasOwnProperty('altGraphKey')) { - mockEvent.altGraphKey = eventParams.altGraphKey; - } - if (eventParams.hasOwnProperty('altKey')) { - mockEvent.altKey = eventParams.altKey; - } - if (eventParams.hasOwnProperty('ctrlKey')) { - mockEvent.ctrlKey = eventParams.ctrlKey; - } - if (eventParams.hasOwnProperty('metaKey')) { - mockEvent.metaKey = eventParams.metaKey; - } - if (eventParams.hasOwnProperty('shiftKey')) { - mockEvent.shiftKey = eventParams.shiftKey; - } - - if (eventParams.hasOwnProperty('searchKeyHeld')) { - mockEvent.searchKeyHeld = eventParams.searchKeyHeld; - } - if (eventParams.hasOwnProperty('stickyMode')) { - mockEvent.stickyMode = eventParams.stickyMode; - } - if (eventParams.hasOwnProperty('prefixKey')) { - mockEvent.keyPrefix = eventParams.prefixKey; - } - - return mockEvent; - }, - - /** @override */ - setUp: function() { - // Set up mock ChromeVox modifier - cvox.ChromeVox.modKeyStr = 'Alt'; - - // Use these mock events in the tests: - - // Down arrow, no modifiers - this.downArrowEvent = this.createMockEvent(40); - - // Down arrow key with alt held down. We specified 'Alt' as the - // mock ChromeVox modifier string, so this means that KeySequence - // should interpret this as the ChromeVox modifier being active. - this.altDownArrowEvent = this.createMockEvent(40, {altKey: true}); - - // Right arrow, no modifiers - this.rightArrowEvent = this.createMockEvent(39); - - // Ctrl key, no modifiers - this.ctrlEvent = this.createMockEvent(17); - - // Ctrl key with sticky mode - this.ctrlStickyEvent = this.createMockEvent(17, {stickyMode: true}); - - // Ctrl key with prefix mode - this.ctrlPrefixEvent = this.createMockEvent(17, {prefixKey: true}); - - // 'a' key, no modifiers - this.aEvent = this.createMockEvent(65); - - // 'a' key with ctrl held down - this.ctrlAEvent = this.createMockEvent(65, {ctrlKey: true}); - - // 'a' key with meta held down - this.metaAEvent = this.createMockEvent(65, {metaKey: true}); - - // 'a' key with shift held down - this.shiftAEvent = this.createMockEvent(65, {shiftKey: true}); - - // 'a' key with alt (which is the mock ChromeVox modifier) and shift held - // down. - this.altShiftAEvent = this.createMockEvent(65, {altKey: true, - shiftKey: true}); - - // 'a' key with shift and prefix held down - this.shiftAPrefixEvent = this.createMockEvent(65, {shiftKey: true, - prefixKey: true}); - - // 'a' key with shift and sticky mode - this.shiftAStickyEvent = this.createMockEvent(65, {shiftKey: true, - stickyMode: true}); - - // 'a' key with sticky mode - this.aEventSticky = this.createMockEvent(65, {stickyMode: true}); - - // 'a' key with prefix key - this.aEventPrefix = this.createMockEvent(65, {prefixKey: true}); - - // 'a' key with alt (which is the mock ChromeVox modifier) held down - this.altAEvent = this.createMockEvent(65, {altKey: true}); - - // 'b' key, no modifiers - this.bEvent = this.createMockEvent(66); - - // 'b' key, with ctrl held down - this.ctrlBEvent = this.createMockEvent(66, {ctrlKey: true}); - - // 'c' key, no modifiers - this.cEvent = this.createMockEvent(67); - - // Shift key with ctrl held down - this.ctrlShiftEvent = this.createMockEvent(60, {ctrlKey: true}); - - // Ctrl key with shift held down - this.shiftCtrlEvent = this.createMockEvent(17, {shiftKey: true}); - } -}; - -TEST_F('CvoxKeySequenceUnitTest', 'SimpleSequenceNoModifier', function() { - var downKey = new cvox.KeySequence(this.downArrowEvent, false); - - assertEqualsJSON([40], downKey.keys.keyCode); - assertFalse(downKey.stickyMode); - assertFalse(downKey.prefixKey); - assertFalse(downKey.cvoxModifier); - - assertEqualsJSON([false], downKey.keys.altGraphKey); - assertEqualsJSON([false], downKey.keys.altKey); - assertEqualsJSON([false], downKey.keys.ctrlKey); - assertEqualsJSON([false], downKey.keys.metaKey); - assertEqualsJSON([false], downKey.keys.searchKeyHeld); - assertEqualsJSON([false], downKey.keys.shiftKey); - - assertEquals(1, downKey.length()); -}); - - -/** Test another key sequence, this time with the modifier */ -TEST_F('CvoxKeySequenceUnitTest', 'SimpleSequenceWithModifier', function() { - var downKey = new cvox.KeySequence(this.downArrowEvent, true); - - assertEqualsJSON([40], downKey.keys.keyCode); - assertFalse(downKey.stickyMode); - assertFalse(downKey.prefixKey); - assertTrue(downKey.cvoxModifier); - - assertEqualsJSON([false], downKey.keys.altGraphKey); - assertEqualsJSON([false], downKey.keys.altKey); - assertEqualsJSON([false], downKey.keys.ctrlKey); - assertEqualsJSON([false], downKey.keys.metaKey); - assertEqualsJSON([false], downKey.keys.searchKeyHeld); - assertEqualsJSON([false], downKey.keys.shiftKey); - - assertEquals(1, downKey.length()); -}); - - -/** Test a key sequence that includes the modifier */ -TEST_F('CvoxKeySequenceUnitTest', 'ModifiedSequence', function() { - var cvoxDownKey = new cvox.KeySequence(this.altDownArrowEvent, true); - - assertEqualsJSON([40], cvoxDownKey.keys.keyCode); - assertFalse(cvoxDownKey.stickyMode); - assertFalse(cvoxDownKey.prefixKey); - assertTrue(cvoxDownKey.cvoxModifier); - - assertEqualsJSON([false], cvoxDownKey.keys.altGraphKey); - assertEqualsJSON([false], cvoxDownKey.keys.altKey); - assertEqualsJSON([false], cvoxDownKey.keys.ctrlKey); - assertEqualsJSON([false], cvoxDownKey.keys.metaKey); - assertEqualsJSON([false], cvoxDownKey.keys.searchKeyHeld); - assertEqualsJSON([false], cvoxDownKey.keys.shiftKey); - - assertEquals(1, cvoxDownKey.length()); -}); - - -/** - * Test equality - Ctrl key vs. Ctrl key with sticky mode on - * These should be equal because Ctrl should still function even with - * sticky mode on. -*/ -TEST_F('CvoxKeySequenceUnitTest', 'StickyEquality', function() { - var ctrlKey = new cvox.KeySequence(this.ctrlEvent, false); - var ctrlSticky = new cvox.KeySequence(this.ctrlStickyEvent, false); - - assertTrue(ctrlKey.equals(ctrlSticky)); -}); - - -/** - * Test equality - 'a' key with Shift modifier vs. 'a' key without Shift - * modifier. - * These should not be equal because they do not have the same modifiers. -*/ -TEST_F('CvoxKeySequenceUnitTest', 'ShiftEquality', function() { - var aKey = new cvox.KeySequence(this.aEvent, false); - var shiftA = new cvox.KeySequence(this.shiftAEvent, false); - - assertFalse(aKey.equals(shiftA)); -}); - - -/** - * Test equality - 'a' with ChromeVox modifier specified, 'a' with sticky mode - * on, 'a' with prefix key, and 'a' with ChromeVox modifier held down. These - * should all be equal to each other. - */ -TEST_F('CvoxKeySequenceUnitTest', 'FourWayEquality', function() { - var commandSequence = new cvox.KeySequence(this.aEvent, true); - var stickySequence = new cvox.KeySequence(this.aEventSticky, false); - var prefixSequence = new cvox.KeySequence(this.aEventPrefix, false); - var cvoxModifierSequence = new cvox.KeySequence(this.altAEvent); - - assertTrue(commandSequence.equals(stickySequence)); - assertTrue(commandSequence.equals(prefixSequence)); - assertTrue(commandSequence.equals(cvoxModifierSequence)); - - assertTrue(stickySequence.equals(commandSequence)); - assertTrue(stickySequence.equals(prefixSequence)); - assertTrue(stickySequence.equals(cvoxModifierSequence)); - - assertTrue(prefixSequence.equals(commandSequence)); - assertTrue(prefixSequence.equals(stickySequence)); - assertTrue(prefixSequence.equals(cvoxModifierSequence)); - - assertTrue(cvoxModifierSequence.equals(commandSequence)); - assertTrue(cvoxModifierSequence.equals(stickySequence)); - assertTrue(cvoxModifierSequence.equals(prefixSequence)); -}); - - -/** - * Test equality - 'a' key with Shift modifier and prefix vs. 'a' key with Shift - * modifier and sticky mode vs. 'a' key with Shift modifier and ChromeVox - * modifier specified vs. 'a' key with ChromeVox modifier held down. - * These should all be equal to each other.. -*/ -TEST_F('CvoxKeySequenceUnitTest', 'ShiftPrefixEquality', function() { - var shiftAWithModifier = new cvox.KeySequence(this.shiftAEvent, true); - var shiftAWithPrefix = new cvox.KeySequence(this.shiftAPrefixEvent, false); - var shiftASticky = new cvox.KeySequence(this.shiftAStickyEvent, false); - var cvoxShiftA = new cvox.KeySequence(this.altShiftAEvent); - - assertTrue(shiftAWithModifier.equals(shiftAWithPrefix)); - assertTrue(shiftAWithModifier.equals(shiftASticky)); - assertTrue(shiftAWithModifier.equals(cvoxShiftA)); - - assertTrue(shiftAWithPrefix.equals(shiftAWithModifier)); - assertTrue(shiftAWithPrefix.equals(shiftASticky)); - assertTrue(shiftAWithPrefix.equals(cvoxShiftA)); - - assertTrue(shiftASticky.equals(shiftAWithPrefix)); - assertTrue(shiftASticky.equals(shiftAWithModifier)); - assertTrue(shiftASticky.equals(cvoxShiftA)); - - assertTrue(cvoxShiftA.equals(shiftAWithModifier)); - assertTrue(cvoxShiftA.equals(shiftAWithPrefix)); - assertTrue(cvoxShiftA.equals(shiftASticky)); -}); - - -/** - * Test inequality - 'a' with modifier key vs. 'a' without modifier key. - * These should not be equal. -*/ -TEST_F('CvoxKeySequenceUnitTest', 'Inequality', function() { - var aNoModifier = new cvox.KeySequence(this.aEvent, false); - var aWithModifier = new cvox.KeySequence(this.aEvent, true); - - assertFalse(aNoModifier.equals(aWithModifier)); - assertFalse(aWithModifier.equals(aNoModifier)); -}); - - -/** - * Test equality - adding an additional key onto a sequence. - */ -TEST_F('CvoxKeySequenceUnitTest', 'CvoxCtrl', function() { - var cvoxCtrlSequence = new cvox.KeySequence(this.ctrlEvent, true); - assertTrue(cvoxCtrlSequence.addKeyEvent(this.rightArrowEvent)); - - assertEquals(2, cvoxCtrlSequence.length()); - - // Can't add more than two key events. - assertFalse(cvoxCtrlSequence.addKeyEvent(this.rightArrowEvent)); - - var cvoxCtrlStickySequence = new cvox.KeySequence(this.ctrlStickyEvent, - false); - assertTrue(cvoxCtrlStickySequence.addKeyEvent(this.rightArrowEvent)); - - var mockCtrlPrefixSequence = new cvox.KeySequence(this.ctrlPrefixEvent, - false); - assertTrue(mockCtrlPrefixSequence.addKeyEvent(this.rightArrowEvent)); - - assertTrue(cvoxCtrlSequence.equals(cvoxCtrlStickySequence)); - assertTrue(cvoxCtrlStickySequence.equals(cvoxCtrlSequence)); - - assertTrue(cvoxCtrlSequence.equals(mockCtrlPrefixSequence)); - assertTrue(mockCtrlPrefixSequence.equals(cvoxCtrlSequence)); - - assertTrue(cvoxCtrlStickySequence.equals(mockCtrlPrefixSequence)); - assertTrue(mockCtrlPrefixSequence.equals(cvoxCtrlStickySequence)); -}); - - -/** - * Test for inequality - key sequences in different orders. - */ -TEST_F('CvoxKeySequenceUnitTest', 'DifferentSequences', function() { - var cvoxBSequence = new cvox.KeySequence(this.bEvent, true); - assertTrue(cvoxBSequence.addKeyEvent(this.cEvent)); - - var cvoxCSequence = new cvox.KeySequence(this.cEvent, false); - assertTrue(cvoxCSequence.addKeyEvent(this.bEvent)); - - assertFalse(cvoxBSequence.equals(cvoxCSequence)); - assertFalse(cvoxCSequence.equals(cvoxBSequence)); -}); - - -/** - * Tests modifiers (ctrl, alt, etc) - if two sequences have different modifiers - * held down then they aren't equal. - */ -TEST_F('CvoxKeySequenceUnitTest', 'MoreModifiers', function() { - var ctrlASequence = new cvox.KeySequence(this.ctrlAEvent, false); - var ctrlModifierKeyASequence = new cvox.KeySequence(this.ctrlAEvent, true); - - var ctrlBSequence = new cvox.KeySequence(this.ctrlBEvent, false); - - var metaASequence = new cvox.KeySequence(this.metaAEvent, false); - - assertFalse(ctrlASequence.equals(metaASequence)); - assertFalse(ctrlASequence.equals(ctrlModifierKeyASequence)); - assertFalse(ctrlASequence.equals(ctrlBSequence)); -}); - - -/** - * Tests modifier (ctrl, alt, etc) order - if two sequences have the same - * modifiers but held down in a different order then they aren't equal. - */ -TEST_F('CvoxKeySequenceUnitTest', 'ModifierOrder', function() { - var ctrlShiftSequence = new cvox.KeySequence(this.ctrlShiftEvent, false); - var shiftCtrlSequence = new cvox.KeySequence(this.shiftCtrlEvent, true); - - assertFalse(ctrlShiftSequence.equals(shiftCtrlSequence)); -}); - - -/** - * Tests converting from a string to a KeySequence object. - */ -TEST_F('CvoxKeySequenceUnitTest', 'FromStr', function() { - var ctrlString = cvox.KeySequence.fromStr('Ctrl'); - assertEqualsJSON(ctrlString.keys.ctrlKey, [true]); - assertEqualsJSON(ctrlString.keys.keyCode, [17]); - - var modifiedLetterString = cvox.KeySequence.fromStr('Ctrl+Z'); - assertEqualsJSON(modifiedLetterString.keys.ctrlKey, [true]); - assertEqualsJSON(modifiedLetterString.keys.keyCode, [90]); - - var keyCodeString = cvox.KeySequence.fromStr('#9'); - assertEqualsJSON(keyCodeString.keys.keyCode, [9]); - - var modifiedKeyCodeString = cvox.KeySequence.fromStr('Shift+#9'); - assertEqualsJSON(modifiedKeyCodeString.keys.shiftKey, [true]); - assertEqualsJSON(modifiedKeyCodeString.keys.keyCode, [9]); - - var cvoxLetterString = cvox.KeySequence.fromStr('Cvox+U'); - assertTrue(cvoxLetterString.cvoxModifier); - assertEqualsJSON(cvoxLetterString.keys.keyCode, [85]); - - var cvoxSequenceString = cvox.KeySequence.fromStr('Cvox+C>T'); - assertTrue(cvoxSequenceString.cvoxModifier); - assertEqualsJSON(cvoxSequenceString.keys.keyCode, [67, 84]); - - var cvoxSequenceKeyCodeString = cvox.KeySequence.fromStr('Cvox+L>#186'); - assertTrue(cvoxSequenceKeyCodeString.cvoxModifier); - assertEqualsJSON(cvoxSequenceKeyCodeString.keys.keyCode, [76, 186]); - - var stickyString = cvox.KeySequence.fromStr('Insert>Insert+'); - assertEqualsJSON(stickyString.keys.keyCode, [45, 45]); -}); - - -/** - * Tests converting from a JSON string to a KeySequence object. - */ -TEST_F('CvoxKeySequenceUnitTest', 'Deserialize', function() { - var forwardSequence = cvox.KeySequence.deserialize({'cvoxModifier': true, - 'stickyMode': false, 'prefixKey': false, 'keys': {'ctrlKey': [false], - 'searchKeyHeld': [false], 'altKey': [false], 'altGraphKey': [false], - 'shiftKey': [false], 'metaKey': [false], 'keyCode': [40]}}); - assertTrue(forwardSequence.cvoxModifier); - assertEqualsJSON(forwardSequence.keys.keyCode, [40]); - - var ctrlSequence = cvox.KeySequence.deserialize({'cvoxModifier': false, - 'stickyMode': true, 'prefixKey': false, 'keys': {'ctrlKey': [true], - 'searchKeyHeld': [false], 'altKey': [false], 'altGraphKey': [false], - 'shiftKey': [false], 'metaKey': [false], 'keyCode': [17]}}); - assertEqualsJSON(ctrlSequence.keys.ctrlKey, [true]); - assertEqualsJSON(ctrlSequence.keys.keyCode, [17]); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/key_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/key_util.js deleted file mode 100644 index 5ff64d01d7a..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/key_util.js +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A collection of JavaScript utilities used to simplify working - * with keyboard events. - */ - - -goog.provide('cvox.KeyUtil'); -goog.provide('cvox.SimpleKeyEvent'); - -goog.require('cvox.ChromeVox'); -goog.require('cvox.KeySequence'); - -/** - * @typedef {{ctrlKey: (boolean|undefined), - * altKey: (boolean|undefined), - * shiftKey: (boolean|undefined), - * keyCode: (number|undefined)}} - */ -cvox.SimpleKeyEvent; - -/** - * Create the namespace - * @constructor - */ -cvox.KeyUtil = function() { -}; - -/** - * The time in ms at which the ChromeVox Sticky Mode key was pressed. - * @type {number} - */ -cvox.KeyUtil.modeKeyPressTime = 0; - -/** - * Indicates if sequencing is currently active for building a keyboard shortcut. - * @type {boolean} - */ -cvox.KeyUtil.sequencing = false; - -/** - * The previous KeySequence when sequencing is ON. - * @type {cvox.KeySequence} - */ -cvox.KeyUtil.prevKeySequence = null; - - -/** - * The sticky key sequence. - * @type {cvox.KeySequence} - */ -cvox.KeyUtil.stickyKeySequence = null; - -/** - * Maximum number of key codes the sequence buffer may hold. This is the max - * length of a sequential keyboard shortcut, i.e. the number of key that can be - * pressed one after the other while modifier keys (Cros+Shift) are held down. - * @const - * @type {number} - */ -cvox.KeyUtil.maxSeqLength = 2; - - -/** - * Convert a key event into a Key Sequence representation. - * - * @param {Event|cvox.SimpleKeyEvent} keyEvent The keyEvent to convert. - * @return {cvox.KeySequence} A key sequence representation of the key event. - */ -cvox.KeyUtil.keyEventToKeySequence = function(keyEvent) { - var util = cvox.KeyUtil; - if (util.prevKeySequence && - (util.maxSeqLength == util.prevKeySequence.length())) { - // Reset the sequence buffer if max sequence length is reached. - util.sequencing = false; - util.prevKeySequence = null; - } - // Either we are in the middle of a key sequence (N > H), or the key prefix - // was pressed before (Ctrl+Z), or sticky mode is enabled - var keyIsPrefixed = util.sequencing || keyEvent['keyPrefix'] || - keyEvent['stickyMode']; - - // Create key sequence. - var keySequence = new cvox.KeySequence(keyEvent); - - // Check if the Cvox key should be considered as pressed because the - // modifier key combination is active. - var keyWasCvox = keySequence.cvoxModifier; - - if (keyIsPrefixed || keyWasCvox) { - if (!util.sequencing && util.isSequenceSwitchKeyCode(keySequence)) { - // If this is the beginning of a sequence. - util.sequencing = true; - util.prevKeySequence = keySequence; - return keySequence; - } else if (util.sequencing) { - if (util.prevKeySequence.addKeyEvent(keyEvent)) { - keySequence = util.prevKeySequence; - util.prevKeySequence = null; - util.sequencing = false; - return keySequence; - } else { - throw 'Think sequencing is enabled, yet util.prevKeySequence already' + - 'has two key codes' + util.prevKeySequence; - } - } - } else { - util.sequencing = false; - } - - // Repeated keys pressed. - var currTime = new Date().getTime(); - if (cvox.KeyUtil.isDoubleTapKey(keySequence) && - util.prevKeySequence && - keySequence.equals(util.prevKeySequence)) { - var prevTime = util.modeKeyPressTime; - if (prevTime > 0 && currTime - prevTime < 300) { // Double tap - keySequence = util.prevKeySequence; - keySequence.doubleTap = true; - util.prevKeySequence = null; - util.sequencing = false; - // Resets the search key state tracked for ChromeOS because in OOBE, - // we never get a key up for the key down (keyCode 91). - if (cvox.ChromeVox.isChromeOS && - keyEvent.keyCode == cvox.KeyUtil.getStickyKeyCode()) { - cvox.ChromeVox.searchKeyHeld = false; - } - return keySequence; - } - // The user double tapped the sticky key but didn't do it within the - // required time. It's possible they will try again, so keep track of the - // time the sticky key was pressed and keep track of the corresponding - // key sequence. - } - util.prevKeySequence = keySequence; - util.modeKeyPressTime = currTime; - return keySequence; -}; - -/** - * Returns the string representation of the specified key code. - * - * @param {number} keyCode key code. - * @return {string} A string representation of the key event. - */ -cvox.KeyUtil.keyCodeToString = function(keyCode) { - if (keyCode == 17) { - return 'Ctrl'; - } - if (keyCode == 18) { - return 'Alt'; - } - if (keyCode == 16) { - return 'Shift'; - } - if ((keyCode == 91) || (keyCode == 93)) { - if (cvox.ChromeVox.isChromeOS) { - return 'Search'; - } else if (cvox.ChromeVox.isMac) { - return 'Cmd'; - } else { - return 'Win'; - } - } - // TODO(rshearer): This is a hack to work around the special casing of the - // sticky mode string that used to happen in keyEventToString. We won't need - // it once we move away from strings completely. - if (keyCode == 45) { - return 'Insert'; - } - if (keyCode >= 65 && keyCode <= 90) { - // A - Z - return String.fromCharCode(keyCode); - } else if (keyCode >= 48 && keyCode <= 57) { - // 0 - 9 - return String.fromCharCode(keyCode); - } else { - // Anything else - return '#' + keyCode; - } -}; - -/** - * Returns the keycode of a string representation of the specified modifier. - * - * @param {string} keyString Modifier key. - * @return {number} Key code. - */ -cvox.KeyUtil.modStringToKeyCode = function(keyString) { - switch (keyString) { - case 'Ctrl': - return 17; - case 'Alt': - return 18; - case 'Shift': - return 16; - case 'Cmd': - case 'Win': - return 91; - } - return -1; -}; - -/** - * Returns the key codes of a string respresentation of the ChromeVox modifiers. - * - * @return {Array<number>} Array of key codes. - */ -cvox.KeyUtil.cvoxModKeyCodes = function() { - var modKeyCombo = cvox.ChromeVox.modKeyStr.split(/\+/g); - var modKeyCodes = modKeyCombo.map(function(keyString) { - return cvox.KeyUtil.modStringToKeyCode(keyString); - }); - return modKeyCodes; -}; - -/** - * Checks if the specified key code is a key used for switching into a sequence - * mode. Sequence switch keys are specified in - * cvox.KeyUtil.sequenceSwitchKeyCodes - * - * @param {!cvox.KeySequence} rhKeySeq The key sequence to check. - * @return {boolean} true if it is a sequence switch keycode, false otherwise. - */ -cvox.KeyUtil.isSequenceSwitchKeyCode = function(rhKeySeq) { - for (var i = 0; i < cvox.ChromeVox.sequenceSwitchKeyCodes.length; i++) { - var lhKeySeq = cvox.ChromeVox.sequenceSwitchKeyCodes[i]; - if (lhKeySeq.equals(rhKeySeq)) { - return true; - } - } - return false; -}; - - -/** - * Get readable string description of the specified keycode. - * - * @param {number} keyCode The key code. - * @return {string} Returns a string description. - */ -cvox.KeyUtil.getReadableNameForKeyCode = function(keyCode) { - if (keyCode == 0) { - return 'Power button'; - } else if (keyCode == 17) { - return 'Control'; - } else if (keyCode == 18) { - return 'Alt'; - } else if (keyCode == 16) { - return 'Shift'; - } else if (keyCode == 9) { - return 'Tab'; - } else if ((keyCode == 91) || (keyCode == 93)) { - if (cvox.ChromeVox.isChromeOS) { - return 'Search'; - } else if (cvox.ChromeVox.isMac) { - return 'Cmd'; - } else { - return 'Win'; - } - } else if (keyCode == 8) { - return 'Backspace'; - } else if (keyCode == 32) { - return 'Space'; - } else if (keyCode == 35) { - return'end'; - } else if (keyCode == 36) { - return 'home'; - } else if (keyCode == 37) { - return 'Left arrow'; - } else if (keyCode == 38) { - return 'Up arrow'; - } else if (keyCode == 39) { - return 'Right arrow'; - } else if (keyCode == 40) { - return 'Down arrow'; - } else if (keyCode == 45) { - return 'Insert'; - } else if (keyCode == 13) { - return 'Enter'; - } else if (keyCode == 27) { - return 'Escape'; - } else if (keyCode == 112) { - return cvox.ChromeVox.isChromeOS ? 'Back' : 'F1'; - } else if (keyCode == 113) { - return cvox.ChromeVox.isChromeOS ? 'Forward' : 'F2'; - } else if (keyCode == 114) { - return cvox.ChromeVox.isChromeOS ? 'Refresh' : 'F3'; - } else if (keyCode == 115) { - return cvox.ChromeVox.isChromeOS ? 'Toggle full screen' : 'F4'; - } else if (keyCode == 116) { - return 'F5'; - } else if (keyCode == 117) { - return 'F6'; - } else if (keyCode == 118) { - return 'F7'; - } else if (keyCode == 119) { - return 'F8'; - } else if (keyCode == 120) { - return 'F9'; - } else if (keyCode == 121) { - return 'F10'; - } else if (keyCode == 122) { - return 'F11'; - } else if (keyCode == 123) { - return 'F12'; - } else if (keyCode == 186) { - return 'Semicolon'; - } else if (keyCode == 187) { - return 'Equal sign'; - } else if (keyCode == 188) { - return 'Comma'; - } else if (keyCode == 189) { - return 'Dash'; - } else if (keyCode == 190) { - return 'Period'; - } else if (keyCode == 191) { - return 'Forward slash'; - } else if (keyCode == 192) { - return 'Grave accent'; - } else if (keyCode == 219) { - return 'Open bracket'; - } else if (keyCode == 220) { - return 'Back slash'; - } else if (keyCode == 221) { - return 'Close bracket'; - } else if (keyCode == 222) { - return 'Single quote'; - } else if (keyCode == 115) { - return 'Toggle full screen'; - } else if (keyCode >= 48 && keyCode <= 90) { - return String.fromCharCode(keyCode); - } -}; - -/** - * Get the platform specific sticky key keycode. - * - * @return {number} The platform specific sticky key keycode. - */ -cvox.KeyUtil.getStickyKeyCode = function() { - // TODO (rshearer): This should not be hard-coded here. - var stickyKeyCode = 45; // Insert for Linux and Windows - if (cvox.ChromeVox.isChromeOS || cvox.ChromeVox.isMac) { - stickyKeyCode = 91; // GUI key (Search/Cmd) for ChromeOs and Mac - } - return stickyKeyCode; -}; - - -/** - * Get readable string description for an internal string representation of a - * key or a keyboard shortcut. - * - * @param {string} keyStr The internal string repsentation of a key or - * a keyboard shortcut. - * @return {?string} Readable string representation of the input. - */ -cvox.KeyUtil.getReadableNameForStr = function(keyStr) { - // TODO (clchen): Refactor this function away since it is no longer used. - return null; -}; - - -/** - * Creates a string representation of a KeySequence. - * A KeySequence with a keyCode of 76 ('L') and the control and alt keys down - * would return the string 'Ctrl+Alt+L', for example. A key code that doesn't - * correspond to a letter or number will typically return a string with a - * pound and then its keyCode, like '#39' for Right Arrow. However, - * if the opt_readableKeyCode option is specified, the key code will return a - * readable string description like 'Right Arrow' instead of '#39'. - * - * The modifiers always come in this order: - * - * Ctrl - * Alt - * Shift - * Meta - * - * @param {cvox.KeySequence} keySequence The KeySequence object. - * @param {boolean=} opt_readableKeyCode Whether or not to return a readable - * string description instead of a string with a pound symbol and a keycode. - * Default is false. - * @param {boolean=} opt_modifiers Restrict printout to only modifiers. Defaults - * to false. - * @return {string} Readable string representation of the KeySequence object. - */ -cvox.KeyUtil.keySequenceToString = function( - keySequence, opt_readableKeyCode, opt_modifiers) { - // TODO(rshearer): Move this method and the getReadableNameForKeyCode and the - // method to KeySequence after we refactor isModifierActive (when the modifie - // key becomes customizable and isn't stored as a string). We can't do it - // earlier because isModifierActive uses KeyUtil.getReadableNameForKeyCode, - // and I don't want KeySequence to depend on KeyUtil. - var str = ''; - - var numKeys = keySequence.length(); - - for (var index = 0; index < numKeys; index++) { - if (str != '' && !opt_modifiers) { - str += '>'; - } else if (str != '') { - str += '+'; - } - - // This iterates through the sequence. Either we're on the first key - // pressed or the second - var tempStr = ''; - for (var keyPressed in keySequence.keys) { - // This iterates through the actual key, taking into account any - // modifiers. - if (!keySequence.keys[keyPressed][index]) { - continue; - } - var modifier = ''; - switch (keyPressed) { - case 'ctrlKey': - // TODO(rshearer): This is a hack to work around the special casing - // of the Ctrl key that used to happen in keyEventToString. We won't - // need it once we move away from strings completely. - modifier = 'Ctrl'; - break; - case 'searchKeyHeld': - var searchKey = cvox.KeyUtil.getReadableNameForKeyCode(91); - modifier = searchKey; - break; - case 'altKey': - modifier = 'Alt'; - break; - case 'altGraphKey': - modifier = 'AltGraph'; - break; - case 'shiftKey': - modifier = 'Shift'; - break; - case 'metaKey': - var metaKey = cvox.KeyUtil.getReadableNameForKeyCode(91); - modifier = metaKey; - break; - case 'keyCode': - var keyCode = keySequence.keys[keyPressed][index]; - // We make sure the keyCode isn't for a modifier key. If it is, then - // we've already added that into the string above. - if (!keySequence.isModifierKey(keyCode) && !opt_modifiers) { - if (opt_readableKeyCode) { - tempStr += cvox.KeyUtil.getReadableNameForKeyCode(keyCode); - } else { - tempStr += cvox.KeyUtil.keyCodeToString(keyCode); - } - } - } - if (str.indexOf(modifier) == -1) { - tempStr += modifier + '+'; - } - } - str += tempStr; - - // Strip trailing +. - if (str[str.length - 1] == '+') { - str = str.slice(0, -1); - } - } - - if (keySequence.cvoxModifier || keySequence.prefixKey) { - if (str != '') { - str = 'Cvox+' + str; - } else { - str = 'Cvox'; - } - } else if (keySequence.stickyMode) { - if (str[str.length - 1] == '>') { - str = str.slice(0, -1); - } - str = str + '+' + str; - } - return str; -}; - -/** - * Looks up if the given key sequence is triggered via double tap. - * @param {cvox.KeySequence} key The key. - * @return {boolean} True if key is triggered via double tap. - */ -cvox.KeyUtil.isDoubleTapKey = function(key) { - var isSet = false; - var originalState = key.doubleTap; - key.doubleTap = true; - for (var i = 0, keySeq; keySeq = cvox.KeySequence.doubleTapCache[i]; i++) { - if (keySeq.equals(key)) { - isSet = true; - break; - } - } - key.doubleTap = originalState; - return isSet; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_attr.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_attr.js deleted file mode 100644 index 940121dc852..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_attr.js +++ /dev/null @@ -1,1529 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Semantic attributes of Math symbols and expressions. - * - * This file contains the basic functionality to lookup and assign semantic - * attributes for mathematical expressions. Since there is no such thing as a - * well-defined semantics for all of mathematics we compute a default semantics - * that closely models mathematical expressions found in K-12 mathematics as - * well as in general undergraduate curriculum (i.e., calculus, linear algebra, - * etc). - * - * Currently semantic attributes of symbols consist of the following two parts: - * - * type -- An unmutable property of an expression, regardless of its position in - * the math expression. For example, the letter 'f' will always have the - * type identifier, regardless of its use in context, e.g. as function - * symbol or variable. - * - * role -- A mutable description of the role an expression plays in the context - * of the overall mathematical expression. For instance, the symbol '|' - * is of type punctuation, but depending on context it has the role of a - * neutral fence or of a single vertical bar. - * - * In addition for some symbols we record the font as a further attribute. - * - * When a semantically interpreted expression is transformed into a XML - * representation, types become tag names, while role, font, etc. are added as - * attributes. - * - * This file is part of the content script as we do not want to call out to the - * background page every time we need to look up the semantic of a symbol. - * - * TODO (sorge) Move semantic tree translation into the background page - * alongside MathJax. - * - */ - -goog.provide('cvox.SemanticAttr'); - -goog.require('cvox.SemanticUtil'); - - -/** - * Contains the basic mappings of characters/symbols and functions to semantic - * attributes. - * - * Observe that all characters are given as hex code number in order to ease the - * comparison with those in the JSON files that define speech rules per - * character. - * @constructor - */ -cvox.SemanticAttr = function() { - // Punctuation Characters. - /** - * @type {Array<string>} - */ - this.generalPunctuations = - [ - '!', '"', '#', '%', '&', '\'', '*', ',', ':', ';', '?', '@', '\\', - '¡', '§', '¶', '·', '¿', '‗', '†', '‡', '•', '‣', '․', '‥', '‧', - '‰', '‱', '‸', '※', '‼', '‽', '‾', '⁁', '⁂', '⁃', '⁇', '⁈', '⁉', - '⁋', '⁌', '⁍', '⁎', '⁏', '⁐', '⁑', '⁓', '⁕', '⁖', '⁘', '⁙', '⁚', - '⁛', '⁜', '⁝', '⁞', '︐', '︓', '︔', '︕', '︖', '︰', '﹅', '﹆', - '﹉', '﹊', '﹋', '﹌', '﹐', '﹔', '﹕', '﹖', '﹗', '﹟', '﹠', '﹡', '﹨', - '﹪', '﹫', '!', '"', '#', '%', '&', ''', '*', ',', '/', ':', - ';', '?', '@', '\' - ]; - /** - * @type {string} - * @private - */ - this.invisibleComma_ = cvox.SemanticUtil.numberToUnicode(0x2063); - this.generalPunctuations.push(this.invisibleComma_); - /** - * @type {Array<string>} - */ - this.ellipses = - [ - '…', '⋮', '⋯', '⋰', '⋱', '︙' - ]; - /** - * @type {Array<string>} - */ - this.fullStops = - [ - '.', '﹒', '.' - ]; - /** - * @type {Array<string>} - */ - this.dashes = - [ - '‒', '–', '—', '―', '〜', '︱', '︲', '﹘' - ]; - /** - * @type {Array<string>} - */ - this.primes = - [ - '′', '″', '‴', '‵', '‶', '‷', '⁗' - ]; - - // Fences. - // Fences are treated slightly differently from other symbols as we want to - // record pairs of opening/closing and top/bottom fences. - /** - * Mapping opening to closing fences. - * @type {Object<string>} - */ - this.openClosePairs = - { - // Unicode categories Ps and Pe. - // Observe that left quotation 301D could also be matched to 301F, - // but is currently matched to 301E. - '(': ')', '[': ']', '{': '}', '⁅': '⁆', '〈': '〉', '❨': '❩', - '❪': '❫', '❬': '❭', '❮': '❯', '❰': '❱', '❲': '❳', '❴': '❵', - '⟅': '⟆', '⟦': '⟧', '⟨': '⟩', '⟪': '⟫', '⟬': '⟭', '⟮': '⟯', - '⦃': '⦄', '⦅': '⦆', '⦇': '⦈', '⦉': '⦊', '⦋': '⦌', '⦍': '⦎', - '⦏': '⦐', '⦑': '⦒', '⦓': '⦔', '⦕': '⦖', '⦗': '⦘', '⧘': '⧙', - '⧚': '⧛', '⧼': '⧽', '⸢': '⸣', '⸤': '⸥', '⸦': '⸧', '⸨': '⸩', - '〈': '〉', '《': '》', '「': '」', '『': '』', '【': '】', - '〔': '〕', '〖': '〗', '〘': '〙', '〚': '〛', '〝': '〞', - '﴾': '﴿', '︗': '︘', '﹙': '﹚', '﹛': '﹜', '﹝': '﹞', '(': ')', - '[': ']', '{': '}', '⦅': '⦆', '「': '」', - // Unicode categories Sm and So. - '⌈': '⌉', '⌊': '⌋', '⌌': '⌍', '⌎': '⌏', '⌜': '⌝', '⌞': '⌟', - // Extender fences. - // Parenthesis. - '⎛': '⎞', '⎜': '⎟', '⎝': '⎠', - // Square bracket. - '⎡': '⎤', '⎢': '⎥', '⎣': '⎦', - // Curly bracket. - '⎧': '⎫', '⎨': '⎬', '⎩': '⎭', '⎰': '⎱', '⎸': '⎹' - }; - /** - * Mapping top to bottom fences. - * @type {Object<string>} - */ - this.topBottomPairs = - { - '⎴': '⎵', '⏜': '⏝', '⏞': '⏟', '⏠': '⏡', '︵': '︶', '︷': '︸', - '︹': '︺', '︻': '︼', '︽': '︾', '︿': '﹀', '﹁': '﹂', - '﹃': '﹄', '﹇': '﹈' - }; - /** - * @type {Array<string>} - */ - this.leftFences = cvox.SemanticUtil.objectsToKeys(this.openClosePairs); - /** - * @type {Array<string>} - */ - this.rightFences = cvox.SemanticUtil.objectsToValues(this.openClosePairs); - this.rightFences.push('〟'); - /** - * @type {Array<string>} - */ - this.topFences = cvox.SemanticUtil.objectsToKeys(this.topBottomPairs); - /** - * @type {Array<string>} - */ - this.bottomFences = cvox.SemanticUtil.objectsToValues(this.topBottomPairs); - /** - * @type {Array<string>} - */ - this.neutralFences = - [ - '|', '¦', '‖', '❘', '⦀', '⫴', '¦', '|' - ]; - /** Array of all fences. - * @type {Array<string>} - */ - this.fences = this.neutralFences.concat( - this.leftFences, this.rightFences, this.topFences, this.bottomFences); - - // Identifiers. - // Latin Alphabets. - /** - * @type {Array<string>} - */ - this.capitalLatin = - [ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - ]; - /** - * @type {Array<string>} - */ - this.smallLatin = - [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - // dotless i and j. - 'ı', 'ȷ' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinFullWidth = - [ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinFullWidth = - [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinBold = - [ - '𝐀', '𝐁', '𝐂', '𝐃', '𝐄', '𝐅', '𝐆', '𝐇', '𝐈', '𝐉', '𝐊', '𝐋', '𝐌', - '𝐍', '𝐎', '𝐏', '𝐐', '𝐑', '𝐒', '𝐓', '𝐔', '𝐕', '𝐖', '𝐗', '𝐘', '𝐙' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinBold = - [ - '𝐚', '𝐛', '𝐜', '𝐝', '𝐞', '𝐟', '𝐠', '𝐡', '𝐢', '𝐣', '𝐤', '𝐥', '𝐦', - '𝐧', '𝐨', '𝐩', '𝐪', '𝐫', '𝐬', '𝐭', '𝐮', '𝐯', '𝐰', '𝐱', '𝐲', '𝐳' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinItalic = - [ - '𝐴', '𝐵', '𝐶', '𝐷', '𝐸', '𝐹', '𝐺', '𝐻', '𝐼', '𝐽', '𝐾', '𝐿', '𝑀', - '𝑁', '𝑂', '𝑃', '𝑄', '𝑅', '𝑆', '𝑇', '𝑈', '𝑉', '𝑊', '𝑋', '𝑌', '𝑍' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinItalic = - [ - '𝑎', '𝑏', '𝑐', '𝑑', '𝑒', '𝑓', '𝑔', 'ℎ', '𝑖', '𝑗', '𝑘', '𝑙', '𝑚', - '𝑛', '𝑜', '𝑝', '𝑞', '𝑟', '𝑠', '𝑡', '𝑢', '𝑣', '𝑤', '𝑥', '𝑦', '𝑧', - // dotless i and j. - '𝚤', '𝚥' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinScript = - [ - '𝒜', 'ℬ', '𝒞', '𝒟', 'ℰ', 'ℱ', '𝒢', 'ℋ', 'ℐ', '𝒥', '𝒦', 'ℒ', 'ℳ', - '𝒩', '𝒪', '𝒫', '𝒬', 'ℛ', '𝒮', '𝒯', '𝒰', '𝒱', '𝒲', '𝒳', '𝒴', '𝒵', - // Powerset Cap P. - '℘' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinScript = - [ - '𝒶', '𝒷', '𝒸', '𝒹', 'ℯ', '𝒻', 'ℊ', '𝒽', '𝒾', '𝒿', '𝓀', '𝓁', '𝓂', - '𝓃', 'ℴ', '𝓅', '𝓆', '𝓇', '𝓈', '𝓉', '𝓊', '𝓋', '𝓌', '𝓍', '𝓎', '𝓏', - // script small l - 'ℓ' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinBoldScript = - [ - '𝓐', '𝓑', '𝓒', '𝓓', '𝓔', '𝓕', '𝓖', '𝓗', '𝓘', '𝓙', '𝓚', '𝓛', '𝓜', - '𝓝', '𝓞', '𝓟', '𝓠', '𝓡', '𝓢', '𝓣', '𝓤', '𝓥', '𝓦', '𝓧', '𝓨', '𝓩' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinBoldScript = - [ - '𝓪', '𝓫', '𝓬', '𝓭', '𝓮', '𝓯', '𝓰', '𝓱', '𝓲', '𝓳', '𝓴', '𝓵', '𝓶', - '𝓷', '𝓸', '𝓹', '𝓺', '𝓻', '𝓼', '𝓽', '𝓾', '𝓿', '𝔀', '𝔁', '𝔂', '𝔃' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinFraktur = - [ - '𝔄', '𝔅', 'ℭ', '𝔇', '𝔈', '𝔉', '𝔊', 'ℌ', 'ℑ', '𝔍', '𝔎', '𝔏', '𝔐', - '𝔑', '𝔒', '𝔓', '𝔔', 'ℜ', '𝔖', '𝔗', '𝔘', '𝔙', '𝔚', '𝔛', '𝔜', 'ℨ' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinFraktur = - [ - '𝔞', '𝔟', '𝔠', '𝔡', '𝔢', '𝔣', '𝔤', '𝔥', '𝔦', '𝔧', '𝔨', '𝔩', '𝔪', - '𝔫', '𝔬', '𝔭', '𝔮', '𝔯', '𝔰', '𝔱', '𝔲', '𝔳', '𝔴', '𝔵', '𝔶', '𝔷' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinDoubleStruck = - [ - '𝔸', '𝔹', 'ℂ', '𝔻', '𝔼', '𝔽', '𝔾', 'ℍ', '𝕀', '𝕁', '𝕂', '𝕃', '𝕄', - 'ℕ', '𝕆', 'ℙ', 'ℚ', 'ℝ', '𝕊', '𝕋', '𝕌', '𝕍', '𝕎', '𝕏', '𝕐', 'ℤ' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinDoubleStruck = - [ - '𝕒', '𝕓', '𝕔', '𝕕', '𝕖', '𝕗', '𝕘', '𝕙', '𝕚', '𝕛', '𝕜', '𝕝', '𝕞', - '𝕟', '𝕠', '𝕡', '𝕢', '𝕣', '𝕤', '𝕥', '𝕦', '𝕧', '𝕨', '𝕩', '𝕪', '𝕫' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinBoldFraktur = - [ - '𝕬', '𝕭', '𝕮', '𝕯', '𝕰', '𝕱', '𝕲', '𝕳', '𝕴', '𝕵', '𝕶', '𝕷', '𝕸', - '𝕹', '𝕺', '𝕻', '𝕼', '𝕽', '𝕾', '𝕿', '𝖀', '𝖁', '𝖂', '𝖃', '𝖄', '𝖅' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinBoldFraktur = - [ - '𝖆', '𝖇', '𝖈', '𝖉', '𝖊', '𝖋', '𝖌', '𝖍', '𝖎', '𝖏', '𝖐', '𝖑', '𝖒', - '𝖓', '𝖔', '𝖕', '𝖖', '𝖗', '𝖘', '𝖙', '𝖚', '𝖛', '𝖜', '𝖝', '𝖞', '𝖟' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinSansSerif = - [ - '𝖠', '𝖡', '𝖢', '𝖣', '𝖤', '𝖥', '𝖦', '𝖧', '𝖨', '𝖩', '𝖪', '𝖫', '𝖬', - '𝖭', '𝖮', '𝖯', '𝖰', '𝖱', '𝖲', '𝖳', '𝖴', '𝖵', '𝖶', '𝖷', '𝖸', '𝖹' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinSansSerif = - [ - '𝖺', '𝖻', '𝖼', '𝖽', '𝖾', '𝖿', '𝗀', '𝗁', '𝗂', '𝗃', '𝗄', '𝗅', '𝗆', - '𝗇', '𝗈', '𝗉', '𝗊', '𝗋', '𝗌', '𝗍', '𝗎', '𝗏', '𝗐', '𝗑', '𝗒', '𝗓' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinSansSerifBold = - [ - '𝗔', '𝗕', '𝗖', '𝗗', '𝗘', '𝗙', '𝗚', '𝗛', '𝗜', '𝗝', '𝗞', '𝗟', '𝗠', - '𝗡', '𝗢', '𝗣', '𝗤', '𝗥', '𝗦', '𝗧', '𝗨', '𝗩', '𝗪', '𝗫', '𝗬', '𝗭' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinSansSerifBold = - [ - '𝗮', '𝗯', '𝗰', '𝗱', '𝗲', '𝗳', '𝗴', '𝗵', '𝗶', '𝗷', '𝗸', '𝗹', '𝗺', - '𝗻', '𝗼', '𝗽', '𝗾', '𝗿', '𝘀', '𝘁', '𝘂', '𝘃', '𝘄', '𝘅', '𝘆', '𝘇' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinSansSerifItalic = - [ - '𝘈', '𝘉', '𝘊', '𝘋', '𝘌', '𝘍', '𝘎', '𝘏', '𝘐', '𝘑', '𝘒', '𝘓', '𝘔', - '𝘕', '𝘖', '𝘗', '𝘘', '𝘙', '𝘚', '𝘛', '𝘜', '𝘝', '𝘞', '𝘟', '𝘠', '𝘡' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinSansSerifItalic = - [ - '𝘢', '𝘣', '𝘤', '𝘥', '𝘦', '𝘧', '𝘨', '𝘩', '𝘪', '𝘫', '𝘬', '𝘭', '𝘮', - '𝘯', '𝘰', '𝘱', '𝘲', '𝘳', '𝘴', '𝘵', '𝘶', '𝘷', '𝘸', '𝘹', '𝘺', '𝘻' - ]; - /** - * @type {Array<string>} - */ - this.capitalLatinMonospace = - [ - '𝙰', '𝙱', '𝙲', '𝙳', '𝙴', '𝙵', '𝙶', '𝙷', '𝙸', '𝙹', '𝙺', '𝙻', '𝙼', - '𝙽', '𝙾', '𝙿', '𝚀', '𝚁', '𝚂', '𝚃', '𝚄', '𝚅', '𝚆', '𝚇', '𝚈', '𝚉' - ]; - /** - * @type {Array<string>} - */ - this.smallLatinMonospace = - [ - '𝚊', '𝚋', '𝚌', '𝚍', '𝚎', '𝚏', '𝚐', '𝚑', '𝚒', '𝚓', '𝚔', '𝚕', '𝚖', - '𝚗', '𝚘', '𝚙', '𝚚', '𝚛', '𝚜', '𝚝', '𝚞', '𝚟', '𝚠', '𝚡', '𝚢', '𝚣' - ]; - /** - * @type {Array<string>} - */ - this.latinDoubleStruckItalic = - [ - 'ⅅ', 'ⅆ', 'ⅇ', 'ⅈ', 'ⅉ' - ]; - - // Greek Alphabets - /** - * @type {Array<string>} - */ - this.capitalGreek = - [ - 'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', - 'Ξ', 'Ο', 'Π', 'Ρ', 'Σ', 'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω' - ]; - /** - * @type {Array<string>} - */ - this.smallGreek = - [ - 'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', - 'ξ', 'ο', 'π', 'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω' - ]; - /** - * @type {Array<string>} - */ - this.capitalGreekBold = - [ - '𝚨', '𝚩', '𝚪', '𝚫', '𝚬', '𝚭', '𝚮', '𝚯', '𝚰', '𝚱', '𝚲', '𝚳', '𝚴', - '𝚵', '𝚶', '𝚷', '𝚸', '𝚺', '𝚻', '𝚼', '𝚽', '𝚾', '𝚿', '𝛀' - ]; - /** - * @type {Array<string>} - */ - this.smallGreekBold = - [ - '𝛂', '𝛃', '𝛄', '𝛅', '𝛆', '𝛇', '𝛈', '𝛉', '𝛊', '𝛋', '𝛌', '𝛍', '𝛎', - '𝛏', '𝛐', '𝛑', '𝛒', '𝛓', '𝛔', '𝛕', '𝛖', '𝛗', '𝛘', '𝛙', '𝛚' - ]; - /** - * @type {Array<string>} - */ - this.capitalGreekItalic = - [ - '𝛢', '𝛣', '𝛤', '𝛥', '𝛦', '𝛧', '𝛨', '𝛩', '𝛪', '𝛫', '𝛬', '𝛭', '𝛮', - '𝛯', '𝛰', '𝛱', '𝛲', '𝛴', '𝛵', '𝛶', '𝛷', '𝛸', '𝛹', '𝛺' - ]; - /** - * @type {Array<string>} - */ - this.smallGreekItalic = - [ - '𝛼', '𝛽', '𝛾', '𝛿', '𝜀', '𝜁', '𝜂', '𝜃', '𝜄', '𝜅', '𝜆', '𝜇', '𝜈', - '𝜉', '𝜊', '𝜋', '𝜌', '𝜍', '𝜎', '𝜏', '𝜐', '𝜑', '𝜒', '𝜓', '𝜔' - ]; - /** - * @type {Array<string>} - */ - this.capitalGreekSansSerifBold = - [ - '𝝖', '𝝗', '𝝘', '𝝙', '𝝚', '𝝛', '𝝜', '𝝝', '𝝞', '𝝟', '𝝠', '𝝡', '𝝢', - '𝝣', '𝝤', '𝝥', '𝝦', '𝝨', '𝝩', '𝝪', '𝝫', '𝝬', '𝝭', '𝝮' - ]; - /** - * @type {Array<string>} - */ - this.smallGreekSansSerifBold = - [ - '𝝰', '𝝱', '𝝲', '𝝳', '𝝴', '𝝵', '𝝶', '𝝷', '𝝸', '𝝹', '𝝺', '𝝻', '𝝼', - '𝝽', '𝝾', '𝝿', '𝞀', '𝞁', '𝞂', '𝞃', '𝞄', '𝞅', '𝞆', '𝞇', '𝞈' - ]; - /** - * @type {Array<string>} - */ - this.greekDoubleStruck = - [ - 'ℼ', 'ℽ', 'ℾ', 'ℿ' - ]; - - // Other alphabets. - /** - * @type {Array<string>} - */ - this.hebrewLetters = - [ - 'ℵ', 'ℶ', 'ℷ', 'ℸ' - ]; - - //Operator symbols - /** - * @type {Array<string>} - */ - this.additions = - [ - '+', '±', '∓', '∔', '∧', '∨', '∩', '∪', '⊌', '⊓', '⊔', '⊝', '⊞', - '⊤', '⊥', '⊺', '⊻', '⊼', '⋄', '⋎', '⋏', '⋒', '⋓', '△', '▷', '▽', - '◁', '⩞', '⊕' - ]; - /** - * @type {Array<string>} - */ - /** - * Invisible operator for plus. - * @type {string} - * @private - */ - this.invisiblePlus_ = cvox.SemanticUtil.numberToUnicode(0x2064); - this.additions.push(this.invisiblePlus_); - /** - * @type {Array<string>} - */ - this.multiplications = - [ - '†', '‡', '∐', '∗', '∘', '∙', '≀', '⊚', '⊛', '⊠', '⊡', '⋅', '⋆', '⋇', - '⋉', '⋊', '⋋', '⋌', '○' - ]; - /** - * Invisible operator for multiplication. - * @type {string} - * @private - */ - this.invisibleTimes_ = cvox.SemanticUtil.numberToUnicode(0x2062); - this.multiplications.push(this.invisibleTimes_); - /** - * @type {Array<string>} - */ - this.subtractions = - [ - '-', '⁒', '⁻', '₋', '−', '∖', '∸', '≂', '⊖', '⊟', '➖', '⨩', '⨪', - '⨫', '⨬', '⨺', '⩁', '⩬', '﹣', '-', '‐', '‑' - ]; - /** - * @type {Array<string>} - */ - this.divisions = - [ - '/', '÷', '⁄', '∕', '⊘', '⟌', '⦼', '⨸' - ]; - /** - * Invisible operator for function application. - * @type {string} - * @private - */ - this.functionApplication_ = cvox.SemanticUtil.numberToUnicode(0x2061); - - //Relation symbols - /** - * @type {Array<string>} - */ - this.equalities = - [ - '=', '~', '⁼', '₌', '∼', '∽', '≃', '≅', '≈', '≊', '≋', '≌', '≍', - '≎', '≑', '≒', '≓', '≔', '≕', '≖', '≗', '≘', '≙', '≚', '≛', '≜', - '≝', '≞', '≟', '≡', '≣', '⧤', '⩦', '⩮', '⩯', '⩰', '⩱', '⩲', '⩳', - '⩴', '⩵', '⩶', '⩷', '⩸', '⋕', '⩭', '⩪', '⩫', '⩬', '﹦', '=' - ]; - /** - * @type {Array<string>} - */ - this.inequalities = - [ - '<', '>', '≁', '≂', '≄', '≆', '≇', '≉', '≏', '≐', '≠', '≢', '≤', - '≥', '≦', '≧', '≨', '≩', '≪', '≫', '≬', '≭', '≮', '≯', '≰', '≱', - '≲', '≳', '≴', '≵', '≶', '≷', '≸', '≹', '≺', '≻', '≼', '≽', '≾', - '≿', '⊀', '⊁', '⋖', '⋗', '⋘', '⋙', '⋚', '⋛', '⋜', '⋝', '⋞', '⋟', - '⋠', '⋡', '⋢', '⋣', '⋤', '⋥', '⋦', '⋧', '⋨', '⋩', '⩹', '⩺', '⩻', - '⩼', '⩽', '⩾', '⩿', '⪀', '⪁', '⪂', '⪃', '⪄', '⪅', '⪆', '⪇', '⪈', - '⪉', '⪊', '⪋', '⪌', '⪍', '⪎', '⪏', '⪐', '⪑', '⪒', '⪓', '⪔', '⪕', - '⪖', '⪗', '⪘', '⪙', '⪚', '⪛', '⪜', '⪝', '⪞', '⪟', '⪠', '⪡', '⪢', - '⪣', '⪤', '⪥', '⪦', '⪧', '⪨', '⪩', '⪪', '⪫', '⪬', '⪭', '⪮', '⪯', - '⪰', '⪱', '⪲', '⪳', '⪴', '⪵', '⪶', '⪷', '⪸', '⪹', '⪺', '⪻', '⪼', - '⫷', '⫸', '⫹', '⫺', '⧀', '⧁', '﹤', '﹥', '<', '>' - ]; - /** - * @type {Array<string>} - */ - this.relations = - [ - // TODO (sorge): Add all the other relations. - ]; - /** - * @type {Array<string>} - */ - this.arrows = - [ - '←', '↑', '→', '↓', '↔', '↕', '↖', '↗', '↘', '↙', '↚', '↛', '↜', - '↝', '↞', '↟', '↠', '↡', '↢', '↣', '↤', '↥', '↦', '↧', '↨', '↩', - '↪', '↫', '↬', '↭', '↮', '↯', '↰', '↱', '↲', '↳', '↴', '↵', '↶', - '↷', '↸', '↹', '↺', '↻', '⇄', '⇅', '⇆', '⇇', '⇈', '⇉', '⇊', '⇍', - '⇎', '⇏', '⇐', '⇑', '⇒', '⇓', '⇔', '⇕', '⇖', '⇗', '⇘', '⇙', '⇚', - '⇛', '⇜', '⇝', '⇞', '⇟', '⇠', '⇡', '⇢', '⇣', '⇤', '⇥', '⇦', '⇧', - '⇨', '⇩', '⇪', '⇫', '⇬', '⇭', '⇮', '⇯', '⇰', '⇱', '⇲', '⇳', '⇴', - '⇵', '⇶', '⇷', '⇸', '⇹', '⇺', '⇻', '⇼', '⇽', '⇾', '⇿', '⌁', '⌃', - '⌄', '⌤', '⎋', '➔', '➘', '➙', '➚', '➛', '➜', '➝', '➞', '➟', '➠', - '➡', '➢', '➣', '➤', '➥', '➦', '➧', '➨', '➩', '➪', '➫', '➬', '➭', - '➮', '➯', '➱', '➲', '➳', '➴', '➵', '➶', '➷', '➸', '➹', '➺', '➻', - '➼', '➽', '➾', '⟰', '⟱', '⟲', '⟳', '⟴', '⟵', '⟶', '⟷', '⟸', '⟹', - '⟺', '⟻', '⟼', '⟽', '⟾', '⟿', '⤀', '⤁', '⤂', '⤃', '⤄', '⤅', '⤆', - '⤇', '⤈', '⤉', '⤊', '⤋', '⤌', '⤍', '⤎', '⤏', '⤐', '⤑', '⤒', '⤓', - '⤔', '⤕', '⤖', '⤗', '⤘', '⤙', '⤚', '⤛', '⤜', '⤝', '⤞', '⤟', '⤠', - '⤡', '⤢', '⤣', '⤤', '⤥', '⤦', '⤧', '⤨', '⤩', '⤪', '⤭', '⤮', '⤯', - '⤰', '⤱', '⤲', '⤳', '⤴', '⤵', '⤶', '⤷', '⤸', '⤹', '⤺', '⤻', '⤼', - '⤽', '⤾', '⤿', '⥀', '⥁', '⥂', '⥃', '⥄', '⥅', '⥆', '⥇', '⥈', '⥉', - '⥰', '⥱', '⥲', '⥳', '⥴', '⥵', '⥶', '⥷', '⥸', '⥹', '⥺', '⥻', '⦳', - '⦴', '⦽', '⧪', '⧬', '⧭', '⨗', '⬀', '⬁', '⬂', '⬃', '⬄', '⬅', '⬆', - '⬇', '⬈', '⬉', '⬊', '⬋', '⬌', '⬍', '⬎', '⬏', '⬐', '⬑', '⬰', '⬱', - '⬲', '⬳', '⬴', '⬵', '⬶', '⬷', '⬸', '⬹', '⬺', '⬻', '⬼', '⬽', '⬾', - '⬿', '⭀', '⭁', '⭂', '⭃', '⭄', '⭅', '⭆', '⭇', '⭈', '⭉', '⭊', '⭋', - '⭌', '←', '↑', '→', '↓', - // Harpoons - '↼', '↽', '↾', '↿', '⇀', '⇁', '⇂', '⇃', '⇋', '⇌', '⥊', '⥋', '⥌', - '⥍', '⥎', '⥏', '⥐', '⥑', '⥒', '⥓', '⥔', '⥕', '⥖', '⥗', '⥘', '⥙', - '⥚', '⥛', '⥜', '⥝', '⥞', '⥟', '⥠', '⥡', '⥢', '⥣', '⥤', '⥥', '⥦', - '⥧', '⥨', '⥩', '⥪', '⥫', '⥬', '⥭', '⥮', '⥯', '⥼', '⥽', '⥾', '⥿' - ]; - - //Big operation symbols - /** - * @type {Array<string>} - */ - this.sumOps = - [ - '⅀', // double struck - '∏', '∐', '∑', '⋀', '⋁', '⋂', '⋃', '⨀', '⨁', '⨂', '⨃', '⨄', '⨅', - '⨆', '⨇', '⨈', '⨉', '⨊', '⨋', '⫼', '⫿' - ]; - /** - * @type {Array<string>} - */ - this.intOps = - [ - '∫', '∬', '∭', '∮', '∯', '∰', '∱', '∲', '∳', '⨌', '⨍', '⨎', '⨏', - '⨐', '⨑', '⨒', '⨓', '⨔', '⨕', '⨖', '⨗', '⨘', '⨙', '⨚', '⨛', '⨜' - ]; - /** - * @type {Array<string>} - */ - this.prefixOps = - // TODO (sorge) Insert nabla, differential operators etc. - [ - '∀', '∃' - ]; - /** - * @type {Array<string>} - */ - this.operatorBits = - // TODO (sorge) What to do if single glyphs of big ops occur on their own. - [ - '⌠', '⌡', '⎶', '⎪', '⎮', '⎯', '⎲', '⎳', '⎷' - ]; - - // Accents. - // TODO (sorge) Add accented characters. - - // Numbers. - // Digits. - /** - * @type {Array<string>} - */ - this.digitsNormal = - [ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - ]; - /** - * @type {Array<string>} - */ - this.digitsFullWidth = - [ - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - ]; - /** - * @type {Array<string>} - */ - this.digitsBold = - [ - '𝟎', '𝟏', '𝟐', '𝟑', '𝟒', '𝟓', '𝟔', '𝟕', '𝟖', '𝟗' - ]; - /** - * @type {Array<string>} - */ - this.digitsDoubleStruck = - [ - '𝟘', '𝟙', '𝟚', '𝟛', '𝟜', '𝟝', '𝟞', '𝟟', '𝟠', '𝟡' - ]; - /** - * @type {Array<string>} - */ - this.digitsSansSerif = - [ - '𝟢', '𝟣', '𝟤', '𝟥', '𝟦', '𝟧', '𝟨', '𝟩', '𝟪', '𝟫' - ]; - /** - * @type {Array<string>} - */ - this.digitsSansSerifBold = - [ - '𝟬', '𝟭', '𝟮', '𝟯', '𝟰', '𝟱', '𝟲', '𝟳', '𝟴', '𝟵' - ]; - /** - * @type {Array<string>} - */ - this.digitsMonospace = - [ - '𝟶', '𝟷', '𝟸', '𝟹', '𝟺', '𝟻', '𝟼', '𝟽', '𝟾', '𝟿' - ]; - /** - * @type {Array<string>} - */ - this.digitsSuperscript = - [ - '²', '³', '¹', '⁰', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹' - ]; - /** - * @type {Array<string>} - */ - this.digitsSubscript = - [ - '₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉' - ]; - /** - * @type {Array<string>} - */ - this.fractions = - [ - '¼', '½', '¾', '⅐', '⅑', '⅒', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', - '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '↉' - ]; - /** - * @type {Array<string>} - */ - this.enclosedNumbers = - // Encircled numbers. - [ - '①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩', '⑪', '⑫', '⑬', - '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳', '⓪', '⓫', '⓬', '⓭', '⓮', '⓯', - '⓰', '⓱', '⓲', '⓳', '⓴', '⓵', '⓶', '⓷', '⓸', '⓹', '⓺', '⓻', '⓼', - '⓽', '⓾', '⓿', '❶', '❷', '❸', '❹', '❺', '❻', '❼', '❽', '❾', '❿', - '➀', '➁', '➂', '➃', '➄', '➅', '➆', '➇', '➈', '➉', '➊', '➋', '➌', - '➍', '➎', '➏', '➐', '➑', '➒', '➓', '㉈', '㉉', '㉊', '㉋', '㉌', - '㉍', '㉎', '㉏', '㉑', '㉒', '㉓', '㉔', '㉕', '㉖', '㉗', '㉘', - '㉙', '㉚', '㉛', '㉜', '㉝', '㉞', '㉟', '㊱', '㊲', '㊳', '㊴', - '㊵', '㊶', '㊷', '㊸', '㊹', '㊺', '㊻', '㊼', '㊽', '㊾', '㊿']; - /** - * @type {Array<string>} - */ - this.fencedNumbers = - // Numbers in Parenthesis. - [ - '⑴', '⑵', '⑶', '⑷', '⑸', '⑹', '⑺', '⑻', '⑼', '⑽', '⑾', '⑿', '⒀', - '⒁', '⒂', '⒃', '⒄', '⒅', '⒆', '⒇' - ]; - /** - * @type {Array<string>} - */ - this.punctuatedNumbers = - // Numbers with other punctuation. - ['⒈', '⒉', '⒊', '⒋', '⒌', '⒍', '⒎', '⒏', '⒐', '⒑', '⒒', '⒓', '⒔', - '⒕', '⒖', '⒗', '⒘', '⒙', '⒚', '⒛', // full stop. - '🄀', '🄁', '🄂', '🄃', '🄄', '🄅', '🄆', '🄇', '🄈', '🄉', '🄊' // comma. - ]; - /** Array of all single digits. - * @type {Array<string>} - */ - this.digits = this.digitsNormal.concat( - this.digitsFullWidth, this.digitsBold, this.digitsDoubleStruck, - this.digitsSansSerif, this.digitsSansSerifBold, this.digitsMonospace); - /** Array of all non-digit number symbols. - * @type {Array<string>} - */ - this.numbers = this.fractions.concat( - this.digitsSuperscript, this.digitsSubscript, - this.enclosedNumbers, this.fencedNumbers, this.punctuatedNumbers); - /** Array of all number symbols. - * @type {Array<string>} - */ - this.allNumbers = this.digits.concat(this.numbers); - - // Functions. - /** - * @type {Array<string>} - */ - this.trigonometricFunctions = - [ - 'cos', 'cot', 'csc', 'sec', 'sin', 'tan', 'arccos', 'arccot', - 'arccsc', 'arcsec', 'arcsin', 'arctan' - ]; - /** - * @type {Array<string>} - */ - this.hyperbolicFunctions = - [ - 'cosh', 'coth', 'csch', 'sech', 'sinh', 'tanh', - 'arcosh', 'arcoth', 'arcsch', 'arsech', 'arsinh', 'artanh', - 'arccosh', 'arccoth', 'arccsch', 'arcsech', 'arcsinh', 'arctanh' - ]; - /** - * @type {Array<string>} - */ - this.algebraicFunctions = - [ - 'deg', 'det', 'dim', 'hom', 'ker', 'Tr', 'tr' - ]; - /** - * @type {Array<string>} - */ - this.elementaryFunctions = - [ - 'log', 'ln', 'lg', 'exp', 'expt', 'gcd', 'gcd', 'arg', 'im', 're', 'Pr' - ]; - /** All predefined prefix functions. - * @type {Array<string>} - */ - this.prefixFunctions = this.trigonometricFunctions.concat( - this.hyperbolicFunctions, - this.algebraicFunctions, - this.elementaryFunctions - ); - /** Limit functions are handled separately as they can have lower (and upper) - * limiting expressions. - * @type {Array<string>} - */ - this.limitFunctions = - [ - 'inf', 'lim', 'liminf', 'limsup', 'max', 'min', 'sup', 'injlim', - 'projlim' - ]; - /** - * @type {Array<string>} - */ - this.infixFunctions = - [ - 'mod', 'rem' - ]; - /** - * Default assignments of semantic attributes. - * @type {Array<{set: Array<string>, - * role: cvox.SemanticAttr.Role, - * type: cvox.SemanticAttr.Type, - * font: cvox.SemanticAttr.Font}>} The semantic meaning of the symbol. - * @private - */ - this.symbolSetToSemantic_ = [ - // Punctuation - {set: this.generalPunctuations, - type: cvox.SemanticAttr.Type.PUNCTUATION, - role: cvox.SemanticAttr.Role.UNKNOWN - }, - {set: this.ellipses, - type: cvox.SemanticAttr.Type.PUNCTUATION, - role: cvox.SemanticAttr.Role.ELLIPSIS - }, - {set: this.fullStops, - type: cvox.SemanticAttr.Type.PUNCTUATION, - role: cvox.SemanticAttr.Role.FULLSTOP - }, - {set: this.dashes, - type: cvox.SemanticAttr.Type.PUNCTUATION, - role: cvox.SemanticAttr.Role.DASH - }, - {set: this.primes, - type: cvox.SemanticAttr.Type.PUNCTUATION, - role: cvox.SemanticAttr.Role.PRIME - }, - // Fences - {set: this.leftFences, - type: cvox.SemanticAttr.Type.FENCE, - role: cvox.SemanticAttr.Role.OPEN - }, - {set: this.rightFences, - type: cvox.SemanticAttr.Type.FENCE, - role: cvox.SemanticAttr.Role.CLOSE - }, - {set: this.topFences, - type: cvox.SemanticAttr.Type.FENCE, - role: cvox.SemanticAttr.Role.TOP - }, - {set: this.bottomFences, - type: cvox.SemanticAttr.Type.FENCE, - role: cvox.SemanticAttr.Role.BOTTOM - }, - {set: this.neutralFences, - type: cvox.SemanticAttr.Type.FENCE, - role: cvox.SemanticAttr.Role.NEUTRAL - }, - // Single characters. - // Latin alphabets. - {set: this.smallLatin, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.capitalLatin, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.smallLatinFullWidth, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.capitalLatinFullWidth, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.smallLatinBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.BOLD - }, - {set: this.capitalLatinBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.BOLD - }, - {set: this.smallLatinItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.ITALIC - }, - {set: this.capitalLatinItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.ITALIC - }, - {set: this.smallLatinScript, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SCRIPT - }, - {set: this.capitalLatinScript, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SCRIPT - }, - {set: this.smallLatinBoldScript, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.BOLDSCRIPT - }, - {set: this.capitalLatinBoldScript, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.BOLDSCRIPT - }, - {set: this.smallLatinFraktur, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.FRAKTUR - }, - {set: this.capitalLatinFraktur, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.FRAKTUR - }, - {set: this.smallLatinDoubleStruck, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.DOUBLESTRUCK - }, - {set: this.capitalLatinDoubleStruck, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.DOUBLESTRUCK - }, - {set: this.smallLatinBoldFraktur, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.BOLDFRAKTUR - }, - {set: this.capitalLatinBoldFraktur, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.BOLDFRAKTUR - }, - {set: this.smallLatinSansSerif, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SANSSERIF - }, - {set: this.capitalLatinSansSerif, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SANSSERIF - }, - {set: this.smallLatinSansSerifBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SANSSERIFBOLD - }, - {set: this.capitalLatinSansSerifBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SANSSERIFBOLD - }, - {set: this.smallLatinSansSerifItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SANSSERIFITALIC - }, - {set: this.capitalLatinSansSerifItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.SANSSERIFITALIC - }, - {set: this.smallLatinMonospace, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.MONOSPACE - }, - {set: this.capitalLatinMonospace, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.MONOSPACE - }, - {set: this.latinDoubleStruckItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.LATINLETTER, - font: cvox.SemanticAttr.Font.DOUBLESTRUCKITALIC - }, - // Greek alphabets. - {set: this.smallGreek, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.capitalGreek, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.smallGreekBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.BOLD - }, - {set: this.capitalGreekBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.BOLD - }, - {set: this.smallGreekItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.ITALIC - }, - {set: this.capitalGreekItalic, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.ITALIC - }, - {set: this.smallGreekSansSerifBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.SANSSERIFBOLD - }, - {set: this.capitalGreekSansSerifBold, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.SANSSERIFBOLD - }, - {set: this.greekDoubleStruck, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.GREEKLETTER, - font: cvox.SemanticAttr.Font.DOUBLESTRUCK - }, - // Other alphabets. - {set: this.hebrewLetters, - type: cvox.SemanticAttr.Type.IDENTIFIER, - role: cvox.SemanticAttr.Role.OTHERLETTER, - font: cvox.SemanticAttr.Font.NORMAL - }, - // Numbers. - {set: this.digitsNormal, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.digitsFullWidth, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.NORMAL - }, - {set: this.digitsBold, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.BOLD - }, - {set: this.digitsDoubleStruck, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.DOUBLESTRUCK - }, - {set: this.digitsSansSerif, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.SANSSERIF - }, - {set: this.digitsSansSerifBold, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.SANSSERIFBOLD - }, - {set: this.digitsMonospace, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER, - font: cvox.SemanticAttr.Font.MONOSPACE - }, - {set: this.numbers, - type: cvox.SemanticAttr.Type.NUMBER, - role: cvox.SemanticAttr.Role.INTEGER - }, - // Operators. - {set: this.additions, - type: cvox.SemanticAttr.Type.OPERATOR, - role: cvox.SemanticAttr.Role.ADDITION - }, - {set: this.multiplications, - type: cvox.SemanticAttr.Type.OPERATOR, - role: cvox.SemanticAttr.Role.MULTIPLICATION - }, - {set: this.subtractions, - type: cvox.SemanticAttr.Type.OPERATOR, - role: cvox.SemanticAttr.Role.SUBTRACTION - }, - {set: this.divisions, - type: cvox.SemanticAttr.Type.OPERATOR, - role: cvox.SemanticAttr.Role.DIVISION - }, - {set: this.prefixOps, - type: cvox.SemanticAttr.Type.PREFIXOP, - role: cvox.SemanticAttr.Role.PREFIXFUNC - }, - // Relations - {set: this.equalities, - type: cvox.SemanticAttr.Type.RELATION, - role: cvox.SemanticAttr.Role.EQUALITY - }, - {set: this.inequalities, - type: cvox.SemanticAttr.Type.RELATION, - role: cvox.SemanticAttr.Role.INEQUALITY - }, - {set: this.relations, - type: cvox.SemanticAttr.Type.RELATION, - role: cvox.SemanticAttr.Role.UNKNOWN - }, - {set: this.arrows, - type: cvox.SemanticAttr.Type.RELATION, - role: cvox.SemanticAttr.Role.ARROW - }, - // Large operators - {set: this.sumOps, - type: cvox.SemanticAttr.Type.LARGEOP, - role: cvox.SemanticAttr.Role.SUM}, - {set: this.intOps, - type: cvox.SemanticAttr.Type.LARGEOP, - role: cvox.SemanticAttr.Role.INTEGRAL}, - // Functions - {set: this.limitFunctions, - type: cvox.SemanticAttr.Type.FUNCTION, - role: cvox.SemanticAttr.Role.LIMFUNC}, - {set: this.prefixFunctions, - type: cvox.SemanticAttr.Type.FUNCTION, - role: cvox.SemanticAttr.Role.PREFIXFUNC}, - {set: this.infixFunctions, - type: cvox.SemanticAttr.Type.OPERATOR, - role: cvox.SemanticAttr.Role.MULTIPLICATION - } - // TODO (sorge) Add some of the remaining elements. - ]; -}; -goog.addSingletonGetter(cvox.SemanticAttr); - - -/** - * Union type of semantic attributes. - * @typedef {cvox.SemanticAttr.Type|cvox.SemanticAttr.Role} - */ -cvox.SemanticAttr.Attr; - - -/** - * Mapping for types of elements. - * @enum {string} - */ -cvox.SemanticAttr.Type = { - // Leafs. - // Punctuation like comma, dot, ellipses. - PUNCTUATION: 'punctuation', - // Fence symbol. - FENCE: 'fence', - // One or several digits, plus some punctuation. - NUMBER: 'number', - // Single or multiple letters. - IDENTIFIER: 'identifier', - // Regular text in a math expression. - TEXT: 'text', - // e.g. +, *. - OPERATOR: 'operator', - // Relation symbol, e.g. equals. - RELATION: 'relation', - // e.g. Sum, product, integral. - LARGEOP: 'largeop', - // Some named function. - FUNCTION: 'function', - - // Branches. - // Compound Symbols. - ACCENT: 'accent', - FENCED: 'fenced', - FRACTION: 'fraction', - PUNCTUATED: 'punctuated', - - // Relations. - // Relation sequence of a single relation. - RELSEQ: 'relseq', - // Relation sequence containing at least two different relations. - MULTIREL: 'multirel', - // Operations. - INFIXOP: 'infixop', - PREFIXOP: 'prefixop', - POSTFIXOP: 'postfixop', - - // Function and Bigop Application. - APPL: 'appl', - INTEGRAL: 'integral', - BIGOP: 'bigop', - - SQRT: 'sqrt', - ROOT: 'root', - // These are bigops or functions with limits. - LIMUPPER: 'limupper', - LIMLOWER: 'limlower', - LIMBOTH: 'limboth', - SUBSCRIPT: 'subscript', - SUPERSCRIPT: 'superscript', - UNDERSCORE: 'underscore', - OVERSCORE: 'overscore', - - // Tables and their elements. - TABLE: 'table', - MULTILINE: 'multiline', - MATRIX: 'matrix', - VECTOR: 'vector', - CASES: 'cases', - ROW: 'row', - // Lines are effectively single cell rows. - LINE: 'line', - CELL: 'cell', - - // General. - UNKNOWN: 'unknown', - EMPTY: 'empty' -}; - - -/** - * Mapping for roles of nodes. - * Roles are more specific than types. - * @enum {string} - */ -cvox.SemanticAttr.Role = { - // Punctuation. - ELLIPSIS: 'ellipsis', - FULLSTOP: 'fullstop', - DASH: 'dash', - PRIME: 'prime', // Superscript. - VBAR: 'vbar', // A vertical bar. - OPENFENCE: 'openfence', - CLOSEFENCE: 'closefence', - APPLICATION: 'application', // Function Application. - - // Fences. - OPEN: 'open', - CLOSE: 'close', - TOP: 'top', - BOTTOM: 'bottom', - NEUTRAL: 'neutral', - - // Letters. - LATINLETTER: 'latinletter', - GREEKLETTER: 'greekletter', - OTHERLETTER: 'otherletter', - - // Numbers. - INTEGER: 'integer', - FLOAT: 'float', - OTHERNUMBER: 'othernumber', - - // Accents. - MULTIACCENT: 'multiaccent', - OVERACCENT: 'overaccent', - UNDERACCENT: 'underaccent', - - // Fenced. - LEFTRIGHT: 'leftright', - ABOVEBELOW: 'abovebelow', - - // Punctuated elements. - SEQUENCE: 'sequence', - ENDPUNCT: 'endpunct', - STARTPUNCT: 'startpunct', - - // Operators. - NEGATIVE: 'negative', - NEGATION: 'negation', - MULTIOP: 'multiop', - - // Functions. - LIMFUNC: 'limit function', - INFIXFUNC: 'infix function', - PREFIXFUNC: 'prefix function', - POSTFIXFUNC: 'postfix function', - - // Large operators. - SUM: 'sum', - INTEGRAL: 'integral', - - // Binary operations. - ADDITION: 'addition', - MULTIPLICATION: 'multiplication', - DIVISION: 'division', - SUBTRACTION: 'subtraction', - IMPLICIT: 'implicit', - - // Relations. - EQUALITY: 'equality', - INEQUALITY: 'inequality', - ELEMENT: 'element', - BINREL: 'binrel', - ARROW: 'arrow', - - // Roles of rows, lines, cells. - // They mirror the different types for tables. - MULTILINE: 'multiline', - MATRIX: 'matrix', - VECTOR: 'vector', - CASES: 'cases', - TABLE: 'table', - - // General - UNKNOWN: 'unknown' -}; - - -/** - * Mapping for font annotations. (Taken from MathML2 section 3.2.2, with the - * exception of double-struck-italic.) - * @enum {string} - */ -cvox.SemanticAttr.Font = { - BOLD: 'bold', - BOLDFRAKTUR: 'bold-fraktur', - BOLDITALIC: 'bold-italic', - BOLDSCRIPT: 'bold-script', - DOUBLESTRUCK: 'double-struck', - DOUBLESTRUCKITALIC: 'double-struck-italic', - FRAKTUR: 'fraktur', - ITALIC: 'italic', - MONOSPACE: 'monospace', - NORMAL: 'normal', - SCRIPT: 'script', - SANSSERIF: 'sans-serif', - SANSSERIFITALIC: 'sans-serif-italic', - SANSSERIFBOLD: 'sans-serif-bold', - SANSSERIFBOLDITALIC: 'sans-serif-bold-italic', - UNKNOWN: 'unknown' -}; - - -/** - * Lookup the semantic type of a symbol. - * @param {string} symbol The symbol to which we want to determine the type. - * @return {cvox.SemanticAttr.Type} The semantic type of the symbol. - */ -cvox.SemanticAttr.prototype.lookupType = function(symbol) { - return cvox.SemanticAttr.Type.UNKNOWN; -}; - - -/** - * Lookup the semantic role of a symbol. - * @param {string} symbol The symbol to which we want to determine the role. - * @return {cvox.SemanticAttr.Role} The semantic role of the symbol. - */ -cvox.SemanticAttr.prototype.lookupRole = function(symbol) { - return cvox.SemanticAttr.Role.UNKNOWN; -}; - - -/** - * Lookup the semantic meaning of a symbol in terms of type and role. - * @param {string} symbol The symbol to which we want to determine the meaning. - * @return {{role: cvox.SemanticAttr.Role, - * type: cvox.SemanticAttr.Type}} The semantic meaning of the symbol. - */ -cvox.SemanticAttr.lookupMeaning = function(symbol) { - return cvox.SemanticAttr.getInstance().lookupMeaning_(symbol); -}; - - -/** - * String representation of the invisible times unicode character. - * @return {string} The invisible times character. - */ -cvox.SemanticAttr.invisibleTimes = function() { - return cvox.SemanticAttr.getInstance().invisibleTimes_; -}; - - -/** - * String representation of the invisible comma unicode character. - * @return {string} The invisible comma character. - */ -cvox.SemanticAttr.invisibleComma = function() { - return cvox.SemanticAttr.getInstance().invisibleComma_; -}; - - -/** - * String representation of the function application character. - * @return {string} The invisible function application character. - */ -cvox.SemanticAttr.functionApplication = function() { - return cvox.SemanticAttr.getInstance().functionApplication_; -}; - - -/** - * Decide when two fences match. Currently we match any right to left - * or bottom to top fence and neutral to neutral. - * @param {cvox.SemanticAttr.Role} open Opening fence. - * @param {cvox.SemanticAttr.Role} close Closing fence. - * @return {boolean} True if the fences are matching. - */ -cvox.SemanticAttr.isMatchingFenceRole = function(open, close) { - return (open == cvox.SemanticAttr.Role.OPEN && - close == cvox.SemanticAttr.Role.CLOSE) || - (open == cvox.SemanticAttr.Role.NEUTRAL && - close == cvox.SemanticAttr.Role.NEUTRAL) || - (open == cvox.SemanticAttr.Role.TOP && - close == cvox.SemanticAttr.Role.BOTTOM); -}; - - -/** - * Decide when opening and closing fences match. For neutral fences they have to - * be the same. - * @param {string} open Opening fence. - * @param {string} close Closing fence. - * @return {boolean} True if the fences are matching. - */ -cvox.SemanticAttr.isMatchingFence = function(open, close) { - return cvox.SemanticAttr.getInstance().isMatchingFence_(open, close); -}; - - -/** - * Determines if a fence is an opening fence. - * @param {cvox.SemanticAttr.Role} fence Opening fence. - * @return {boolean} True if the fence is open or neutral. - */ -cvox.SemanticAttr.isOpeningFence = function(fence) { - return (fence == cvox.SemanticAttr.Role.OPEN || - fence == cvox.SemanticAttr.Role.NEUTRAL); -}; - - -/** - * Determines if a fence is a closing fence. - * @param {cvox.SemanticAttr.Role} fence Closing fence. - * @return {boolean} True if the fence is close or neutral. - */ -cvox.SemanticAttr.isClosingFence = function(fence) { - return (fence == cvox.SemanticAttr.Role.CLOSE || - fence == cvox.SemanticAttr.Role.NEUTRAL); -}; - - -// TODO (sorge) Make this depended on position in the alphabets. -/** - * Check if a character is a small 'd' in some font. - * @param {!string} chr The character string. - * @return {boolean} True if the character is indeed a single small d. - */ -cvox.SemanticAttr.isCharacterD = function(chr) { - var Ds = ['d', 'ⅆ', 'd', '𝐝', '𝑑', '𝒹', '𝓭', '𝔡', - '𝕕', '𝖉', '𝖽', '𝗱', '𝘥', '𝚍']; - return Ds.indexOf(chr) != -1; -}; - - -/** - * Decide when opening and closing fences match. For neutral fences they have to - * be the same. - * @param {!string} open Opening fence. - * @param {!string} close Closing fence. - * @return {boolean} True if the fences are matching. - * @private - */ -cvox.SemanticAttr.prototype.isMatchingFence_ = function(open, close) { - if (this.neutralFences.indexOf(open) != -1) { - return open == close; - } - return this.openClosePairs[open] == close || - this.topBottomPairs[open] == close; -}; - - -/** - * Lookup the semantic meaning of a symbol in terms of type and role. - * @param {!string} symbol The symbol to which we want to determine the meaning. - * @return {{role: cvox.SemanticAttr.Role, - * type: cvox.SemanticAttr.Type, - * font: cvox.SemanticAttr.Font}} The semantic meaning of the symbol. - * @private - */ -cvox.SemanticAttr.prototype.lookupMeaning_ = function(symbol) { - for (var i = 0, set; set = this.symbolSetToSemantic_[i]; i++) { - if (set.set.indexOf(symbol) != -1) { - return {role: set.role || cvox.SemanticAttr.Role.UNKNOWN, - type: set.type || cvox.SemanticAttr.Type.UNKNOWN, - font: set.font || cvox.SemanticAttr.Font.UNKNOWN - }; - } - } - return {role: cvox.SemanticAttr.Role.UNKNOWN, - type: cvox.SemanticAttr.Type.UNKNOWN, - font: cvox.SemanticAttr.Font.UNKNOWN - }; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree.js deleted file mode 100644 index 991ae3244ca..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree.js +++ /dev/null @@ -1,1939 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A semantic tree for MathML expressions. - * - * This file contains functionality to compute a semantic interpretation from a - * given MathML expression. This is a very heuristic approach that assumes a - * fairly simple default semantic which is suitable for K-12 and simple UG - * mathematics. - * - */ - -goog.provide('cvox.SemanticTree'); -goog.provide('cvox.SemanticTree.Node'); - -goog.require('cvox.DomUtil'); -goog.require('cvox.SemanticAttr'); -goog.require('cvox.SemanticUtil'); - - -/** - * Create an initial semantic tree. - * @param {!Element} mml The original MathML node. - * @constructor - */ -cvox.SemanticTree = function(mml) { - /** ID counter. - * @type {number} - * @private - */ - this.idCounter_ = 0; - - /** Original MathML tree. - * @type {Node} - */ - this.mathml = mml; - - /** @type {cvox.SemanticTree.Node} */ - this.root = this.parseMathml_(mml); -}; - - -/** - * @param {number} id Node id. - * @constructor - */ -cvox.SemanticTree.Node = function(id) { - /** @type {number} */ - this.id = id; - - /** @type {Array<Element>} */ - this.mathml = []; - - /** @type {cvox.SemanticTree.Node} */ - this.parent = null; - - /** @type {cvox.SemanticAttr.Type} */ - this.type = cvox.SemanticAttr.Type.UNKNOWN; - - /** @type {cvox.SemanticAttr.Role} */ - this.role = cvox.SemanticAttr.Role.UNKNOWN; - - /** @type {cvox.SemanticAttr.Font} */ - this.font = cvox.SemanticAttr.Font.UNKNOWN; - - /** @type {!Array<cvox.SemanticTree.Node>} */ - this.childNodes = []; - - /** @type {string} */ - this.textContent = ''; - - /** Branch nodes can store additional nodes that can be useful. - * E.g. a node of type FENCED can have the opening and closing fences here. - * @type {!Array<cvox.SemanticTree.Node>} - */ - this.contentNodes = []; -}; - - -/** - * Retrieve all subnodes (including the node itself) that satisfy a given - * predicate. - * @param {function(cvox.SemanticTree.Node): boolean} pred The predicate. - * @return {!Array<cvox.SemanticTree.Node>} The nodes in the tree for which the - * predicate holds. - */ -cvox.SemanticTree.Node.prototype.querySelectorAll = function(pred) { - var result = []; - for (var i = 0, child; child = this.childNodes[i]; i++) { - result = result.concat(child.querySelectorAll(pred)); - } - if (pred(this)) { - result.unshift(this); - } - return result; -}; - - - /** - * Returns an XML representation of the tree. - * @param {boolean=} brief If set attributes are omitted. - * @return {Node} The XML representation of the tree. - */ - cvox.SemanticTree.prototype.xml = function(brief) { - var dp = new DOMParser(); - var xml = dp.parseFromString('<stree></stree>', 'text/xml'); - - var xmlRoot = this.root.xml(xml, brief); - xml.childNodes[0].appendChild(xmlRoot); - - return xml.childNodes[0]; - }; - - - /** - * An XML tree representation of the current node. - * @param {Document} xml The XML document. - * @param {boolean=} brief If set attributes are omitted. - * @return {Node} The XML representation of the node. - */ - cvox.SemanticTree.Node.prototype.xml = function(xml, brief) { - /** - * Translates a list of nodes into XML representation. - * @param {string} tag Name of the enclosing tag. - * @param {!Array<!cvox.SemanticTree.Node>} nodes A list of nodes. - * @return {Node} An XML representation of the node list. - */ - var xmlNodeList = function(tag, nodes) { - var xmlNodes = nodes.map(function(x) {return x.xml(xml, brief);}); - var tagNode = xml.createElement(tag); - for (var i = 0, child; child = xmlNodes[i]; i++) { - tagNode.appendChild(child); - } - return tagNode; - }; - var node = xml.createElement(this.type); - if (!brief) { - this.xmlAttributes_(node); - } - node.textContent = this.textContent; - if (this.contentNodes.length > 0) { - node.appendChild(xmlNodeList('content', this.contentNodes)); - } - if (this.childNodes.length > 0) { - node.appendChild(xmlNodeList('children', this.childNodes)); - } - return node; - }; - - -/** - * Serializes the XML representation of the tree. - * @param {boolean=} brief If set attributes are omitted. - * @return {string} Serialized string. - */ -cvox.SemanticTree.prototype.toString = function(brief) { - var xmls = new XMLSerializer(); - return xmls.serializeToString(this.xml(brief)); -}; - - -/** - * Pretty print the XML representation of the tree. - * @param {boolean=} brief If set attributes are omitted. - * @return {string} The formatted string. - */ -cvox.SemanticTree.prototype.formatXml = function(brief) { - var xml = this.toString(brief); - return cvox.SemanticTree.formatXml(xml); -}; - - -/** - * Pretty prints an XML representation. - * @param {string} xml The serialised XML string. - * @return {string} The formatted string. - */ -cvox.SemanticTree.formatXml = function(xml) { - var reg = /(>)(<)(\/*)/g; - xml = xml.replace(reg, '$1\r\n$2$3'); - reg = /(>)(.+)(<c)/g; - xml = xml.replace(reg, '$1\r\n$2\r\n$3'); - var formatted = ''; - var padding = ''; - xml.split('\r\n') - .forEach(function(node) { - if (node.match(/.+<\/\w[^>]*>$/)) { - // Node with content. - formatted += padding + node + '\r\n'; - } else if (node.match(/^<\/\w/)) { - if (padding) { - // Closing tag - padding = padding.slice(2); - formatted += padding + node + '\r\n'; - } - } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { - // Opening tag - formatted += padding + node + '\r\n'; - padding += ' '; - } else { - // Empty tag - formatted += padding + node + '\r\n'; - } - }); - return formatted; -}; - - -/** - * Serializes the XML representation of a node. - * @param {boolean=} brief If attributes are to be omitted. - * @return {string} Serialized string. - */ -cvox.SemanticTree.Node.prototype.toString = function(brief) { - var xmls = new XMLSerializer(); - var dp = new DOMParser(); - var xml = dp.parseFromString('', 'text/xml'); - return xmls.serializeToString(this.xml(xml, brief)); -}; - - -/** - * Adds attributes to the XML representation of the current node. - * @param {Node} node The XML node. - * @private - */ -cvox.SemanticTree.Node.prototype.xmlAttributes_ = function(node) { - node.setAttribute('role', this.role); - if (this.font != cvox.SemanticAttr.Font.UNKNOWN) { - node.setAttribute('font', this.font); - } - node.setAttribute('id', this.id); -}; - - -/** Creates a new node object. - * @return {cvox.SemanticTree.Node} The newly created node. - * @private - */ -cvox.SemanticTree.prototype.createNode_ = function() { - return new cvox.SemanticTree.Node(this.idCounter_++); -}; - - -/** - * Replaces a node in the tree. Updates the root node if necessary. - * @param {!cvox.SemanticTree.Node} oldNode The node to be replaced. - * @param {!cvox.SemanticTree.Node} newNode The new node. - * @private - */ -cvox.SemanticTree.prototype.replaceNode_ = function(oldNode, newNode) { - var parent = oldNode.parent; - if (!parent) { - this.root = newNode; - return; - } - parent.replaceChild_(oldNode, newNode); -}; - - -/** - * Updates the content of the node thereby possibly changing type and role. - * @param {string} content The new content string. - * @private - */ -cvox.SemanticTree.Node.prototype.updateContent_ = function(content) { - // Remove superfluous whitespace! - content = content.trim(); - if (this.textContent == content) { - return; - } - var meaning = cvox.SemanticAttr.lookupMeaning(content); - this.textContent = content; - this.role = meaning.role; - this.type = meaning.type; - this.font = meaning.font; -}; - - -/** - * Adds MathML nodes to the node's store of MathML nodes if necessary only, as - * we can not necessarily assume that the MathML of the content nodes and - * children are all disjoint. - * @param {Array<Node>} mmlNodes List of MathML nodes. - * @private - */ -cvox.SemanticTree.Node.prototype.addMathmlNodes_ = function(mmlNodes) { - for (var i = 0, mml; mml = mmlNodes[i]; i++) { - if (this.mathml.indexOf(mml) == -1) { - this.mathml.push(mml); - } - } -}; - - -/** - * Removes MathML nodes from the node's store of MathML nodes. - * @param {Array<Node>} mmlNodes List of MathML nodes. - * @private - */ -cvox.SemanticTree.Node.prototype.removeMathmlNodes_ = function(mmlNodes) { - var mmlList = this.mathml; - for (var i = 0, mml; mml = mmlNodes[i]; i++) { - var index = mmlList.indexOf(mml); - if (index != -1) { - mmlList.splice(index, 1); - } - } - this.mathml = mmlList; -}; - - -/** - * Appends a child to the node. - * @param {cvox.SemanticTree.Node} child The new child. - * @private - */ -cvox.SemanticTree.Node.prototype.appendChild_ = function(child) { - this.childNodes.push(child); - this.addMathmlNodes_(child.mathml); - child.parent = this; -}; - - -/** - * Replaces a child node of the node. - * @param {!cvox.SemanticTree.Node} oldNode The node to be replaced. - * @param {!cvox.SemanticTree.Node} newNode The new node. - * @private - */ -cvox.SemanticTree.Node.prototype.replaceChild_ = function(oldNode, newNode) { - var index = this.childNodes.indexOf(oldNode); - if (index == -1) { - return; - } - newNode.parent = this; - oldNode.parent = null; - this.childNodes[index] = newNode; - // To not mess up the order of MathML elements more than necessary, we only - // remove and add difference lists. The hope is that we might end up with - // little change. - var removeMathml = oldNode.mathml.filter( - function(x) {return newNode.mathml.indexOf(x) == -1;}); - var addMathml = newNode.mathml.filter( - function(x) {return oldNode.mathml.indexOf(x) == -1;}); - this.removeMathmlNodes_(removeMathml); - this.addMathmlNodes_(addMathml); -}; - - -/** - * Appends a content node to the node. - * @param {cvox.SemanticTree.Node} node The new content node. - * @private - */ -cvox.SemanticTree.Node.prototype.appendContentNode_ = function(node) { - if (node) { - this.contentNodes.push(node); - this.addMathmlNodes_(node.mathml); - node.parent = this; - } -}; - - -/** - * Removes a content node from the node. - * @param {cvox.SemanticTree.Node} node The content node to be removed. - * @private - */ -cvox.SemanticTree.Node.prototype.removeContentNode_ = function(node) { - if (node) { - var index = this.contentNodes.indexOf(node); - if (index != -1) { - this.contentNodes.splice(index, 1); - } - } -}; - - -/** - * This is the main function that creates the semantic tree by recursively - * parsing the initial MathML tree and bottom up assembling the tree. - * @param {!Element} mml The MathML tree. - * @return {!cvox.SemanticTree.Node} The root of the new tree. - * @private - */ -cvox.SemanticTree.prototype.parseMathml_ = function(mml) { - var children = cvox.DomUtil.toArray(mml.children); - switch (cvox.SemanticUtil.tagName(mml)) { - case 'MATH': - case 'MROW': - case 'MPADDED': - case 'MSTYLE': - children = cvox.SemanticUtil.purgeNodes(children); - // Single child node, i.e. the row is meaningless. - if (children.length == 1) { - return this.parseMathml_(/** @type {!Element} */(children[0])); - } - // Case of a 'meaningful' row, even if they are empty. - return this.processRow_(this.parseMathmlChildren_(children)); - break; - case 'MFRAC': - var newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.FRACTION, - [this.parseMathml_(children[0]), this.parseMathml_(children[1])], - []); - newNode.role = cvox.SemanticAttr.Role.DIVISION; - return newNode; - break; - case 'MSUB': - case 'MSUP': - case 'MSUBSUP': - case 'MOVER': - case 'MUNDER': - case 'MUNDEROVER': - return this.makeLimitNode_(cvox.SemanticUtil.tagName(mml), - this.parseMathmlChildren_(children)); - break; - case 'MROOT': - return this.makeBranchNode_( - cvox.SemanticAttr.Type.ROOT, - [this.parseMathml_(children[0]), this.parseMathml_(children[1])], - []); - break; - case 'MSQRT': - children = this.parseMathmlChildren_( - cvox.SemanticUtil.purgeNodes(children)); - return this.makeBranchNode_( - cvox.SemanticAttr.Type.SQRT, [this.processRow_(children)], []); - break; - case 'MTABLE': - newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.TABLE, - this.parseMathmlChildren_(children), []); - if (cvox.SemanticTree.tableIsMultiline_(newNode)) { - this.tableToMultiline_(newNode); - } - return newNode; - break; - case 'MTR': - newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.ROW, - this.parseMathmlChildren_(children), []); - newNode.role = cvox.SemanticAttr.Role.TABLE; - return newNode; - break; - case 'MTD': - children = this.parseMathmlChildren_( - cvox.SemanticUtil.purgeNodes(children)); - newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.CELL, [this.processRow_(children)], []); - newNode.role = cvox.SemanticAttr.Role.TABLE; - return newNode; - break; - case 'MTEXT': - var leaf = this.makeLeafNode_(mml); - leaf.type = cvox.SemanticAttr.Type.TEXT; - return leaf; - break; - // TODO (sorge) Role and font of multi-character and digits unicode strings. - // TODO (sorge) Reclassify wrongly tagged numbers or identifiers. - // TODO (sorge) Put this all in a single clean reclassification method. - case 'MI': - leaf = this.makeLeafNode_(mml); - if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) { - leaf.type = cvox.SemanticAttr.Type.IDENTIFIER; - } - return leaf; - break; - case 'MN': - leaf = this.makeLeafNode_(mml); - if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) { - leaf.type = cvox.SemanticAttr.Type.NUMBER; - } - return leaf; - break; - case 'MO': - leaf = this.makeLeafNode_(mml); - if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) { - leaf.type = cvox.SemanticAttr.Type.OPERATOR; - } - return leaf; - break; - // TODO (sorge) Do something useful with error and phantom symbols. - default: - // Ordinarilly at this point we should not get any other tag. - return this.makeUnprocessed_(mml); - } -}; - - -/** - * Parse a list of MathML nodes into the semantic tree. - * @param {Array<Element>} mmls A list of MathML nodes. - * @return {!Array<cvox.SemanticTree.Node>} The list of resulting semantic - * node. - * @private - */ -cvox.SemanticTree.prototype.parseMathmlChildren_ = function(mmls) { - var result = []; - for (var i = 0, mml; mml = mmls[i]; i++) { - result.push(this.parseMathml_(mml)); - } - return result; -}; - -/** - * Create a node that is to be processed at a later point in time. - * @param {Node} mml The MathML tree. - * @return {!cvox.SemanticTree.Node} The new node. - * @private - */ -cvox.SemanticTree.prototype.makeUnprocessed_ = function(mml) { - var node = this.createNode_(); - node.mathml = [mml]; - return node; -}; - - -/** - * Create an empty leaf node. - * @return {!cvox.SemanticTree.Node} The new node. - * @private - */ -cvox.SemanticTree.prototype.makeEmptyNode_ = function() { - var node = this.createNode_(); - node.type = cvox.SemanticAttr.Type.EMPTY; - return node; -}; - - -/** - * Create a leaf node. - * @param {Node} mml The MathML tree. - * @return {!cvox.SemanticTree.Node} The new node. - * @private - */ -cvox.SemanticTree.prototype.makeLeafNode_ = function(mml) { - var node = this.createNode_(); - node.mathml = [mml]; - node.updateContent_(mml.textContent); - node.font = mml.getAttribute('mathvariant') || node.font; - return node; -}; - - -/** - * Create a branching node. - * @param {!cvox.SemanticAttr.Type} type The type of the node. - * @param {!Array<cvox.SemanticTree.Node>} children The child nodes. - * @param {!Array<cvox.SemanticTree.Node>} contentNodes The content Nodes. - * @param {string=} content Content string if there is any. - * @return {!cvox.SemanticTree.Node} The new node. - * @private - */ -cvox.SemanticTree.prototype.makeBranchNode_ = function( - type, children, contentNodes, content) { - var node = this.createNode_(); - if (content) { - node.updateContent_(content); - } - node.type = type; - node.childNodes = children; - node.contentNodes = contentNodes; - children.concat(contentNodes) - .forEach( - function(x) { - x.parent = node; - node.addMathmlNodes_(x.mathml); - }); - return node; -}; - - -/** - * Create a branching node for an implicit operation, currently assumed to - * be of multiplicative type. - * @param {!Array<!cvox.SemanticTree.Node>} nodes The operands. - * @return {!cvox.SemanticTree.Node} The new branch node. - * @private - */ -cvox.SemanticTree.prototype.makeImplicitNode_ = function(nodes) { - if (nodes.length == 1) { - return nodes[0]; - } - var operator = this.createNode_(); - // For now we assume this is a multiplication using invisible times. - operator.updateContent_(cvox.SemanticAttr.invisibleTimes()); - var newNode = this.makeInfixNode_(nodes, operator); - newNode.role = cvox.SemanticAttr.Role.IMPLICIT; - return newNode; -}; - - -/** - * Create a branching node for an infix operation. - * @param {!Array<cvox.SemanticTree.Node>} children The operands. - * @param {!cvox.SemanticTree.Node} opNode The operator. - * @return {!cvox.SemanticTree.Node} The new branch node. - * @private - */ -cvox.SemanticTree.prototype.makeInfixNode_ = function(children, opNode) { - return this.makeBranchNode_( - cvox.SemanticAttr.Type.INFIXOP, children, [opNode], opNode.textContent); -}; - - -/** - * Creates a node of the specified type by collapsing the given node list into - * one content (thereby concatenating the content of each node into a single - * content string) with the inner node as a child. - * @param {!cvox.SemanticTree.Node} inner The inner node. - * @param {!Array<cvox.SemanticTree.Node>} nodeList List of nodes. - * @param {!cvox.SemanticAttr.Type} type The new type of the node. - * @return {!cvox.SemanticTree.Node} The new branch node. - * @private - */ -cvox.SemanticTree.prototype.makeConcatNode_ = function(inner, nodeList, type) { - if (nodeList.length == 0) { - return inner; - } - var content = nodeList.map(function(x) {return x.textContent;}).join(' '); - var newNode = this.makeBranchNode_(type, [inner], nodeList, content); - if (nodeList.length > 0) { - newNode.role = cvox.SemanticAttr.Role.MULTIOP; - } - return newNode; -}; - - -/** - * Wraps a node into prefix operators. - * Example: + - a becomes (+ (- (a))) - * Input: a [+, -] -> Output: content: '+ -', child: a - * @param {!cvox.SemanticTree.Node} node The inner node. - * @param {!Array<cvox.SemanticTree.Node>} prefixes Prefix operators - * from the outermost to the innermost. - * @return {!cvox.SemanticTree.Node} The new branch node. - * @private - */ -cvox.SemanticTree.prototype.makePrefixNode_ = function(node, prefixes) { - var negatives = cvox.SemanticTree.partitionNodes_( - prefixes, cvox.SemanticTree.attrPred_('role', 'SUBTRACTION')); - var newNode = this.makeConcatNode_( - node, negatives.comp.pop(), cvox.SemanticAttr.Type.PREFIXOP); - - while (negatives.rel.length > 0) { - newNode = this.makeConcatNode_( - newNode, [negatives.rel.pop()], cvox.SemanticAttr.Type.PREFIXOP); - newNode.role = cvox.SemanticAttr.Role.NEGATIVE; - newNode = this.makeConcatNode_( - newNode, negatives.comp.pop(), cvox.SemanticAttr.Type.PREFIXOP); - } - return newNode; -}; - - -/** - * Wraps a node into postfix operators. - * Example: a - + becomes (((a) -) +) - * Input: a [-, +] -> Output: content: '- +', child: a - * @param {!cvox.SemanticTree.Node} node The inner node. - * @param {!Array<cvox.SemanticTree.Node>} postfixes Postfix operators from - * innermost to outermost. - * @return {!cvox.SemanticTree.Node} The new branch node. - * @private - */ -cvox.SemanticTree.prototype.makePostfixNode_ = function(node, postfixes) { - return this.makeConcatNode_( - node, postfixes, cvox.SemanticAttr.Type.POSTFIXOP); -}; - - -// TODO (sorge) Separate out interspersed text before the relations in row -// heuristic otherwise we get them as implicit operations! -// Currently we handle that later in the rules, which is rather messy. -/** - * Processes a list of nodes, combining expressions by delimiters, tables, - * punctuation sequences, function/big operator/integral applications to - * generate a syntax tree with relation and operator precedence. - * - * This is the main heuristic to rewrite a flat row of terms into a meaningful - * term tree. - * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes. - * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. - * @private - */ -cvox.SemanticTree.prototype.processRow_ = function(nodes) { - if (nodes.length == 0) { - return this.makeEmptyNode_(); - } - nodes = this.getFencesInRow_(nodes); - nodes = this.processTablesInRow_(nodes); - nodes = this.getPunctuationInRow_(nodes); - nodes = this.getFunctionsInRow_(nodes); - return this.processRelationsInRow_(nodes); -}; - - -/** - * Constructs a syntax tree with relation and operator precedence from a list - * of nodes. - * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes. - * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. - * @private - */ -cvox.SemanticTree.prototype.processRelationsInRow_ = function(nodes) { - var partition = cvox.SemanticTree.partitionNodes_( - nodes, cvox.SemanticTree.attrPred_('type', 'RELATION')); - var firstRel = partition.rel[0]; - - if (!firstRel) { - return this.processOperationsInRow_(nodes); - } - if (nodes.length == 1) { - return nodes[0]; - } - var children = partition.comp.map( - goog.bind(this.processOperationsInRow_, this)); - if (partition.rel.every( - function(x) {return x.textContent == firstRel.textContent;})) { - return this.makeBranchNode_( - cvox.SemanticAttr.Type.RELSEQ, children, partition.rel, - firstRel.textContent); - } - return this.makeBranchNode_( - cvox.SemanticAttr.Type.MULTIREL, children, partition.rel); -}; - - -/** - * Constructs a syntax tree with operator precedence from a list nodes. - * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes. - * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. - * @private - */ -cvox.SemanticTree.prototype.processOperationsInRow_ = function(nodes) { - if (nodes.length == 0) { - return this.makeEmptyNode_(); - } - if (nodes.length == 1) { - return nodes[0]; - } - - var prefix = []; - while (nodes.length > 0 && - nodes[0].type == cvox.SemanticAttr.Type.OPERATOR) { - prefix.push(nodes.shift()); - } - // Pathological case: only operators in row. - if (nodes.length == 0) { - return this.makePrefixNode_(prefix.pop(), prefix); - } - if (nodes.length == 1) { - return this.makePrefixNode_(nodes[0], prefix); - } - - var split = cvox.SemanticTree.sliceNodes_( - nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR')); - // At this point, we know that split.head is not empty! - var node = this.makePrefixNode_( - this.makeImplicitNode_( - /** @type {!Array<!cvox.SemanticTree.Node>} */ (split.head)), - prefix); - if (!split.div) { - return node; - } - return this.makeOperationsTree_(split.tail, node, split.div); -}; - - -/** - * Recursively constructs syntax tree with operator precedence from a list nodes - * given a initial root node. - * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes. - * @param {!cvox.SemanticTree.Node} root Initial tree. - * @param {!cvox.SemanticTree.Node} lastop Last operator that has not been - * processed yet. - * @param {Array<cvox.SemanticTree.Node>=} prefixes Operator nodes that will - * become prefix operation (or postfix in case they come after last operand). - * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. - * @private - */ -cvox.SemanticTree.prototype.makeOperationsTree_ = function( - nodes, root, lastop, prefixes) { - prefixes = prefixes || []; - - if (nodes.length == 0) { - // Left over prefixes become postfixes. - prefixes.unshift(lastop); - if (root.type == cvox.SemanticAttr.Type.INFIXOP) { - // We assume prefixes bind stronger than postfixes. - var node = this.makePostfixNode_( - // Here we know that the childNodes are not empty! - /** @type {!cvox.SemanticTree.Node} */ (root.childNodes.pop()), - prefixes); - root.appendChild_(node); - return root; - } - return this.makePostfixNode_(root, prefixes); - } - - var split = cvox.SemanticTree.sliceNodes_( - nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR')); - - if (split.head.length == 0) { - prefixes.push(split.div); - return this.makeOperationsTree_(split.tail, root, lastop, prefixes); - } - - var node = this.makePrefixNode_( - this.makeImplicitNode_(split.head), prefixes); - var newNode = this.appendOperand_(root, lastop, node); - if (!split.div) { - return newNode; - } - - return this.makeOperationsTree_(split.tail, newNode, split.div, []); -}; - -// TODO (sorge) The following four functions could be combined into -// a single one. Currently it is clearer the way it is, though. -/** - * Appends an operand at the right place in an operator tree. - * @param {!cvox.SemanticTree.Node} root The operator tree. - * @param {!cvox.SemanticTree.Node} op The operator node. - * @param {!cvox.SemanticTree.Node} node The node to be added. - * @return {!cvox.SemanticTree.Node} The modified root node. - * @private - */ -cvox.SemanticTree.prototype.appendOperand_ = function(root, op, node) { - // In general our operator tree will have the form that additions and - // subtractions are stacked, while multiplications are subordinate. - if (root.type != cvox.SemanticAttr.Type.INFIXOP) { - return this.makeInfixNode_([root, node], op); - } - if (this.appendExistingOperator_(root, op, node)) { - return root; - } - return op.role == cvox.SemanticAttr.Role.MULTIPLICATION ? - this.appendMultiplicativeOp_(root, op, node) : - this.appendAdditiveOp_(root, op, node); -}; - - -/** - * Appends a multiplicative operator and operand. - * @param {!cvox.SemanticTree.Node} root The root node. - * @param {!cvox.SemanticTree.Node} op The operator node. - * @param {!cvox.SemanticTree.Node} node The operand node to be added. - * @return {!cvox.SemanticTree.Node} The modified root node. - * @private - */ -cvox.SemanticTree.prototype.appendMultiplicativeOp_ = function(root, op, node) { - var lastRoot = root; - var lastChild = root.childNodes[root.childNodes.length - 1]; - while (lastChild && lastChild.type == cvox.SemanticAttr.Type.INFIXOP) { - lastRoot = lastChild; - lastChild = lastRoot.childNodes[root.childNodes.length - 1]; - } - var newNode = this.makeInfixNode_([lastRoot.childNodes.pop(), node], op); - lastRoot.appendChild_(newNode); - return root; -}; - - -/** - * Appends an additive/substractive operator and operand. - * @param {!cvox.SemanticTree.Node} root The old root node. - * @param {!cvox.SemanticTree.Node} op The operator node. - * @param {!cvox.SemanticTree.Node} node The operand node to be added. - * @return {!cvox.SemanticTree.Node} The new root node. - * @private - */ -cvox.SemanticTree.prototype.appendAdditiveOp_ = function(root, op, node) { - return this.makeInfixNode_([root, node], op); -}; - - -/** - * Adds an operand to an operator node if it is the continuation of an existing - * operation. - * @param {!cvox.SemanticTree.Node} root The root node. - * @param {!cvox.SemanticTree.Node} op The operator node. - * @param {!cvox.SemanticTree.Node} node The operand node to be added. - * @return {boolean} True if operator was successfully appended. - * @private - */ -cvox.SemanticTree.prototype.appendExistingOperator_ = function(root, op, node) { - if (!root || root.type != cvox.SemanticAttr.Type.INFIXOP) { - return false; - } - if (root.textContent == op.textContent) { - root.appendContentNode_(op); - root.appendChild_(node); - return true; - } - this.appendExistingOperator_( - // Again, if this is an INFIXOP node, we know it has a child! - /** @type {!cvox.SemanticTree.Node} */ - (root.childNodes[root.childNodes.length - 1]), - op, node); -}; - - -// TODO (sorge) The following procedure needs a rational reconstruction. It -// contains a number of similar cases which should be combined. -/** - * Combines delimited expressions in a list of nodes. - * - * The basic idea of the heuristic is as follows: - * 1. Opening and closing delimiters are matched regardless of the actual shape - * of the fence. These are turned into fenced nodes. - * 2. Neutral fences are matched only with neutral fences of the same shape. - * 3. For a collection of unmatched neutral fences we try to get a maximum - * number of matching fences. E.g. || a|b || would be turned into a fenced - * node with fences || and content a|b. - * 4. Any remaining unmatched delimiters are turned into punctuation nodes. - * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes. - * @return {!Array<!cvox.SemanticTree.Node>} The new list of nodes. - * @private - */ -cvox.SemanticTree.prototype.getFencesInRow_ = function(nodes) { - var partition = cvox.SemanticTree.partitionNodes_( - nodes, cvox.SemanticTree.attrPred_('type', 'FENCE')); - var felem = partition.comp.shift(); - return this.processFences_(partition.rel, partition.comp, [], [felem]); -}; - - -/** - * Recursively processes a list of nodes and combines all the fenced expressions - * into single nodes. It also processes singular fences, building expressions - * that are only fenced left or right. - * @param {!Array<cvox.SemanticTree.Node>} fences FIFO queue of fence nodes. - * @param {!Array<Array<cvox.SemanticTree.Node>>} content FIFO queue content - * between fences. - * @param {!Array<cvox.SemanticTree.Node>} openStack LIFO stack of open fences. - * @param {!Array<!Array<cvox.SemanticTree.Node>>} contentStack LIFO stack of - * content between fences yet to be processed. - * @return {!Array<cvox.SemanticTree.Node>} A list of nodes with all fenced - * expressions processed. - * @private - */ -cvox.SemanticTree.prototype.processFences_ = function( - fences, content, openStack, contentStack) { - // Base case 1: Everything is used up. - if (fences.length == 0 && openStack.length == 0) { - return contentStack[0]; - } - var openPred = cvox.SemanticTree.attrPred_('role', 'OPEN'); - // Base case 2: Only open and neutral fences are left on the stack. - if (fences.length == 0) { - // Basic idea: - // - make punctuation nodes from open fences - // - combine as many neutral fences as possible, if the are not separated by - // open fences. - // The idea is to allow for things like case statements etc. and not bury - // them inside a neutral fenced expression. - // - // 0. We process the list from left to right. Hence the first element on the - // content stack are actually left most elements in the expression. - // 1. Slice at open fence. - // 2. On tail optimize for neutral fences. - // 3. Repeat until fence stack is exhausted. - // Push rightmost elements onto the result. - var result = contentStack.shift(); - while (openStack.length > 0) { - if (openPred(openStack[0])) { - var firstOpen = openStack.shift(); - cvox.SemanticTree.fenceToPunct_(firstOpen); - result.push(firstOpen); - } else { - var split = cvox.SemanticTree.sliceNodes_(openStack, openPred); - var cutLength = split.head.length - 1; - var innerNodes = this.processNeutralFences_( - split.head, contentStack.slice(0, cutLength)); - contentStack = contentStack.slice(cutLength); - //var rightContent = contentStack.shift(); - result.push.apply(result, innerNodes); - //result.push.apply(result, rightContent); - if (split.div) { - split.tail.unshift(split.div); - } - openStack = split.tail; - } - result.push.apply(result, contentStack.shift()); - } - return result; - } - var lastOpen = openStack[openStack.length - 1]; - var firstRole = fences[0].role; - // General opening case. - // Either we have an open fence. - if (firstRole == cvox.SemanticAttr.Role.OPEN || - // Or we have a neutral fence that does not have a counter part. - (firstRole == cvox.SemanticAttr.Role.NEUTRAL && - (!lastOpen || - fences[0].textContent != lastOpen.textContent))) { - openStack.push(fences.shift()); - contentStack.push(content.shift()); - return this.processFences_(fences, content, openStack, contentStack); - } - // General closing case. - if (lastOpen && ( - // Closing fence for some opening fence. - (firstRole == cvox.SemanticAttr.Role.CLOSE && - lastOpen.role == cvox.SemanticAttr.Role.OPEN) || - // Netural fence with exact counter part. - (firstRole == cvox.SemanticAttr.Role.NEUTRAL && - fences[0].textContent == lastOpen.textContent))) { - var fenced = this.makeHorizontalFencedNode_( - openStack.pop(), fences.shift(), contentStack.pop()); - contentStack.push(contentStack.pop().concat([fenced], content.shift())); - return this.processFences_(fences, content, openStack, contentStack); - } - // Closing with a neutral fence on the stack. - if (lastOpen && firstRole == cvox.SemanticAttr.Role.CLOSE && - lastOpen.role == cvox.SemanticAttr.Role.NEUTRAL && - openStack.some(openPred)) { - // Steps of the algorithm: - // 1. Split list at right most opening bracket. - // 2. Cut content list at corresponding length. - // 3. Optimise the neutral fences. - // 4. Make fenced node. - // - // Careful, this reverses openStack! - var split = cvox.SemanticTree.sliceNodes_(openStack, openPred, true); - // We know that - // (a) div & tail exist, - // (b) all are combined in this step into a single fenced node, - // (c) head is the new openStack, - // (d) the new contentStack is remainder of contentStack + new fenced node + - // shift of content. - var rightContent = contentStack.pop(); - var cutLength = contentStack.length - split.tail.length + 1; - var innerNodes = this.processNeutralFences_( - split.tail, contentStack.slice(cutLength)); - contentStack = contentStack.slice(0, cutLength); - var fenced = this.makeHorizontalFencedNode_( - split.div, fences.shift(), - contentStack.pop().concat(innerNodes, rightContent)); - contentStack.push(contentStack.pop().concat([fenced], content.shift())); - return this.processFences_(fences, content, split.head, contentStack); - } - // Final Case: A singular closing fence. - // We turn the fence into a punctuation. - var fenced = fences.shift(); - cvox.SemanticTree.fenceToPunct_(fenced); - contentStack.push(contentStack.pop().concat([fenced], content.shift())); - return this.processFences_(fences, content, openStack, contentStack); -}; - - -// TODO (sorge) The following could be done with linear programming. -/** - * Trys to combine neutral fences as much as possible. - * @param {!Array<!cvox.SemanticTree.Node>} fences A list of neutral fences. - * @param {!Array<!Array<cvox.SemanticTree.Node>>} content Intermediate - * content. Observe that |content| = |fences| - 1 - * @return {!Array<cvox.SemanticTree.Node>} List of node with fully fenced - * nodes. - * @private - */ -cvox.SemanticTree.prototype.processNeutralFences_ = function(fences, content) { - if (fences.length == 0) { - return fences; - } - if (fences.length == 1) { - cvox.SemanticTree.fenceToPunct_(fences[0]); - return fences; - } - var firstFence = fences.shift(); - var split = cvox.SemanticTree.sliceNodes_( - fences, function(x) {return x.textContent == firstFence.textContent;}); - if (!split.div) { - cvox.SemanticTree.fenceToPunct_(firstFence); - var restContent = content.shift(); - restContent.unshift(firstFence); - return restContent.concat(this.processNeutralFences_(fences, content)); - } - var newContent = this.combineFencedContent_( - firstFence, split.div, split.head, content); - if (split.tail.length > 0) { - var leftContent = newContent.shift(); - var result = this.processNeutralFences_(split.tail, newContent); - return leftContent.concat(result); - } - return newContent[0]; -}; - - -/** - * Combines nodes framed by two matching fences using the given content. - * Example: leftFence: [, rightFence: ], midFences: |, | - * content: c1, c2, c3, c4, ... cn - * return: [c1 | c2 | c3 ], c4, ... cn - * @param {!cvox.SemanticTree.Node} leftFence The left fence. - * @param {!cvox.SemanticTree.Node} rightFence The right fence. - * @param {!Array<cvox.SemanticTree.Node>} midFences A list of intermediate - * fences. - * @param {!Array<!Array<cvox.SemanticTree.Node>>} content Intermediate - * content. Observe that |content| = |fences| - 1 + k where k >= 0 is the - * remainder. - * @return {!Array<!Array<cvox.SemanticTree.Node>>} List of content nodes - * where the first is the fully fenced node wrt. the given left and right - * fence. - * @private - */ -cvox.SemanticTree.prototype.combineFencedContent_ = function( - leftFence, rightFence, midFences, content) { - - if (midFences.length == 0) { - var fenced = this.makeHorizontalFencedNode_( - leftFence, rightFence, content.shift()); - content.unshift(fenced); - return content; - } - - var leftContent = content.shift(); - var cutLength = midFences.length - 1; - var midContent = content.slice(0, cutLength); - content = content.slice(cutLength); - var rightContent = content.shift(); - var innerNodes = this.processNeutralFences_(midFences, midContent); - leftContent.push.apply(leftContent, innerNodes); - leftContent.push.apply(leftContent, rightContent); - var fenced = this.makeHorizontalFencedNode_( - leftFence, rightFence, leftContent); - if (content.length > 0) { - content[0].unshift(fenced); - } else { - content = [[fenced]]; - } - return content; - }; - - -/** - * Rewrite fences into punctuation. This is done with any "leftover" fence. - * @param {cvox.SemanticTree.Node} fence Fence. - * @private - */ -cvox.SemanticTree.fenceToPunct_ = function(fence) { - fence.type = cvox.SemanticAttr.Type.PUNCTUATION; - switch (fence.role) { - case cvox.SemanticAttr.Role.NEUTRAL: - fence.role = cvox.SemanticAttr.Role.VBAR; - break; - case cvox.SemanticAttr.Role.OPEN: - fence.role = cvox.SemanticAttr.Role.OPENFENCE; - break; - case cvox.SemanticAttr.Role.CLOSE: - fence.role = cvox.SemanticAttr.Role.CLOSEFENCE; - break; - } -}; - - -/** - * Create a fenced node. - * @param {cvox.SemanticTree.Node} ofence Opening fence. - * @param {cvox.SemanticTree.Node} cfence Closing fence. - * @param {!Array<cvox.SemanticTree.Node>} content The content - * between the fences. - * @return {!cvox.SemanticTree.Node} The new node. - * @private - */ -cvox.SemanticTree.prototype.makeHorizontalFencedNode_ = function( - ofence, cfence, content) { - var childNode = this.processRow_(content); - var newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.FENCED, [childNode], [ofence, cfence]); - if (ofence.role == cvox.SemanticAttr.Role.OPEN) { - newNode.role = cvox.SemanticAttr.Role.LEFTRIGHT; - } else { - newNode.role = ofence.role; - } - return newNode; -}; - - -/** - * Combines sequences of punctuated expressions in a list of nodes. - * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes. - * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes. - * @private - */ -cvox.SemanticTree.prototype.getPunctuationInRow_ = function(nodes) { - // For now we just make a punctuation node with a particular role. This is - // similar to an mrow. The only exception are ellipses, which we assume to be - // in lieu of identifiers. - // In addition we keep the single punctuation nodes as content. - var partition = cvox.SemanticTree.partitionNodes_( - nodes, function(x) { - return cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(x) && - !cvox.SemanticTree.attrPred_('role', 'ELLIPSIS')(x);}); - if (partition.rel.length == 0) { - return nodes; - } - var newNodes = []; - var firstComp = partition.comp.shift(); - if (firstComp.length > 0) { - newNodes.push(this.processRow_(firstComp)); - } - var relCounter = 0; - while (partition.comp.length > 0) { - newNodes.push(partition.rel[relCounter++]); - firstComp = partition.comp.shift(); - if (firstComp.length > 0) { - newNodes.push(this.processRow_(firstComp)); - } - } - return [this.makePunctuatedNode_(newNodes, partition.rel)]; -}; - - -/** - * Create a punctuated node. - * @param {!Array<!cvox.SemanticTree.Node>} nodes List of all nodes separated - * by punctuations. - * @param {!Array<!cvox.SemanticTree.Node>} punctuations List of all separating - * punctations. Observe that punctations is a subset of nodes. - * @return {!cvox.SemanticTree.Node} - * @private - */ -cvox.SemanticTree.prototype.makePunctuatedNode_ = function( - nodes, punctuations) { - var newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.PUNCTUATED, nodes, punctuations); - - if (punctuations.length == 1 && - nodes[0].type == cvox.SemanticAttr.Type.PUNCTUATION) { - newNode.role = cvox.SemanticAttr.Role.STARTPUNCT; - } else if (punctuations.length == 1 && - nodes[nodes.length - 1].type == cvox.SemanticAttr.Type.PUNCTUATION) { - newNode.role = cvox.SemanticAttr.Role.ENDPUNCT; - } else { - newNode.role = cvox.SemanticAttr.Role.SEQUENCE; - } - return newNode; -}; - - -/** - * Creates a limit node from a sub/superscript or over/under node if the central - * element is a big operator. Otherwise it creates the standard elements. - * @param {string} mmlTag The tag name of the original node. - * @param {!Array<!cvox.SemanticTree.Node>} children The children of the - * original node. - * @return {!cvox.SemanticTree.Node} The newly created limit node. - * @private - */ -cvox.SemanticTree.prototype.makeLimitNode_ = function(mmlTag, children) { - var center = children[0]; - var isFunction = cvox.SemanticTree.attrPred_('type', 'FUNCTION')(center); - // TODO (sorge) Put this into a single function. - var isLimit = cvox.SemanticTree.attrPred_('type', 'LARGEOP')(center) || - cvox.SemanticTree.attrPred_('type', 'LIMBOTH')(center) || - cvox.SemanticTree.attrPred_('type', 'LIMLOWER')(center) || - cvox.SemanticTree.attrPred_('type', 'LIMUPPER')(center) || - (isFunction && cvox.SemanticTree.attrPred_('role', 'LIMFUNC')(center)); - var type = cvox.SemanticAttr.Type.UNKNOWN; - // TODO (sorge) Make use of the difference in information on sub vs under etc. - if (isLimit) { - switch (mmlTag) { - case 'MSUB': - case 'MUNDER': - type = cvox.SemanticAttr.Type.LIMLOWER; - break; - case 'MSUP': - case 'MOVER': - type = cvox.SemanticAttr.Type.LIMUPPER; - break; - case 'MSUBSUP': - case 'MUNDEROVER': - type = cvox.SemanticAttr.Type.LIMBOTH; - break; - } - } else { - switch (mmlTag) { - case 'MSUB': - type = cvox.SemanticAttr.Type.SUBSCRIPT; - break; - case 'MSUP': - type = cvox.SemanticAttr.Type.SUPERSCRIPT; - break; - case 'MSUBSUP': - var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.SUBSCRIPT, - [center, children[1]], []); - innerNode.role = center.role; - children = [innerNode, children[2]]; - type = cvox.SemanticAttr.Type.SUPERSCRIPT; - break; - case 'MOVER': - type = cvox.SemanticAttr.Type.OVERSCORE; - break; - case 'MUNDER': - type = cvox.SemanticAttr.Type.UNDERSCORE; - break; - case 'MUNDEROVER': - default: - var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.UNDERSCORE, - [center, children[1]], []); - innerNode.role = center.role; - children = [innerNode, children[2]]; - type = cvox.SemanticAttr.Type.OVERSCORE; - break; - } - } - var newNode = this.makeBranchNode_(type, children, []); - newNode.role = center.role; - return newNode; -}; - - -/** - * Recursive method to accumulate function expressions. - * - * The idea is to process functions in a row from left to right combining them - * with there arguments. Thereby we take the notion of a function rather broadly - * as a functional expressions that consists of a prefix and some arguments. - * In particular we distinguish four types of functional expressions: - * - integral: Integral expression. - * - bigop: A big operator expression like a sum. - * - prefix: A well defined prefix function such as sin, cos or a limit - * functions like lim, max. - * - simple: An expression consisting of letters that are potentially a function - * symbol. If we have an explicit function application symbol - * following the expression we turn into a prefix function. Otherwise - * we decide heuristically if we could have a function application. - * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of - * nodes. - * @param {!Array<cvox.SemanticTree.Node>=} result The result node list. - * @return {!Array<!cvox.SemanticTree.Node>} The fully processed list. - * @private - */ -cvox.SemanticTree.prototype.getFunctionsInRow_ = function(restNodes, result) { - result = result || []; - // Base case. - if (restNodes.length == 0) { - return result; - } - var firstNode = /** @type {!cvox.SemanticTree.Node} */ (restNodes.shift()); - var heuristic = cvox.SemanticTree.classifyFunction_(firstNode, restNodes); - // First node is not a function node. - if (!heuristic) { - result.push(firstNode); - return this.getFunctionsInRow_(restNodes, result); - } - // Combine functions in the rest of the row. - var processedRest = this.getFunctionsInRow_(restNodes, []); - var newRest = this.getFunctionArgs_(firstNode, processedRest, heuristic); - return result.concat(newRest); -}; - - -/** - * Classifies a function wrt. the heuristic that should be applied. - * @param {!cvox.SemanticTree.Node} funcNode The node to be classified. - * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of - * nodes. They can useful to look ahead if there is an explicit function - * application. If there is one, it will be destructively removed! - * @return {!string} The string specifying the heuristic. - * @private - */ -cvox.SemanticTree.classifyFunction_ = function(funcNode, restNodes) { - // We do not allow double function application. This is not lambda calculus! - if (funcNode.type == cvox.SemanticAttr.Type.APPL || - funcNode.type == cvox.SemanticAttr.Type.BIGOP || - funcNode.type == cvox.SemanticAttr.Type.INTEGRAL) { - return ''; - } - // Find and remove explicit function applications. - // We now treat funcNode as a prefix function, regardless of what its actual - // content is. - if (restNodes[0] && - restNodes[0].textContent == cvox.SemanticAttr.functionApplication()) { - // Remove explicit function application. This is destructive on the - // underlying list. - restNodes.shift(); - cvox.SemanticTree.propagatePrefixFunc_(funcNode); - return 'prefix'; - } - switch (funcNode.role) { - case cvox.SemanticAttr.Role.INTEGRAL: - return 'integral'; - break; - case cvox.SemanticAttr.Role.SUM: - return 'bigop'; - break; - case cvox.SemanticAttr.Role.PREFIXFUNC: - case cvox.SemanticAttr.Role.LIMFUNC: - return 'prefix'; - break; - default: - if (funcNode.type == cvox.SemanticAttr.Type.IDENTIFIER) { - return 'simple'; - } - } - return ''; -}; - - -/** - * Propagates a prefix function role in a node. - * @param {cvox.SemanticTree.Node} funcNode The node whose role is to be - * rewritten. - * @private - */ -cvox.SemanticTree.propagatePrefixFunc_ = function(funcNode) { - if (funcNode) { - funcNode.role = cvox.SemanticAttr.Role.PREFIXFUNC; - cvox.SemanticTree.propagatePrefixFunc_(funcNode.childNodes[0]); - } -}; - - -/** - * Computes the arguments for a function from a list of nodes depending on the - * given heuristic. - * @param {!cvox.SemanticTree.Node} func A function node. - * @param {!Array<cvox.SemanticTree.Node>} rest List of nodes to choose - * arguments from. - * @param {string} heuristic The heuristic to follow. - * @return {!Array<!cvox.SemanticTree.Node>} The function and the remainder of - * the rest list. - * @private - */ -cvox.SemanticTree.prototype.getFunctionArgs_ = function(func, rest, heuristic) { - switch (heuristic) { - case 'integral': - var components = this.getIntegralArgs_(rest); - var integrand = this.processRow_(components.integrand); - var funcNode = this.makeIntegralNode_(func, integrand, components.intvar); - components.rest.unshift(funcNode); - return components.rest; - break; - case 'prefix': - if (rest[0] && rest[0].type == cvox.SemanticAttr.Type.FENCED) { - funcNode = this.makeFunctionNode_( - func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); - rest.unshift(funcNode); - return rest; - } - case 'bigop': - var partition = cvox.SemanticTree.sliceNodes_( - rest, cvox.SemanticTree.prefixFunctionBoundary_); - var arg = this.processRow_(partition.head); - if (heuristic == 'prefix') { - funcNode = this.makeFunctionNode_(func, arg); - } else { - funcNode = this.makeBigOpNode_(func, arg); - } - if (partition.div) { - partition.tail.unshift(partition.div); - } - partition.tail.unshift(funcNode); - return partition.tail; - break; - case 'simple': - if (rest.length == 0) { - return [func]; - } - var firstArg = rest[0]; - if (firstArg.type == cvox.SemanticAttr.Type.FENCED && - firstArg.role != cvox.SemanticAttr.Role.NEUTRAL && - this.simpleFunctionHeuristic_(firstArg)) { - funcNode = this.makeFunctionNode_( - func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); - rest.unshift(funcNode); - return rest; - } - rest.unshift(func); - return rest; - break; - } -}; - - -/** - * Tail recursive function to obtain integral arguments. - * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to take - * arguments from. - * @param {Array<cvox.SemanticTree.Node>=} args List of integral arguments. - * @return {{integrand: !Array<cvox.SemanticTree.Node>, - * intvar: cvox.SemanticTree.Node, - * rest: !Array<cvox.SemanticTree.Node>}} - * Result split into integrand, integral variable and the remaining - * elements. - * @private - */ -cvox.SemanticTree.prototype.getIntegralArgs_ = function(nodes, args) { - args = args || []; - if (nodes.length == 0) { - return {integrand: args, intvar: null, rest: nodes}; - } - var firstNode = nodes[0]; - if (cvox.SemanticTree.generalFunctionBoundary_(firstNode)) { - return {integrand: args, intvar: null, rest: nodes}; - } - if (cvox.SemanticTree.integralDxBoundarySingle_(firstNode)) { - return {integrand: args, intvar: firstNode, rest: nodes.slice(1)}; - } - if (nodes[1] && cvox.SemanticTree.integralDxBoundary_(firstNode, nodes[1])) { - var comma = this.createNode_(); - comma.updateContent_(cvox.SemanticAttr.invisibleComma()); - var intvar = this.makePunctuatedNode_( - [firstNode, comma, nodes[1]], [comma]); - intvar.role = cvox.SemanticAttr.Role.INTEGRAL; - return {integrand: args, intvar: intvar, rest: nodes.slice(2)}; - } - args.push(nodes.shift()); - return this.getIntegralArgs_(nodes, args); -}; - - -/** - * Create a function node. - * @param {!cvox.SemanticTree.Node} func The function operator. - * @param {!cvox.SemanticTree.Node} arg The argument. - * @return {!cvox.SemanticTree.Node} The new function node. - * @private - */ -cvox.SemanticTree.prototype.makeFunctionNode_ = function(func, arg) { - var applNode = this.createNode_(); - applNode.updateContent_(cvox.SemanticAttr.functionApplication()); - applNode.type = cvox.SemanticAttr.Type.PUNCTUATION; - applNode.role = cvox.SemanticAttr.Role.APPLICATION; - var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.APPL, [func, arg], - [applNode]); - newNode.role = func.role; - return newNode; -}; - - -/** - * Create a big operator node. - * @param {!cvox.SemanticTree.Node} bigOp The big operator. - * @param {!cvox.SemanticTree.Node} arg The argument. - * @return {!cvox.SemanticTree.Node} The new big operator node. - * @private - */ -cvox.SemanticTree.prototype.makeBigOpNode_ = function(bigOp, arg) { - var newNode = this.makeBranchNode_( - cvox.SemanticAttr.Type.BIGOP, [bigOp, arg], []); - newNode.role = bigOp.role; - return newNode; -}; - - -/** - * Create an integral node. It has three children: integral, integrand and - * integration variable. The latter two can be omitted. - * @param {!cvox.SemanticTree.Node} integral The integral operator. - * @param {cvox.SemanticTree.Node} integrand The integrand. - * @param {cvox.SemanticTree.Node} intvar The integral variable. - * @return {!cvox.SemanticTree.Node} The new integral node. - * @private - */ -cvox.SemanticTree.prototype.makeIntegralNode_ = function( - integral, integrand, intvar) { - integrand = integrand || this.makeEmptyNode_(); - intvar = intvar || this.makeEmptyNode_(); - var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.INTEGRAL, - [integral, integrand, intvar], []); - newNode.role = integral.role; - return newNode; -}; - - -/** - * Predicate implementing the boundary criteria for simple functions: - * - * @param {!cvox.SemanticTree.Node} node A semantic node of type fenced. - * @return {boolean} True if the node meets the boundary criteria. - * @private - */ -cvox.SemanticTree.prototype.simpleFunctionHeuristic_ = function(node) { - var children = node.childNodes; - if (children.length == 0) { - return true; - } - if (children.length > 1) { - return false; - } - var child = children[0]; - if (child.type == cvox.SemanticAttr.Type.INFIXOP) { - if (child.role != cvox.SemanticAttr.Role.IMPLICIT) { - return false; - } - if (child.childNodes.some(cvox.SemanticTree.attrPred_('type', 'INFIXOP'))) { - return false; - } - } - return true; -}; - - -/** - * Predicate implementing the boundary criteria for prefix functions and big - * operators: - * 1. an explicit operator, - * 2. a relation symbol, or - * 3. some punctuation. - * @param {cvox.SemanticTree.Node} node A semantic node. - * @return {boolean} True if the node meets the boundary criteria. - * @private - */ -cvox.SemanticTree.prefixFunctionBoundary_ = function(node) { - return cvox.SemanticTree.attrPred_('type', 'OPERATOR')(node) || - cvox.SemanticTree.generalFunctionBoundary_(node); -}; - - -/** - * Predicate implementing the boundary criteria for integrals dx on two nodes. - * @param {cvox.SemanticTree.Node} firstNode A semantic node. - * @param {cvox.SemanticTree.Node} secondNode The direct neighbour of first - * Node. - * @return {boolean} True if the second node exists and the first node is a 'd'. - * @private - */ -cvox.SemanticTree.integralDxBoundary_ = function( - firstNode, secondNode) { - return !!secondNode && - cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(secondNode) && - cvox.SemanticAttr.isCharacterD(firstNode.textContent); -}; - - -/** - * Predicate implementing the boundary criteria for integrals dx on a single - * node. - * @param {cvox.SemanticTree.Node} node A semantic node. - * @return {boolean} True if the node meets the boundary criteria. - * @private - */ -cvox.SemanticTree.integralDxBoundarySingle_ = function(node) { - if (cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(node)) { - var firstChar = node.textContent[0]; - return firstChar && node.textContent[1] && - cvox.SemanticAttr.isCharacterD(firstChar); - } - return false; -}; - - -/** - * Predicate implementing the general boundary criteria for function operators: - * 1. a relation symbol, - * 2. some punctuation. - * @param {cvox.SemanticTree.Node} node A semantic node. - * @return {boolean} True if the node meets the boundary criteria. - * @private - */ -cvox.SemanticTree.generalFunctionBoundary_ = function(node) { - return cvox.SemanticTree.attrPred_('type', 'RELATION')(node) || - cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(node); -}; - - -/** - * Rewrites tables into matrices or case statements in a list of nodes. - * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to rewrite. - * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes. - * @private - */ -cvox.SemanticTree.prototype.processTablesInRow_ = function(nodes) { - // First we process all matrices: - var partition = cvox.SemanticTree.partitionNodes_( - nodes, cvox.SemanticTree.tableIsMatrixOrVector_); - var result = []; - for (var i = 0, matrix; matrix = partition.rel[i]; i++) { - result = result.concat(partition.comp.shift()); - result.push(this.tableToMatrixOrVector_(matrix)); - } - result = result.concat(partition.comp.shift()); - // Process the remaining tables for cases. - partition = cvox.SemanticTree.partitionNodes_( - result, cvox.SemanticTree.isTableOrMultiline_); - result = []; - for (var i = 0, table; table = partition.rel[i]; i++) { - var prevNodes = partition.comp.shift(); - if (cvox.SemanticTree.tableIsCases_(table, prevNodes)) { - this.tableToCases_( - table, /** @type {!cvox.SemanticTree.Node} */ (prevNodes.pop())); - } - result = result.concat(prevNodes); - result.push(table); - } - return result.concat(partition.comp.shift()); -}; - - -/** - * Decides if a node is a table or multiline element. - * @param {cvox.SemanticTree.Node} node A node. - * @return {boolean} True if node is either table or multiline. - * @private - */ -cvox.SemanticTree.isTableOrMultiline_ = function(node) { - return !!node && (cvox.SemanticTree.attrPred_('type', 'TABLE')(node) || - cvox.SemanticTree.attrPred_('type', 'MULTILINE')(node)); -}; - - -/** - * Heuristic to decide if we have a matrix: An expression fenced on both sides - * without any other content is considered a fenced node. - * @param {cvox.SemanticTree.Node} node A node. - * @return {boolean} True if we believe we have a matrix. - * @private - */ -cvox.SemanticTree.tableIsMatrixOrVector_ = function(node) { - return !!node && cvox.SemanticTree.attrPred_('type', 'FENCED')(node) && - cvox.SemanticTree.attrPred_('role', 'LEFTRIGHT')(node) && - node.childNodes.length == 1 && - cvox.SemanticTree.isTableOrMultiline_(node.childNodes[0]); -}; - - -/** - * Replaces a fenced node by a matrix or vector node. - * @param {!cvox.SemanticTree.Node} node The fenced node to be rewritten. - * @return {!cvox.SemanticTree.Node} The matrix or vector node. - * @private - */ -cvox.SemanticTree.prototype.tableToMatrixOrVector_ = function(node) { - var matrix = node.childNodes[0]; - var type = cvox.SemanticTree.attrPred_('type', 'MULTILINE')(matrix) ? - 'VECTOR' : 'MATRIX'; - matrix.type = cvox.SemanticAttr.Type[type]; - node.contentNodes.forEach(goog.bind(matrix.appendContentNode_, matrix)); - for (var i = 0, row; row = matrix.childNodes[i]; i++) { - cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role[type]); - } - return matrix; -}; - - -/** - * Heuristic to decide if we have a case statement: An expression with a - * singular open fence before it. - * @param {!cvox.SemanticTree.Node} table A table node. - * @param {!Array<cvox.SemanticTree.Node>} prevNodes A list of previous nodes. - * @return {boolean} True if we believe we have a case statement. - * @private - */ -cvox.SemanticTree.tableIsCases_ = function(table, prevNodes) { - return prevNodes.length > 0 && - cvox.SemanticTree.attrPred_('role', 'OPENFENCE')( - prevNodes[prevNodes.length - 1]); -}; - - -/** - * Makes case node out of a table and a fence. - * @param {!cvox.SemanticTree.Node} table The table containing the cases. - * @param {!cvox.SemanticTree.Node} openFence The left delimiter of the case - * statement. - * @return {!cvox.SemanticTree.Node} The cases node. - * @private - */ -cvox.SemanticTree.prototype.tableToCases_ = function(table, openFence) { - for (var i = 0, row; row = table.childNodes[i]; i++) { - cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role.CASES); - // } - } - table.type = cvox.SemanticAttr.Type.CASES; - table.appendContentNode_(openFence); - return table; -}; - - -// TODO (sorge) This heuristic is very primitive. We could start reworking -// multilines, by combining all cells, semantically rewriting the entire line -// and see if there are any similarities. Alternatively, we could look for -// similarities in columns (e.g., single relation symbols, like equalities or -// inequalities in the same column could indicate an equation array). -/** - * Heuristic to decide if we have a multiline formula. A table is considered a - * multiline formula if it does not have any separate cells. - * @param {!cvox.SemanticTree.Node} table A table node. - * @return {boolean} True if we believe we have a mulitline formula. - * @private - */ -cvox.SemanticTree.tableIsMultiline_ = function(table) { - return table.childNodes.every( - function(row) { - var length = row.childNodes.length; - return length <= 1;}); -}; - - -/** - * Rewrites a table to multiline structure, simplifying it by getting rid of the - * cell hierarchy level. - * @param {!cvox.SemanticTree.Node} table The node to be rewritten a multiline. - * @private - */ -cvox.SemanticTree.prototype.tableToMultiline_ = function(table) { - table.type = cvox.SemanticAttr.Type.MULTILINE; - for (var i = 0, row; row = table.childNodes[i]; i++) { - cvox.SemanticTree.rowToLine_(row, cvox.SemanticAttr.Role.MULTILINE); - } -}; - - -/** - * Converts a row that only contains one cell into a single line. - * @param {!cvox.SemanticTree.Node} row The row to convert. - * @param {cvox.SemanticAttr.Role=} role The new role for the line. - * @private - */ -cvox.SemanticTree.rowToLine_ = function(row, role) { - role = role || cvox.SemanticAttr.Role.UNKNOWN; - if (cvox.SemanticTree.attrPred_('type', 'ROW')(row) && - row.childNodes.length == 1 && - cvox.SemanticTree.attrPred_('type', 'CELL')(row.childNodes[0])) { - row.type = cvox.SemanticAttr.Type.LINE; - row.role = role; - row.childNodes = row.childNodes[0].childNodes; - } -}; - - -/** - * Assign a row and its contained cells a new role value. - * @param {!cvox.SemanticTree.Node} row The row to be updated. - * @param {!cvox.SemanticAttr.Role} role The new role for the row and its cells. - * @private - */ -cvox.SemanticTree.assignRoleToRow_ = function(row, role) { - if (cvox.SemanticTree.attrPred_('type', 'LINE')(row)) { - row.role = role; - return; - } - if (cvox.SemanticTree.attrPred_('type', 'ROW')(row)) { - row.role = role; - var cellPred = cvox.SemanticTree.attrPred_('type', 'CELL'); - row.childNodes.forEach(function(cell) { - if (cellPred(cell)) { - cell.role = role; - } - }); - } -}; - - -/** - * Splits a list of nodes wrt. to a given predicate. - * @param {Array<cvox.SemanticTree.Node>} nodes A list of nodes. - * @param {!function(cvox.SemanticTree.Node): boolean} pred Predicate for the - * partitioning relation. - * @param {boolean=} reverse If true slicing is done from the end. - * @return {{head: !Array<cvox.SemanticTree.Node>, - * div: cvox.SemanticTree.Node, - * tail: !Array<cvox.SemanticTree.Node>}} The split list. - * @private - */ -cvox.SemanticTree.sliceNodes_ = function(nodes, pred, reverse) { - if (reverse) { - nodes.reverse(); - } - var head = []; - for (var i = 0, node; node = nodes[i]; i++) { - if (pred(node)) { - if (reverse) { - return {head: nodes.slice(i + 1).reverse(), - div: node, - tail: head.reverse()}; - } - return {head: head, - div: node, - tail: nodes.slice(i + 1)}; - } - head.push(node); - } - if (reverse) { - return {head: [], div: null, tail: head.reverse()}; - } - return {head: head, div: null, tail: []}; -}; - - -/** - * Partitions a list of nodes wrt. to a given predicate. Effectively works like - * a PER on the ordered set of nodes. - * @param {!Array<!cvox.SemanticTree.Node>} nodes A list of nodes. - * @param {!function(cvox.SemanticTree.Node): boolean} pred Predicate for the - * partitioning relation. - * @return {{rel: !Array<cvox.SemanticTree.Node>, - * comp: !Array<!Array<cvox.SemanticTree.Node>>}} - * The partitioning given in terms of a collection of elements satisfying - * the predicate and a collection of complementary sets lying inbetween the - * related elements. Observe that we always have |comp| = |rel| + 1. - * - * Example: On input [a, r_1, b, c, r_2, d, e, r_3] where P(r_i) holds, we - * get as output: {rel: [r_1, r_2, r_3], comp: [[a], [b, c], [d, e], []]. - * @private - */ -cvox.SemanticTree.partitionNodes_ = function(nodes, pred) { - var restNodes = nodes; - var rel = []; - var comp = []; - - do { - var result = cvox.SemanticTree.sliceNodes_(restNodes, pred); - comp.push(result.head); - rel.push(result.div); - restNodes = result.tail; - } while (result.div); - rel.pop(); - return {rel: rel, comp: comp}; -}; - - -/** - * Constructs a predicate to check the semantic attribute of a node. - * @param {!string} prop The property of a node. - * @param {!string} attr The attribute. - * @return {function(cvox.SemanticTree.Node): boolean} The predicate. - * @private - */ - -cvox.SemanticTree.attrPred_ = function(prop, attr) { - var getAttr = function(prop) { - switch (prop) { - case 'type': return cvox.SemanticAttr.Type[attr]; - case 'role': return cvox.SemanticAttr.Role[attr]; - case 'font': return cvox.SemanticAttr.Font[attr]; - } - }; - - return function(node) {return node[prop] == getAttr(prop);}; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree_test.unitjs deleted file mode 100644 index d6948aebd65..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_tree_test.unitjs +++ /dev/null @@ -1,6188 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxSemanticTreeUnitTest() {} - -CvoxSemanticTreeUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.SemanticAttr', - 'cvox.SemanticTree', - 'cvox.SemanticUtil', - 'cvox.XpathUtil' - ], - - /** @override */ - setUp: function() { - this.nodeCounter = 0; - this.xpathBlacklist = []; - this.brief = true; - this.setupAttributes(); - }, - - /** - * Adds some unicode characters via hex code to the right category. - * - * This method is necessary as the test framework can not handle code - * containing utf-8 encoded characters. - */ - setupAttributes: function() { - var attr = cvox.SemanticAttr.getInstance(); - attr.neutralFences.unshift(cvox.SemanticUtil.numberToUnicode(0x00A6)); - attr.dashes.unshift(cvox.SemanticUtil.numberToUnicode(0x2015)); - attr.neutralFences.unshift(cvox.SemanticUtil.numberToUnicode(0x2016)); - attr.arrows.unshift(cvox.SemanticUtil.numberToUnicode(0x2192)); - attr.sumOps.unshift(cvox.SemanticUtil.numberToUnicode(0x2211)); - attr.additions.unshift(cvox.SemanticUtil.numberToUnicode(0x2213)); - attr.multiplications.unshift(cvox.SemanticUtil.numberToUnicode(0x2218)); - attr.intOps.unshift(cvox.SemanticUtil.numberToUnicode(0x222B)); - attr.inequalities.unshift(cvox.SemanticUtil.numberToUnicode(0x2264)); - attr.additions.unshift(cvox.SemanticUtil.numberToUnicode(0x2295)); - var open = cvox.SemanticUtil.numberToUnicode(0x3008); - var close = cvox.SemanticUtil.numberToUnicode(0x3009); - attr.openClosePairs[open] = close; - attr.leftFences.unshift(open); - attr.rightFences.unshift(close); - }, - - /** - * Removes XML nodes according to the XPath elements in the blacklist. - * @param {Node} xml Xml representation of the semantic node. - */ - customizeXml: function(xml) { - this.xpathBlacklist.forEach( - function(xpath) { - var removes = cvox.XpathUtil.evalXPath(xpath, xml); - removes.forEach( - function(node) { - node.parentNode.removeChild(node); - }); - }); - }, - - /** - * Tests if for a given mathml snippet results in a particular semantic tree. - * @param {string} mml MathML expression. - * @param {string} sml XML snippet for the semantic tree. - */ - executeTreeTest: function(mml, sml) { - var mathMl = '<math id=' + this.nodeCounter + '>' + mml + ''; - this.loadHtml(mathMl); - var node = document.getElementById((this.nodeCounter++).toString()); - var stree = new cvox.SemanticTree(/** @type {!Element} */(node)); - var sxml = stree.xml(this.brief); - this.customizeXml(sxml); - var dp = new DOMParser(); - var xml = dp.parseFromString('<stree>' + sml + '</stree>', 'text/xml'); - var xmls = new XMLSerializer(); - assertEquals(xmls.serializeToString(xml), - xmls.serializeToString(sxml)); - } -}; - -TEST_F('CvoxSemanticTreeUnitTest', 'StreeRelations', function() { - this.brief = true; - this.executeTreeTest( - '<mo>=</mo>', - '<relation>=</relation>'); - this.executeTreeTest( - '<mi>a</mi><mo>=</mo><mi>b</mi>', - '<relseq>=' + - '<content><relation>=</relation></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); - this.executeTreeTest( - '<mi>a</mi><mo>=</mo><mi>b</mi><mo>=</mo><mi>c</mi>', - '<relseq>=' + - '<content><relation>=</relation><relation>=</relation></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</relseq>'); - this.executeTreeTest( - '<mi>a</mi><mo>=</mo><mi>b</mi><mo>=</mo><mi>c</mi>' + - '<mo>\u2264</mo><mi>d</mi>', - '<multirel>' + - '<content><relation>=</relation><relation>=</relation>' + - '<relation>\u2264</relation></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '<identifier>d</identifier>' + - '</children>' + - '</multirel>'); -}); - - -// Operators. -/** - * Test operator trees with pre- and postfixes. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrePostfixOperators', function() { - this.brief = true; - // Pathological operator only case. - this.executeTreeTest( - '<mo>+</mo><mo>-</mo><mo>+</mo>', - '<prefixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<prefixop>-' + - '<content><operator>-</operator></content>' + - '<children>' + - '<operator>+</operator>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</prefixop>'); - // Single identifier with prefixes. - this.executeTreeTest( - '<mo>+</mo><mo>+</mo><mi>a</mi>', - '<prefixop>+ +' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</prefixop>'); - // Single identifier with prefix and negative. - this.executeTreeTest( - '<mo>+</mo><mo>-</mo><mi>a</mi>', - '<prefixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<prefixop>-' + - '<content><operator>-</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</prefixop>'); - // Single identifier with postfixes. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mo>-</mo>', - '<postfixop>+ -' + - '<content><operator>+</operator><operator>-</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</postfixop>'); - // Single identifier with pre- and postfixes. - this.executeTreeTest( - '<mo>+</mo><mo>+</mo><mi>a</mi><mo>+</mo><mo>+</mo>', - '<postfixop>+ +' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<prefixop>+ +' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</postfixop>'); - // Single identifier with mixed pre- and postfixes. - this.executeTreeTest( - '<mo>\u2213</mo><mo>+</mo><mi>a</mi><mo>\u2213</mo><mo>+</mo>', - '<postfixop>\u2213 +' + - '<content>' + - '<operator>\u2213</operator><operator>+</operator>' + - '</content>' + - '<children>' + - '<prefixop>\u2213 +' + - '<content>' + - '<operator>\u2213</operator><operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</postfixop>'); - // Two identifiers with pre- and postfixes. - this.executeTreeTest( - '<mo>+</mo><mo>+</mo><mi>a</mi><mo>\u2213</mo><mo>+</mo>' + - '<mi>b</mi><mo>+</mo>', - '<infixop>\u2213' + - '<content><operator>\u2213</operator></content>' + - '<children>' + - '<prefixop>+ +' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</prefixop>' + - '<postfixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<prefixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<identifier>b</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</postfixop>' + - '</children>' + - '</infixop>'); - // Three identifiers with pre- and postfixes. - this.executeTreeTest( - '<mo>+</mo><mo>+</mo><mi>a</mi><mo>\u2213</mo><mo>+</mo>' + - '<mi>b</mi><mo>+</mo><mo>\u2213</mo><mi>c</mi><mo>+</mo>', - '<infixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<infixop>\u2213' + - '<content><operator>\u2213</operator></content>' + - '<children>' + - '<prefixop>+ +' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</prefixop>' + - '<prefixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<identifier>b</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</infixop>' + - '<postfixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<prefixop>\u2213' + - '<content><operator>\u2213</operator></content>' + - '<children>' + - '<identifier>c</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</postfixop>' + - '</children>' + - '</infixop>'); -}); - - -/** - * Test operator trees with single operator. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeSingleOperators', function() { - this.brief = true; - // Single identifier. - this.executeTreeTest( - '<mi>a</mi>', - '<identifier>a</identifier>'); - // Single implicit node. - this.executeTreeTest( - '<mi>a</mi><mi>b</mi>', - '<infixop>\u2062' + - '<content><operator>\u2062</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - // Implicit multi node. - this.executeTreeTest( - '<mi>a</mi><mi>b</mi><mi>c</mi>', - '<infixop>\u2062' + - '<content><operator>\u2062</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>'); - // Single addition. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi>', - '<infixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - // Multi addition. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mo>+</mo><mi>c</mi>', - '<infixop>+' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>'); - // Multi addition with implicit node. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mi>c</mi><mo>+</mo><mi>d</mi>', - '<infixop>+' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<infixop>\u2062' + - '<content><operator>\u2062</operator></content>' + - '<children>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>'); -}); - - -/** - * Test operator trees with multiple operators. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeMultipleOperators', function() { - this.brief = true; - // Addition and subtraction. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mo>-</mo><mi>c</mi><mo>+</mo><mi>d</mi>', - '<infixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<infixop>-' + - '<content><operator>-</operator></content>' + - '<children>' + - '<infixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>'); - // Addition and subtraction. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mo>+</mo><mi>c</mi><mo>-</mo>' + - '<mi>d</mi><mo>-</mo><mi>e</mi>', - '<infixop>-' + - '<content><operator>-</operator><operator>-</operator></content>' + - '<children>' + - '<infixop>+' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>d</identifier>' + - '<identifier>e</identifier>' + - '</children>' + - '</infixop>'); - // Addition and explicit multiplication. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mo>\u2218</mo><mi>c</mi><mo>+</mo>' + - '<mi>d</mi>', - '<infixop>+' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<infixop>\u2218' + - '<content><operator>\u2218</operator></content>' + - '<children>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>'); - // Addition with explicit and implicit multiplication. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mo>\u2218</mo><mi>c</mi><mi>d</mi>' + - '<mo>+</mo><mi>e</mi><mo>\u2218</mo><mi>f</mi>', - '<infixop>+' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<infixop>\u2218' + - '<content><operator>\u2218</operator></content>' + - '<children>' + - '<identifier>b</identifier>' + - '<infixop>\u2062' + - '<content><operator>\u2062</operator></content>' + - '<children>' + - '<identifier>c</identifier>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</infixop>' + - '<infixop>\u2218' + - '<content><operator>\u2218</operator></content>' + - '<children>' + - '<identifier>e</identifier>' + - '<identifier>f</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</infixop>'); - // Two Additions, subtraction plus explicit and implicit multiplication, - // one prefix and one postfix. - this.executeTreeTest( - '<mi>a</mi><mo>+</mo><mi>b</mi><mo>+</mo><mi>c</mi><mi>d</mi>' + - '<mo>+</mo><mi>e</mi><mo>\u2218</mo><mi>f</mi><mo>-</mo><mi>g</mi>' + - '<mo>+</mo><mo>+</mo><mi>h</mi><mo>\u2295</mo><mi>i</mi>' + - '<mo>\u2295</mo><mi>j</mi><mo>+</mo><mo>+</mo>', - '<infixop>\u2295' + - '<content><operator>\u2295</operator>' + - '<operator>\u2295</operator></content>' + - '<children>' + - '<infixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<infixop>-' + - '<content><operator>-</operator></content>' + - '<children>' + - '<infixop>+' + - '<content><operator>+</operator>' + - '<operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<infixop>\u2062' + - '<content><operator>\u2062</operator></content>' + - '<children>' + - '<identifier>c</identifier>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '<infixop>\u2218' + - '<content><operator>\u2218</operator></content>' + - '<children>' + - '<identifier>e</identifier>' + - '<identifier>f</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</infixop>' + - '<identifier>g</identifier>' + - '</children>' + - '</infixop>' + - '<prefixop>+' + - '<content><operator>+</operator></content>' + - '<children>' + - '<identifier>h</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</infixop>' + - '<identifier>i</identifier>' + - '<postfixop>+ +' + - '<content><operator>+</operator><operator>+</operator></content>' + - '<children>' + - '<identifier>j</identifier>' + - '</children>' + - '</postfixop>' + - '</children>' + - '</infixop>'); -}); - - -// Fences. -/** - * Test regular directed fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeRegularFences', function() { - this.brief = true; - // No fence. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>+</mo><mi>b</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - // Empty parentheses. - this.executeTreeTest( - '<mrow><mo>(</mo><mo>)</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<empty/>' + - '</children>' + - '</fenced>'); - // Single Fenced Expression. - this.executeTreeTest( - '<mrow><mo>(</mo><mi>a</mi><mo>+</mo><mi>b</mi><mo>)</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>'); - // Single Fenced Expression and operators. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>+</mo><mo>(</mo><mi>b</mi><mo>+</mo><mi>c</mi>' + - '<mo>)</mo><mo>+</mo><mi>d</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>'); - // Parallel Parenthesis. - this.executeTreeTest( - '<mrow><mo>(</mo><mi>a</mi><mo>+</mo><mi>b</mi><mo>)</mo><mo>(</mo>' + - '<mi>c</mi><mo>+</mo><mi>d</mi><mo>)</mo></mrow>', - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>'); - // Nested Parenthesis. - this.executeTreeTest( - '<mrow><mo>(</mo><mo>(</mo><mi>a</mi><mo>+</mo><mi>b</mi><mo>)</mo>' + - '<mo>(</mo><mi>c</mi><mo>+</mo><mi>d</mi><mo>)</mo><mo>)</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>'); - // Nested parenthesis and brackets. - this.executeTreeTest( - '<mrow><mo>(</mo><mo>[</mo><mi>a</mi><mo>+</mo><mi>b</mi><mo>+</mo>' + - '<mi>c</mi><mo>]</mo><mo>+</mo><mi>d</mi><mo>)</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>'); - // Nested parenthesis, brackets, braces and superscript operator. - this.executeTreeTest( - '<mrow><mo>(</mo><msup><mi>a</mi><mrow><mn>2</mn><mo>[</mo><mi>i</mi>' + - '<mo>+</mo><mi>n</mi><mo>]</mo></mrow></msup><mo>+</mo><mi>b</mi>' + - '<mo>)</mo><mo>+</mo><mo>{</mo><mi>c</mi><mi>d</mi><mo>-</mo><mo>[</mo>' + - '<mi>e</mi><mo>+</mo><mi>f</mi><mo>]</mo><mo>}</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<identifier>a</identifier>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<number>2</number>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>i</identifier>' + - '<identifier>n</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '</children>' + - '</superscript>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<fenced>' + - '<content>' + - '<fence>{</fence>' + - '<fence>}</fence>' + - '</content>' + - '<children>' + - '<infixop>-' + - '<content>' + - '<operator>-</operator>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>e</identifier>' + - '<identifier>f</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>'); -}); - - -/** - * Test neutral fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeNeutralFences', function() { - this.brief = true; - // Empty bars. - this.executeTreeTest( - '<mrow><mo>|</mo><mo>|</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<empty/>' + - '</children>' + - '</fenced>'); - // Simple bar fence. - this.executeTreeTest( - '<mrow><mo>|</mo><mi>a</mi><mo>|</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</fenced>'); - // Parallel bar fences. - this.executeTreeTest( - '<mrow><mo>|</mo><mi>a</mi><mo>|</mo><mi>b</mi><mo>+</mo>' + - '<mo>\u00A6</mo><mi>c</mi><mo>\u00A6</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</fenced>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '<fenced>' + - '<content>' + - '<fence>\u00A6</fence>' + - '<fence>\u00A6</fence>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>'); - // Nested bar fences. - this.executeTreeTest( - '<mrow><mo>\u00A6</mo><mo>|</mo><mi>a</mi><mo>|</mo><mi>b</mi>' + - '<mo>+</mo><mi>c</mi><mo>\u00A6</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>\u00A6</fence>' + - '<fence>\u00A6</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</fenced>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>'); -}); - - -/** - * Mixed neutral and regular fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedFences', function() { - this.brief = true; - // Empty parenthsis inside bars. - this.executeTreeTest( - '<mrow><mo>|</mo><mo>(</mo><mo>)</mo><mo>|</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<empty/>' + - '</children>' + - '</fenced>' + - '</children>' + - '</fenced>'); - // Bars inside parentheses. - this.executeTreeTest( - '<mrow><mo>(</mo><mo>|</mo><mi>a</mi><mo>|</mo><mi>b</mi>' + - '<mo>¦</mo><mi>c</mi><mo>¦</mo><mi>d</mi>' + - '<mo>)</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '</children>' + - '</fenced>' + - '<identifier>b</identifier>' + - '<fenced>' + - '<content>' + - '<fence>\u00A6</fence>' + - '<fence>\u00A6</fence>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '</children>' + - '</fenced>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>'); - // Parentheses inside bards. - this.executeTreeTest( - '<mrow><mo>|</mo><mo>(</mo><mi>a</mi><mo>+</mo><mi>b</mi><mo>)</mo>' + - '<mo>¦</mo><mi>c</mi><mo>¦</mo><mi>d</mi><mo>|</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<fenced>' + - '<content>' + - '<fence>\u00A6</fence>' + - '<fence>\u00A6</fence>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '</children>' + - '</fenced>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>'); - // Parentheses inside bards. - this.executeTreeTest( - '<mrow><mo>[</mo><mo>|</mo><mi>a</mi><mo>+</mo><mi>b</mi><mo>|</mo>' + - '<mo>+</mo><mi>c</mi><mo>]</mo><mo>+</mo><mo>\u00A6</mo><mi>d</mi>' + - '<mo>+</mo><mo>(</mo><mi>e</mi><mo>+</mo><mi>f</mi><mo>)</mo>' + - '<mo>\u00A6</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '<fenced>' + - '<content>' + - '<fence>\u00A6</fence>' + - '<fence>\u00A6</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>d</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>e</identifier>' + - '<identifier>f</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>'); -}); - - -/** - * Mixed with isolated bars. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedFencesWithBars', function() { - this.brief = true; - this.xpathBlacklist = ['descendant::punctuated/content']; - // Set notation. - this.executeTreeTest( - '<mrow><mo>{</mo><mo>(</mo><mi>x</mi><mo>,</mo><mi>y</mi><mo>,</mo>' + - '<mi>z</mi><mo>)</mo><mo>|</mo><mi>x</mi><mi>y</mi><mo>=</mo>' + - '<mo>z</mo><mo>}</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>{</fence>' + - '<fence>}</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>x</identifier>' + - '<punctuation>,</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>,</punctuation>' + - '<identifier>z</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '<identifier>z</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>'); - // Disjunction of bracketed parallel statements. - this.executeTreeTest( - '<mrow><mo>[</mo><mi>a</mi><mo>‖</mo><mi>b</mi><mo>]</mo>' + - '<mo>|</mo><mo>[</mo><mi>x</mi><mo>‖</mo><mi>y</mi><mo>]</mo>' + - '</mrow>', - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>x</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</punctuated>' - ); - // Metric over the above. - this.executeTreeTest( - '<mrow><mo>‖</mo><mo>[</mo><mi>a</mi><mo>‖</mo>' + - '<mi>b</mi><mo>]</mo><mo>|</mo><mo>[</mo><mi>x</mi><mo>‖</mo>' + - '<mi>y</mi><mo>]</mo><mo>‖</mo></mrow>', - '<fenced>' + - '<content>' + - '<fence>\u2016</fence>' + - '<fence>\u2016</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>x</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>'); - // Mix of metrics and bracketed expression and single bars. - this.executeTreeTest( - '<mrow><mo>‖</mo><mo>[</mo><mi>a</mi><mo>‖</mo><mi>b</mi>' + - '<mo>]</mo><mo>|</mo><mo>[</mo><mi>c</mi><mo>‖</mo>' + - '<mo>¦</mo><mi>d</mi><mo>]</mo><mo>‖</mo><mo>[</mo>' + - '<mi>u</mi><mo>‖</mo><mi>v</mi><mo>]</mo><mo>|</mo><mi>x</mi>' + - '<mo>‖</mo><mi>y</mi><mo>¦</mo><mi>z</mi></mrow>', - '<punctuated>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>\u2016</fence>' + - '<fence>\u2016</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>c</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>d</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>u</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>v</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '<punctuation>|</punctuation>' + - '<identifier>x</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>z</identifier>' + - '</children>' + - '</punctuated>'); - this.xpathBlacklist = []; -}); - - -/** - * Pathological cases with only opening fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeOpeningFencesOnly', function() { - this.brief = true; - this.xpathBlacklist = ['descendant::punctuated/content']; - // Single. - this.executeTreeTest( - '<mrow><mo>[</mo></mrow>', - '<fence>[</fence>'); - // Single right. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>[</mo></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>[</punctuation>' + - '</children>' + - '</punctuated>'); - // Single middle. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>[</mo><mi>b</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>[</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>'); - // Single left. - this.executeTreeTest( - '<mrow><mo>[</mo><mi>b</mi></mrow>', - '<punctuated>' + - '<children>' + - '<punctuation>[</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>[</mo><mi>b</mi><mi>c</mi><mo>(</mo><mi>d</mi>' + - '<mo>{</mo><mi>e</mi><mo>〈</mo><mi>f</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>[</punctuation>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '<punctuation>(</punctuation>' + - '<identifier>d</identifier>' + - '<punctuation>{</punctuation>' + - '<identifier>e</identifier>' + - '<punctuation>\u3008</punctuation>' + - '<identifier>f</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple plus inner fenced. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>[</mo><mi>b</mi><mo>[</mo><mo>(</mo><mo>(</mo>' + - '<mi>c</mi><mo>)</mo><mi>d</mi><mo>{</mo><mi>e</mi><mo>〈</mo>' + - '<mi>f</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>[</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>[</punctuation>' + - '<punctuation>(</punctuation>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '</children>' + - '</fenced>' + - '<identifier>d</identifier>' + - '</children>' + - '</infixop>' + - '<punctuation>{</punctuation>' + - '<identifier>e</identifier>' + - '<punctuation>\u3008</punctuation>' + - '<identifier>f</identifier>' + - '</children>' + - '</punctuated>'); - this.xpathBlacklist = []; -}); - - -/** - * Pathological cases with only closing fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeClosingFencesOnly', function() { - this.brief = true; - this.xpathBlacklist = ['descendant::punctuated/content']; - // Single. - this.executeTreeTest( - '<mrow><mo>]</mo></mrow>', - '<fence>]</fence>'); - // Single right. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>]</mo></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>]</punctuation>' + - '</children>' + - '</punctuated>'); - // Single middle. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>]</mo><mi>b</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>]</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>'); - // Single left. - this.executeTreeTest( - '<mrow><mo>]</mo><mi>b</mi></mrow>', - '<punctuated>' + - '<children>' + - '<punctuation>]</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>]</mo><mi>b</mi><mi>c</mi><mo>)</mo><mi>d</mi>' + - '<mo>}</mo><mi>e</mi><mo>〉</mo><mi>f</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>]</punctuation>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>b</identifier>' + - '<identifier>c</identifier>' + - '</children>' + - '</infixop>' + - '<punctuation>)</punctuation>' + - '<identifier>d</identifier>' + - '<punctuation>}</punctuation>' + - '<identifier>e</identifier>' + - '<punctuation>\u3009</punctuation>' + - '<identifier>f</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple plus inner fenced. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>]</mo><mi>b</mi><mo>]</mo><mo>(</mo><mi>c</mi>' + - '<mo>)</mo><mo>)</mo><mi>d</mi><mo>}</mo><mi>e</mi><mo>〉</mo>' + - '<mi>f</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>]</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>]</punctuation>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>c</identifier>' + - '</children>' + - '</fenced>' + - '<punctuation>)</punctuation>' + - '<identifier>d</identifier>' + - '<punctuation>}</punctuation>' + - '<identifier>e</identifier>' + - '<punctuation>\u3009</punctuation>' + - '<identifier>f</identifier>' + - '</children>' + - '</punctuated>'); - this.xpathBlacklist = []; -}); - - -/** - * Pathological cases with only neutral fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeNeutralFencesOnly', function() { - this.brief = true; - this.xpathBlacklist = ['descendant::punctuated/content']; - // Single. - this.executeTreeTest( - '<mrow><mo>|</mo></mrow>', - '<fence>|</fence>'); - // Single right. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>|</mo></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>|</punctuation>' + - '</children>' + - '</punctuated>'); - // Single middle. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>|</mo><mi>b</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>|</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>'); - // Single left. - this.executeTreeTest( - '<mrow><mo>|</mo><mi>b</mi></mrow>', - '<punctuated>' + - '<children>' + - '<punctuation>|</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>'); - // Two different bars. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>|</mo><mi>b</mi><mo>¦</mo><mi>c</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>|</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>c</identifier>' + - '</children>' + - '</punctuated>'); - // Three different bars. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>‖</mo><mi>b</mi><mo>|</mo><mi>c</mi>' + - '<mo>¦</mo><mi>d</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>|</punctuation>' + - '<identifier>c</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>d</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple plus inner fenced. - this.executeTreeTest( - '<mrow><mo>‖</mo><mo>[</mo><mi>a</mi><mo>‖</mo><mi>b</mi>' + - '<mo>]</mo><mo>‖</mo><mo>|</mo><mi>x</mi><mo>‖</mo>' + - '<mi>y</mi><mo>¦</mo><mi>z</mi></mrow>', - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>\u2016</fence>' + - '<fence>\u2016</fence>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<identifier>x</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>z</identifier>' + - '</children>' + - '</punctuated>'); - this.xpathBlacklist = []; -}); - - -/** - * Pathological cases with mixed fences. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeMixedUnmatchedFences', function() { - this.brief = true; - this.xpathBlacklist = ['descendant::punctuated/content']; - // Close, neutral, open. - this.executeTreeTest( - '<mrow><mo>]</mo><mo>‖</mo><mi>b</mi><mo>|</mo><mi>c</mi>' + - '<mo>(</mo></mrow>', - '<punctuated>' + - '<children>' + - '<punctuation>]</punctuation>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>|</punctuation>' + - '<identifier>c</identifier>' + - '<punctuation>(</punctuation>' + - '</children>' + - '</punctuated>'); - // Neutrals and close. - this.executeTreeTest( - '<mrow><mi>a</mi><mo>‖</mo><mi>b</mi><mo>|</mo><mi>c</mi>' + - '<mo>¦</mo><mi>d</mi><mo>]</mo><mi>e</mi></mrow>', - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>|</punctuation>' + - '<identifier>c</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>d</identifier>' + - '<punctuation>]</punctuation>' + - '<identifier>e</identifier>' + - '</children>' + - '</punctuated>'); - // Neutrals and open. - this.executeTreeTest( - '<mrow><mo>[</mo><mi>a</mi><mo>‖</mo><mi>b</mi><mo>|</mo>' + - '<mi>c</mi><mo>¦</mo><mi>d</mi></mrow>', - '<punctuated>' + - '<children>' + - '<punctuation>[</punctuation>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '<punctuation>|</punctuation>' + - '<identifier>c</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>d</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple fences, fenced and operations - this.executeTreeTest( - '<mrow><mo>‖</mo><mo>[</mo><mi>a</mi><mo>‖</mo><mi>b</mi>' + - '<mo>]</mo><mo>|</mo><mo>[</mo><mi>c</mi><mo>‖</mo>' + - '<mo>¦</mo><mi>d</mi><mo>]</mo><mo>‖</mo><mo>|</mo>' + - '<mi>x</mi><mo>‖</mo><mi>y</mi><mo>¦</mo><mi>z</mi>' + - '<mo>]</mo></mrow>', - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>\u2016</fence>' + - '<fence>\u2016</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>c</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>d</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>|</punctuation>' + - '<identifier>x</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>z</identifier>' + - '<punctuation>]</punctuation>' + - '</children>' + - '</punctuated>'); - // Multiple fences, fenced and operations - this.executeTreeTest( - '<mrow><mo>‖</mo><mo>]</mo><mo>¦</mo><mo>‖</mo>' + - '<mo>[</mo><mo>|</mo><mo>[</mo><mi>a</mi><mo>‖</mo><mi>b</mi>' + - '<mo>]</mo><mo>‖</mo><mo>|</mo><mi>[</mi><mo>‖</mo>' + - '<mi>y</mi><mo>¦</mo><mi>z</mi></mrow>', - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>\u2016</fence>' + - '<fence>\u2016</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<punctuation>]</punctuation>' + - '<punctuation>\u00A6</punctuation>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>[</punctuation>' + - '<fenced>' + - '<content>' + - '<fence>|</fence>' + - '<fence>|</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>a</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>b</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>\u2016</punctuation>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '<punctuation>[</punctuation>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<identifier>z</identifier>' + - '</children>' + - '</punctuated>'); - // Multiple fences, fenced and operations - this.executeTreeTest( - '<mrow><mo>‖</mo><mo>[</mo><mi>a</mi><mo>¦</mo>' + - '<mo>‖</mo><mo>[</mo><mo>+</mo><mo>[</mo><mi>b</mi>' + - '<mo>‖</mo><mi>c</mi><mo>]</mo><mo>+</mo><mo>‖</mo>' + - '<mo>|</mo><mi>d</mi><mo>+</mo><mi>e</mi><mi>[</mi><mo>‖</mo>' + - '<mi>y</mi><mo>¦</mo><mo>+</mo><mi>z</mi></mrow>', - '<punctuated>' + - '<children>' + - '<punctuation>\u2016</punctuation>' + - '<punctuation>[</punctuation>' + - '<identifier>a</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<punctuation>\u2016</punctuation>' + - '<punctuation>[</punctuation>' + - '<postfixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<prefixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<children>' + - '<identifier>b</identifier>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>c</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</postfixop>' + - '<punctuation>\u2016</punctuation>' + - '<punctuation>|</punctuation>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>d</identifier>' + - '<identifier>e</identifier>' + - '</children>' + - '</infixop>' + - '<punctuation>[</punctuation>' + - '<punctuation>\u2016</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>\u00A6</punctuation>' + - '<prefixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>z</identifier>' + - '</children>' + - '</prefixop>' + - '</children>' + - '</punctuated>'); - this.xpathBlacklist = []; -}); - - -/** - * Simple function applications - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeSimpleFuncsSingle', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>f</mi></mrow>', - '<identifier>f</identifier>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mi>y</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>,</mo><mi>y</mi>' + - '<mo>,</mo><mi>z</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<punctuated>' + - '<content>' + - '<punctuation>,</punctuation>' + - '<punctuation>,</punctuation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<punctuation>,</punctuation>' + - '<identifier>y</identifier>' + - '<punctuation>,</punctuation>' + - '<identifier>z</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><msup><mi>x</mi><mn>2</mn></msup>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><msub><mi>x</mi><mn>2</mn></msub>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><msubsup><mi>x</mi><mn>2</mn>' + - '<mn>1</mn></msubsup><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '<number>1</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mover><mi>x</mi><mn>2</mn></mover>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<overscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</overscore>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><munder><mi>x</mi><mn>2</mn></munder>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<underscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</underscore>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><munderover><mi>x</mi><mn>2</mn>' + - '<mn>1</mn></munderover><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<overscore>' + - '<children>' + - '<underscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</underscore>' + - '<number>1</number>' + - '</children>' + - '</overscore>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mfrac><mn>1</mn><mn>2</mn></mfrac>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<fraction>' + - '<children>' + - '<number>1</number>' + - '<number>2</number>' + - '</children>' + - '</fraction>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>+</mo><mi>y</mi>' + - '<mo>)</mo></mrow>', - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>'); -}); - - -/** - * Simple functions with surrounding operators. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeSimpleFuncsWithOps', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mn>1</mn><mo>+</mo><mi>f</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<number>1</number>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo>' + - '<mn>2</mn></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<number>2</number>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mn>1</mn><mo>+</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>+</mo><mn>2</mn></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<number>1</number>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<number>2</number>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>+</mo><mi>f</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo>' + - '<mo>b</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>+</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>+</mo><mo>b</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>=</mo><mi>f</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>=</mo>' + - '<mo>b</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>=</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>=</mo><mo>b</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Multiple simple functions. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeSimpleFuncsMulti', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo><mi>g</mi>' + - '<mo>(</mo><mi>x</mi><mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>g</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo><mi>g</mi>' + - '<mo>(</mo><mi>x</mi><mo>)</mo><mo>=</mo><mi>h</mi><mo>(</mo>' + - '<mi>x</mi><mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>g</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>h</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo><mi>g</mi>' + - '<mo>(</mo><mi>y</mi><mo>)</mo><mo>=</mo><mi>h</mi><mo>(</mo>' + - '<mi>x</mi><mi>y</mi><mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>g</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>h</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Nested simple functions. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeSimpleFuncsNested', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>g</mi><mo>(</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>g</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>h</mi><mo>(</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mi>g</mi><mo>(</mo><mi>y</mi><mo>)</mo><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>h</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>g</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>h</mi><mo>(</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>+</mo><mi>g</mi><mo>(</mo><mi>y</mi><mo>)</mo><mo>)</mo></mrow>', - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>h</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>g</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mi>P</mi><mo>[</mo><mi>x</mi><mo>=</mo><mn>2</mn><mo>]</mo>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>P</identifier>' + - '<fenced>' + - '<content>' + - '<fence>[</fence>' + - '<fence>]</fence>' + - '</content>' + - '<children>' + - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</relseq>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); -}); - - -/** - * Simple functions with explicit function application. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeSimpleFuncsExplicitApp', function() { - this.brief = true; - this.executeTreeTest( - '<mi>f</mi><mo>\u2061</mo><mo>(</mo><mi>x</mi><mo>+</mo><mi>y</mi>' + - '<mo>)</mo>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mi>f</mi><mo>\u2061</mo><mo>(</mo><mi>x</mi><mo>+</mo><mi>y</mi>' + - '<mo>)</mo><mo>+</mo><mi>f</mi><mo>(</mo><mi>x</mi><mo>+</mo>' + - '<mi>y</mi><mo>)</mo>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>f</identifier>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<msub><mi>f</mi><mn>1</mn></msub><mo>\u2061</mo><mo>(</mo><mi>x</mi>' + - '<mo>+</mo><mi>y</mi><mo>)</mo>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>f</identifier>' + - '<number>1</number>' + - '</children>' + - '</subscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<msup><msub><mi>f</mi><mn>n</mn></msub><mn>2</mn></msup>' + - '<mo>\u2061</mo><mo>(</mo><mi>x</mi><mo>+</mo><mi>y</mi><mo>)</mo>' + - '<mo>+</mo><msup><msub><mi>f</mi><mn>m</mn></msub><mn>2</mn></msup>' + - '<mo>(</mo><mi>x</mi><mo>+</mo><mi>y</mi><mo>)</mo>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>f</identifier>' + - '<identifier>n</identifier>' + - '</children>' + - '</subscript>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>f</identifier>' + - '<identifier>m</identifier>' + - '</children>' + - '</subscript>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</infixop>' + - '</children>' + - '</infixop>'); -}); - - -/** - * Prefix function applications - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsSingle', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mi>y</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><msup><mi>x</mi><mn>2</mn></msup>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><msub><mi>x</mi><mn>2</mn></msub>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><msubsup><mi>x</mi><mn>2</mn>' + - '<mn>1</mn></msubsup><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '<number>1</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mover><mi>x</mi><mn>2</mn></mover>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<overscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</overscore>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><munder><mi>x</mi><mn>2</mn></munder>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<underscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</underscore>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><munderover><mi>x</mi><mn>2</mn>' + - '<mn>1</mn></munderover><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<overscore>' + - '<children>' + - '<underscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</underscore>' + - '<number>1</number>' + - '</children>' + - '</overscore>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mfrac><mn>1</mn><mn>2</mn></mfrac>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<fraction>' + - '<children>' + - '<number>1</number>' + - '<number>2</number>' + - '</children>' + - '</fraction>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>+</mo><mi>y</mi>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); -}); - - -/** - * Prefix functions applications with surrounding operators. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsWithOps', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mn>1</mn><mo>+</mo><mi>sin</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<number>1</number>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo>' + - '<mn>2</mn></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<number>2</number>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mn>1</mn><mo>+</mo><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>+</mo><mn>2</mn></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<number>1</number>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<number>2</number>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>+</mo><mi>sin</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo>' + - '<mo>b</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>+</mo><mi>sin</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo><mo>+</mo><mo>b</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>=</mo><mi>sin</mi><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>=</mo>' + - '<mo>b</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>=</mo><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>=</mo><mo>b</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Multiple prefix function applications. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsMulti', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo><mi>cos</mi>' + - '<mo>(</mo><mi>x</mi><mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo><mi>cos</mi>' + - '<mo>(</mo><mi>x</mi><mo>)</mo><mo>=</mo><mi>tan</mi><mo>(</mo>' + - '<mi>x</mi><mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>tan</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>+</mo><mi>cos</mi>' + - '<mo>(</mo><mi>y</mi><mo>)</mo><mo>=</mo><mi>tan</mi><mo>(</mo>' + - '<mi>x</mi><mi>y</mi><mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>tan</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Prefix function applications with sub- and superscripts. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsScripts', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><msup><mi>sin</mi><mn>2</mn></msup><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><msub><mi>sin</mi><mn>1</mn></msub><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<subscript>' + - '<children>' + - '<function>sin</function>' + - '<number>1</number>' + - '</children>' + - '</subscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><msubsup><mi>sin</mi><mn>2</mn><mn>1</mn></msubsup><mo>(</mo>' + - '<mi>x</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '<number>1</number>' + - '</children>' + - '</superscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><msup><mi>sin</mi><mn>2</mn></msup><mo>(</mo><mi>x</mi>' + - '<mo>)</mo><mo>+</mo><msup><mi>cos</mi><mn>2</mn></msup><mo>(</mo>' + - '<mi>y</mi><mo>)</mo><mo>=</mo><mn>1</mn></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>cos</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<number>1</number>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Prefix function applications with unfenced arguments. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsUnfenced', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mi>y</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><msup><mi>x</mi><mn>2</mn></msup></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<superscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><msub><mi>x</mi><mn>2</mn></msub></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<subscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><msubsup><mi>x</mi><mn>2</mn><mn>1</mn>' + - '</msubsup></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '<number>1</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mover><mi>x</mi><mn>2</mn></mover></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<overscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</overscore>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><munder><mi>x</mi><mn>2</mn></munder></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<underscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</underscore>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><munderover><mi>x</mi><mn>2</mn><mn>1</mn>' + - '</munderover></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<overscore>' + - '<children>' + - '<underscore>' + - '<children>' + - '<identifier>x</identifier>' + - '<number>2</number>' + - '</children>' + - '</underscore>' + - '<number>1</number>' + - '</children>' + - '</overscore>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mfrac><mn>1</mn><mn>2</mn></mfrac></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fraction>' + - '<children>' + - '<number>1</number>' + - '<number>2</number>' + - '</children>' + - '</fraction>' + - '</children>' + - '</appl>'); -}); - - -/** - * Prefix function applications with unfenced arguments in an operator - * expression. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsUnfencedOps', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mn>1</mn><mo>+</mo><mi>sin</mi><mi>x</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<number>1</number>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mo>+</mo><mn>2</mn></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<number>2</number>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mn>1</mn><mo>+</mo><mi>sin</mi><mi>x</mi><mo>+</mo>' + - '<mn>2</mn></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<number>1</number>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<number>2</number>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>+</mo><mi>sin</mi><mi>x</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mo>+</mo><mo>b</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>+</mo><mi>sin</mi><mi>x</mi><mo>+</mo>' + - '<mo>b</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>=</mo><mi>sin</mi><mi>x</mi></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mo>=</mo><mo>b</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mo>a</mo><mo>=</mo><mi>sin</mi><mi>x</mi><mo>=</mo>' + - '<mo>b</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Multiple prefix function applications with unfenced arguments. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsMultiUnfenced', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mo>+</mo><mi>cos</mi><mi>x</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mo>+</mo><mi>cos</mi><mi>x</mi><mo>=</mo>' + - '<mi>tan</mi><mi>x</mi></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>tan</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mi>x</mi><mo>+</mo><mi>cos</mi><mi>y</mi><mo>=</mo>' + - '<mi>tan</mi><mi>x</mi><mi>y</mi></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<identifier>y</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>tan</function>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Prefix function applications with sub- and superscripts and unfenced - * arguments. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsScriptUnfenced', - function() { - this.brief = true; - this.executeTreeTest( - '<mrow><msup><mi>sin</mi><mn>2</mn></msup><mi>x</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><msub><mi>sin</mi><mn>1</mn></msub><mi>x</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<subscript>' + - '<children>' + - '<function>sin</function>' + - '<number>1</number>' + - '</children>' + - '</subscript>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><msubsup><mi>sin</mi><mn>2</mn><mn>1</mn></msubsup>' + - '<mi>x</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '<number>1</number>' + - '</children>' + - '</superscript>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><msup><mi>sin</mi><mn>2</mn></msup><mi>x</mi><mo>+</mo><msup>' + - '<mi>cos</mi><mn>2</mn></msup><mi>y</mi><mo>=</mo><mn>1</mn></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>cos</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<identifier>y</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<number>1</number>' + - '</children>' + - '</relseq>'); - this.executeTreeTest( - '<mrow><msubsup><msubsup><mi>sin</mi><mn>2</mn><mn>1</mn>' + - '</msubsup><mi>n</mi><mi>m</mi></msubsup><mi>x</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<superscript>' + - '<children>' + - '<subscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</subscript>' + - '<number>1</number>' + - '</children>' + - '</superscript>' + - '<identifier>n</identifier>' + - '</children>' + - '</subscript>' + - '<identifier>m</identifier>' + - '</children>' + - '</superscript>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>'); -}); - - -/** - * Prefix functions without arguments. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsNoArgs', function() { - this.brief = true; - this.executeTreeTest( - '<mi>sin</mi>', - '<function>sin</function>'); - - this.executeTreeTest( - '<msup><mi>sin</mi><mn>2</mn></msup>', - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>'); - - this.executeTreeTest( - '<msup><mi>sin</mi><mn>2</mn></msup><mo>+</mo><msup><mi>cos</mi>' + - '<mn>2</mn></msup>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<empty/>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>cos</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<empty/>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><msup><mi>sin</mi><mn>2</mn></msup><mo>+</mo>' + - '<msup><mi>cos</mi><mn>2</mn></msup><mo>=</mo><mn>1</mn></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>sin</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<empty/>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<superscript>' + - '<children>' + - '<function>cos</function>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '<empty/>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<number>1</number>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mi>sin</mi><mo>=</mo><mfrac><mn>1</mn>' + - '<mi>csc</mi></mfrac></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<empty/>' + - '</children>' + - '</appl>' + - '<fraction>' + - '<children>' + - '<number>1</number>' + - '<function>csc</function>' + - '</children>' + - '</fraction>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Nested prefix function applications, both with and without fenced arguments. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreePrefixFuncsNested', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><mi>log</mi><mi>cos</mi><mi>x</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>log</function>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>ln</mi><mo>' + - '(</mo><mi>sin</mi><mo>(</mo><mi>x</mi><mo>)</mo><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>ln</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>log</mi><mi>cos</mi><mi>x</mi><mo>=' + - '</mo><mi>ln</mi><mo>(</mo><mi>sin</mi><mo>' + - '(</mo><mi>x</mi><mo>)</mo><mo>)</mo></mrow>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>log</function>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>cos</function>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>ln</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>sin</function>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Variations of tables representing matrices, vectors, case statements, - * multiline equations and regular tables. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeTables', function() { - this.brief = false; - this.executeTreeTest( - '<mrow class="MJX-TeXAtom-ORD"><mi mathvariant="bold">A</mi>' + - '<mo>=</mo><mo>[</mo><mtable rowspacing="4pt" columnspacing="1em">' + - '<mtr><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr><mtr><mtd>' + - '<mn>2</mn></mtd><mtd><mn>3</mn></mtd></mtr></mtable><mo>]</mo>' + - '</mrow>', - '<relseq role="equality" id="16">=' + - '<content>' + - '<relation role="equality" id="1">=</relation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="bold" id="0">A</identifier>' + - '<matrix role="unknown" id="13">' + - '<content>' + - '<fence role="open" id="2">[</fence>' + - '<fence role="close" id="14">]</fence>' + - '</content>' + - '<children>' + - '<row role="matrix" id="7">' + - '<children>' + - '<cell role="matrix" id="4">' + - '<children>' + - '<number role="integer" font="normal" id="3">0</number>' + - '</children>' + - '</cell>' + - '<cell role="matrix" id="6">' + - '<children>' + - '<number role="integer" font="normal" id="5">1</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="matrix" id="12">' + - '<children>' + - '<cell role="matrix" id="9">' + - '<children>' + - '<number role="integer" font="normal" id="8">2</number>' + - '</children>' + - '</cell>' + - '<cell role="matrix" id="11">' + - '<children>' + - '<number role="integer" font="normal" id="10">3</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</matrix>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mo>[</mo><mtable rowspacing="4pt" columnspacing="1em"><mtr>' + - '<mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr><mtr><mtd>' + - '<mn>2</mn></mtd><mtd><mn>3</mn></mtd></mtr></mtable>' + - '<mo>]</mo>', - '<matrix role="unknown" id="11">' + - '<content>' + - '<fence role="open" id="0">[</fence>' + - '<fence role="close" id="12">]</fence>' + - '</content>' + - '<children>' + - '<row role="matrix" id="5">' + - '<children>' + - '<cell role="matrix" id="2">' + - '<children>' + - '<number role="integer" font="normal" id="1">0</number>' + - '</children>' + - '</cell>' + - '<cell role="matrix" id="4">' + - '<children>' + - '<number role="integer" font="normal" id="3">1</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="matrix" id="10">' + - '<children>' + - '<cell role="matrix" id="7">' + - '<children>' + - '<number role="integer" font="normal" id="6">2</number>' + - '</children>' + - '</cell>' + - '<cell role="matrix" id="9">' + - '<children>' + - '<number role="integer" font="normal" id="8">3</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</matrix>'); - - this.executeTreeTest( - '<mrow class="MJX-TeXAtom-ORD"><mi mathvariant="bold">V</mi>' + - '<mo>=</mo><mo>[</mo><mtable rowspacing="4pt" columnspacing="1em">' + - '<mtr><mtd><mn>1</mn></mtd></mtr><mtr><mtd><mn>2</mn></mtd></mtr>' + - '<mtr><mtd><mn>3</mn></mtd></mtr></mtable><mo>]</mo></mrow>', - '<relseq role="equality" id="15">=' + - '<content>' + - '<relation role="equality" id="1">=</relation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="bold" id="0">V</identifier>' + - '<vector role="unknown" id="12">' + - '<content>' + - '<fence role="open" id="2">[</fence>' + - '<fence role="close" id="13">]</fence>' + - '</content>' + - '<children>' + - '<line role="vector" id="5">' + - '<children>' + - '<number role="integer" font="normal" id="3">1</number>' + - '</children>' + - '</line>' + - '<line role="vector" id="8">' + - '<children>' + - '<number role="integer" font="normal" id="6">2</number>' + - '</children>' + - '</line>' + - '<line role="vector" id="11">' + - '<children>' + - '<number role="integer" font="normal" id="9">3</number>' + - '</children>' + - '</line>' + - '</children>' + - '</vector>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mo>[</mo><mtable rowspacing="4pt" columnspacing="1em">' + - '<mtr><mtd><mn>1</mn></mtd></mtr><mtr><mtd><mn>2</mn></mtd></mtr>' + - '<mtr><mtd><mn>3</mn></mtd></mtr></mtable><mo>]</mo>', - '<vector role="unknown" id="10">' + - '<content>' + - '<fence role="open" id="0">[</fence>' + - '<fence role="close" id="11">]</fence>' + - '</content>' + - '<children>' + - '<line role="vector" id="3">' + - '<children>' + - '<number role="integer" font="normal" id="1">1</number>' + - '</children>' + - '</line>' + - '<line role="vector" id="6">' + - '<children>' + - '<number role="integer" font="normal" id="4">2</number>' + - '</children>' + - '</line>' + - '<line role="vector" id="9">' + - '<children>' + - '<number role="integer" font="normal" id="7">3</number>' + - '</children>' + - '</line>' + - '</children>' + - '</vector>'); - - - this.executeTreeTest( - '<mrow><mo>{</mo><mtable><mtr><mtd><mi>a</mi></mtd><mtd>' + - '<mtext>often</mtext></mtd></mtr><mtr><mtd><mi>b</mi></mtd>' + - '<mtd><mtext>sometimes</mtext></mtd></mtr></mtable></mrow>', - '<cases role="unknown" id="11">' + - '<content>' + - '<punctuation role="openfence" id="0">{</punctuation>' + - '</content>' + - '<children>' + - '<row role="cases" id="5">' + - '<children>' + - '<cell role="cases" id="2">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="1">a</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="4">' + - '<children>' + - '<text role="unknown" id="3">often</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="cases" id="10">' + - '<children>' + - '<cell role="cases" id="7">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="6">b</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="9">' + - '<children>' + - '<text role="unknown" id="8">sometimes</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</cases>'); - - this.executeTreeTest( - '<mrow><mi mathvariant="bold">A</mi><mo>=</mo><mo>{</mo><mtable>' + - '<mtr><mtd><mi>a</mi></mtd><mtd><mtext>often</mtext></mtd></mtr>' + - '<mtr><mtd><mi>b</mi></mtd><mtd><mtext>sometimes</mtext></mtd></mtr>' + - '</mtable></mrow>', - '<relseq role="equality" id="14">=' + - '<content>' + - '<relation role="equality" id="1">=</relation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="bold" id="0">A</identifier>' + - '<cases role="unknown" id="13">' + - '<content>' + - '<punctuation role="openfence" id="2">{</punctuation>' + - '</content>' + - '<children>' + - '<row role="cases" id="7">' + - '<children>' + - '<cell role="cases" id="4">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="3">a</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="6">' + - '<children>' + - '<text role="unknown" id="5">often</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="cases" id="12">' + - '<children>' + - '<cell role="cases" id="9">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="8">b</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="11">' + - '<children>' + - '<text role="unknown" id="10">sometimes</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</cases>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mrow><mo>{</mo><mtable><mtr><mtd><mi>a</mi></mtd><mtd>' + - '<mtext>often</mtext></mtd></mtr><mtr><mtd><mi>b</mi></mtd><mtd>' + - '<mtext>sometimes</mtext></mtd></mtr></mtable><mo>.</mo></mrow>', - '<punctuated role="endpunct" id="13">' + - '<content>' + - '<punctuation role="fullstop" id="12">.</punctuation>' + - '</content>' + - '<children>' + - '<cases role="unknown" id="11">' + - '<content>' + - '<punctuation role="openfence" id="0">{</punctuation>' + - '</content>' + - '<children>' + - '<row role="cases" id="5">' + - '<children>' + - '<cell role="cases" id="2">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="1">a</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="4">' + - '<children>' + - '<text role="unknown" id="3">often</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="cases" id="10">' + - '<children>' + - '<cell role="cases" id="7">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="6">b</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="9">' + - '<children>' + - '<text role="unknown" id="8">sometimes</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</cases>' + - '<punctuation role="fullstop" id="12">.</punctuation>' + - '</children>' + - '</punctuated>'); - - this.executeTreeTest( - '<mrow><mo>{</mo><mtable><mtr><mtd><mi>a</mi></mtd>' + - '<mtd><mtext>often</mtext></mtd></mtr><mtr><mtd><mi>b</mi></mtd>' + - '<mtd><mtext>sometimes</mtext></mtd></mtr></mtable>' + - '<mo>,</mo><mi>b</mi><mo>,</mo><mi>c</mi><mo>.</mo></mrow>', - '<punctuated role="sequence" id="17">' + - '<content>' + - '<punctuation role="unknown" id="12">,</punctuation>' + - '<punctuation role="unknown" id="14">,</punctuation>' + - '<punctuation role="fullstop" id="16">.</punctuation>' + - '</content>' + - '<children>' + - '<cases role="unknown" id="11">' + - '<content>' + - '<punctuation role="openfence" id="0">{</punctuation>' + - '</content>' + - '<children>' + - '<row role="cases" id="5">' + - '<children>' + - '<cell role="cases" id="2">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="1">a</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="4">' + - '<children>' + - '<text role="unknown" id="3">often</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="cases" id="10">' + - '<children>' + - '<cell role="cases" id="7">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="6">b</identifier>' + - '</children>' + - '</cell>' + - '<cell role="cases" id="9">' + - '<children>' + - '<text role="unknown" id="8">sometimes</text>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</cases>' + - '<punctuation role="unknown" id="12">,</punctuation>' + - '<identifier role="latinletter" font="normal" id="13">b</identifier>' + - '<punctuation role="unknown" id="14">,</punctuation>' + - '<identifier role="latinletter" font="normal" id="15">c</identifier>' + - '<punctuation role="fullstop" id="16">.</punctuation>' + - '</children>' + - '</punctuated>'); - - this.executeTreeTest( - '<mrow><mo>{</mo><mtable><mtr><mtd><mi>a</mi><mo>,</mo>' + - '<mtext>often</mtext></mtd></mtr><mtr><mtd><mi>b</mi><mo>,</mo>' + - '<mtext>sometimes</mtext></mtd></mtr></mtable><mo>,</mo><mi>b</mi>' + - '<mo>,</mo><mi>c</mi><mo>.</mo></mrow>', - '<punctuated role="sequence" id="19">' + - '<content>' + - '<punctuation role="unknown" id="14">,</punctuation>' + - '<punctuation role="unknown" id="16">,</punctuation>' + - '<punctuation role="fullstop" id="18">.</punctuation>' + - '</content>' + - '<children>' + - '<cases role="unknown" id="13">' + - '<content>' + - '<punctuation role="openfence" id="0">{</punctuation>' + - '</content>' + - '<children>' + - '<line role="cases" id="6">' + - '<children>' + - '<punctuated role="sequence" id="4">' + - '<content>' + - '<punctuation role="unknown" id="2">,</punctuation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="normal" id="1">a</identifier>' + - '<punctuation role="unknown" id="2">,</punctuation>' + - '<text role="unknown" id="3">often</text>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</line>' + - '<line role="cases" id="12">' + - '<children>' + - '<punctuated role="sequence" id="10">' + - '<content>' + - '<punctuation role="unknown" id="8">,</punctuation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="normal" id="7">b</identifier>' + - '<punctuation role="unknown" id="8">,</punctuation>' + - '<text role="unknown" id="9">sometimes</text>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</line>' + - '</children>' + - '</cases>' + - '<punctuation role="unknown" id="14">,</punctuation>' + - '<identifier role="latinletter" font="normal" id="15">b</identifier>' + - '<punctuation role="unknown" id="16">,</punctuation>' + - '<identifier role="latinletter" font="normal" id="17">c</identifier>' + - '<punctuation role="fullstop" id="18">.</punctuation>' + - '</children>' + - '</punctuated>'); - - this.executeTreeTest( - '<mtable><mtr><mtd><mi>x</mi><maligngroup/><mo>=</mo><mn>4</mn>' + - '</mtd></mtr><mtr><mtd><mi>y</mi><maligngroup/><mo>=</mo><mn>2</mn>' + - '</mtd></mtr><mtr><mtd><mi>x</mi><mi>y</mi><maligngroup/><mo>=</mo>' + - '<mn>6</mn></mtd></mtr></mtable>', - '<multiline role="unknown" id="21">' + - '<children>' + - '<line role="multiline" id="5">' + - '<children>' + - '<relseq role="equality" id="3">=' + - '<content>' + - '<relation role="equality" id="1">=</relation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="normal" id="0">x</identifier>' + - '<number role="integer" font="normal" id="2">4</number>' + - '</children>' + - '</relseq>' + - '</children>' + - '</line>' + - '<line role="multiline" id="11">' + - '<children>' + - '<relseq role="equality" id="9">=' + - '<content>' + - '<relation role="equality" id="7">=</relation>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="normal" id="6">y</identifier>' + - '<number role="integer" font="normal" id="8">2</number>' + - '</children>' + - '</relseq>' + - '</children>' + - '</line>' + - '<line role="multiline" id="20">' + - '<children>' + - '<relseq role="equality" id="18">=' + - '<content>' + - '<relation role="equality" id="14">=</relation>' + - '</content>' + - '<children>' + - '<infixop role="implicit" id="17">\u2062' + - '<content>' + - '<operator role="multiplication" id="16">\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="normal" id="12">x</identifier>' + - '<identifier role="latinletter" font="normal" id="13">y</identifier>' + - '</children>' + - '</infixop>' + - '<number role="integer" font="normal" id="15">6</number>' + - '</children>' + - '</relseq>' + - '</children>' + - '</line>' + - '</children>' + - '</multiline>'); - - this.executeTreeTest( - '<mtable><mtr><mtd><mi>x</mi></mtd><mtd><mo>=</mo></mtd><mtd><mn>4</mn>' + - '</mtd></mtr><mtr><mtd><mi>y</mi></mtd><mtd><mo>=</mo></mtd><mtd>' + - '<mn>2</mn></mtd></mtr><mtr><mtd><mi>x</mi><mi>y</mi></mtd><mtd>' + - '<mo>=</mo></mtd><mtd><mn>6</mn></mtd></mtr></mtable>', - '<table role="unknown" id="24">' + - '<children>' + - '<row role="table" id="6">' + - '<children>' + - '<cell role="table" id="1">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="0">x</identifier>' + - '</children>' + - '</cell>' + - '<cell role="table" id="3">' + - '<children>' + - '<relation role="equality" id="2">=</relation>' + - '</children>' + - '</cell>' + - '<cell role="table" id="5">' + - '<children>' + - '<number role="integer" font="normal" id="4">4</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="table" id="13">' + - '<children>' + - '<cell role="table" id="8">' + - '<children>' + - '<identifier role="latinletter" font="normal" id="7">y</identifier>' + - '</children>' + - '</cell>' + - '<cell role="table" id="10">' + - '<children>' + - '<relation role="equality" id="9">=</relation>' + - '</children>' + - '</cell>' + - '<cell role="table" id="12">' + - '<children>' + - '<number role="integer" font="normal" id="11">2</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '<row role="table" id="23">' + - '<children>' + - '<cell role="table" id="18">' + - '<children>' + - '<infixop role="implicit" id="17">\u2062' + - '<content>' + - '<operator role="multiplication" id="16">\u2062</operator>' + - '</content>' + - '<children>' + - '<identifier role="latinletter" font="normal" id="14">x</identifier>' + - '<identifier role="latinletter" font="normal" id="15">y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</cell>' + - '<cell role="table" id="20">' + - '<children>' + - '<relation role="equality" id="19">=</relation>' + - '</children>' + - '</cell>' + - '<cell role="table" id="22">' + - '<children>' + - '<number role="integer" font="normal" id="21">6</number>' + - '</children>' + - '</cell>' + - '</children>' + - '</row>' + - '</children>' + - '</table>'); -}); - - -TEST_F('CvoxSemanticTreeUnitTest', 'StreeLimitFunctions', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><munder><mi>lim</mi><mrow><mi>x</mi><mo>\u2192</mo>' + - '<mi>\u221E</mi></mrow></munder><mo>(</mo><mi>x</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>lim</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><mi>a</mi><mo>+</mo><munder><mi>lim</mi><mrow><mi>x</mi>' + - '<mo>\u2192</mo><mi>\u221E</mi></mrow></munder><mo>(</mo><mi>x</mi>' + - '<mo>)</mo><mo>+</mo><mi>b</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>lim</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><msup><munder><mi>lim</mi><mrow><mi>x</mi><mo>\u2192</mo>' + - '<mi>\u221E</mi></mrow></munder><mo>+</mo></msup><mo>(</mo><mi>x</mi>' + - '<mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limupper>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>lim</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<operator>+</operator>' + - '</children>' + - '</limupper>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><munderover><mi>lim</mi><mo>\u2015</mo><mrow><mi>x</mi>' + - '<mo>\u2192</mo><mi>\u221E</mi></mrow></munderover><mo>(</mo>' + - '<mi>x</mi><mo>)</mo></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limboth>' + - '<children>' + - '<function>lim</function>' + - '<punctuation>\u2015</punctuation>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limboth>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mrow><munder><mi>liminf</mi><mrow><mi>x</mi><mo>\u2192</mo>' + - '<mi>\u221E</mi></mrow></munder><mo>(</mo><mi>x</mi><mo>)</mo>' + - '<mo>+</mo><munder><mi>limsup</mi><mrow><mi>y</mi><mo>\u2192</mo>' + - '<mi>\u221E</mi></mrow></munder><mo>(</mo><mi>y</mi><mo>)</mo></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>liminf</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>limsup</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<fenced>' + - '<content>' + - '<fence>(</fence>' + - '<fence>)</fence>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '</children>' + - '</fenced>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><mi>a</mi><mo>+</mo><munder><mi>lim</mi><mrow><mi>x</mi>' + - '<mo>\u2192</mo><mi>\u221E</mi></mrow></munder><mi>x</mi><mo>+</mo>' + - '<mi>b</mi></mrow>', - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>a</identifier>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>lim</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<identifier>x</identifier>' + - '</children>' + - '</appl>' + - '<identifier>b</identifier>' + - '</children>' + - '</infixop>'); - - this.executeTreeTest( - '<mrow><munder><mi>lim</mi><mrow><mi>x</mi><mo>\u2192</mo><mi>\u221E</mi>' + - '</mrow></munder><mi>lim</mi><munder><mrow><mi>y</mi><mo>\u2192</mo>' + - '<mi>\u221E</mi></mrow></munder><mi>x</mi><mi>y</mi></mrow>', - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<limlower>' + - '<children>' + - '<function>lim</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>lim</function>' + - '<infixop>\u2062' + - '<content>' + - '<operator>\u2062</operator>' + - '</content>' + - '<children>' + - '<underscore>' + - '<children>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>y</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</underscore>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '</children>' + - '</appl>' + - '</children>' + - '</appl>'); - - this.executeTreeTest( - '<mi>liminf</mi>', - '<function>liminf</function>'); - - this.executeTreeTest( - '<munder><mi>lim</mi><mrow><mi>x</mi><mo>\u2192</mo><mi>\u221E</mi>' + - '</mrow></munder>', - '<limlower>' + - '<children>' + - '<function>lim</function>' + - '<relseq>\u2192' + - '<content>' + - '<relation>\u2192</relation>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>'); - - this.executeTreeTest( - '<mi>liminf</mi><mo>+</mo><mi>limsup</mi><mo>=</mo><mi>lim</mi>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>liminf</function>' + - '<empty/>' + - '</children>' + - '</appl>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>limsup</function>' + - '<empty/>' + - '</children>' + - '</appl>' + - '</children>' + - '</infixop>' + - '<appl>' + - '<content>' + - '<punctuation>\u2061</punctuation>' + - '</content>' + - '<children>' + - '<function>lim</function>' + - '<empty/>' + - '</children>' + - '</appl>' + - '</children>' + - '</relseq>'); -}); - - -/** - * Variations of big operators. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeBigOps', function() { - this.brief = true; - this.executeTreeTest( - '<mrow><munderover><mi>\u2211</mi><mrow><mi>n</mi><mo>=</mo><mn>0</mn>' + - '</mrow><mi>\u221E</mi></munderover><msup><mi>n</mi><mn>2</mn>' + - '</msup></mrow>', - '<bigop>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u2211</largeop>' + - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>n</identifier>' + - '<number>0</number>' + - '</children>' + - '</relseq>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</limboth>' + - '<superscript>' + - '<children>' + - '<identifier>n</identifier>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</bigop>'); - - this.executeTreeTest( - '<mrow><munderover><mi>\u2211</mi><mrow><mi>n</mi><mo>=</mo><mn>0</mn>' + - '</mrow><mi>\u221E</mi></munderover><munderover><mi>\u2211</mi><mrow>' + - '<mi>m</mi><mo>=</mo><mn>0</mn></mrow><mi>\u221E</mi></munderover><msup>' + - '<mi>n</mi><mn>m</mn></msup></mrow>', - '<bigop>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u2211</largeop>' + - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>n</identifier>' + - '<number>0</number>' + - '</children>' + - '</relseq>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</limboth>' + - '<bigop>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u2211</largeop>' + - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>m</identifier>' + - '<number>0</number>' + - '</children>' + - '</relseq>' + - '<identifier>\u221E</identifier>' + - '</children>' + - '</limboth>' + - '<superscript>' + - '<children>' + - '<identifier>n</identifier>' + - '<identifier>m</identifier>' + - '</children>' + - '</superscript>' + - '</children>' + - '</bigop>' + - '</children>' + - '</bigop>'); - - this.executeTreeTest( - '<mrow><munder><mi>\u2211</mi><mrow><mi>n</mi><mo>=</mo><mn>0</mn></mrow>' + - '</munder><msup><mi>n</mi><mn>2</mn></msup></mrow>', - '<bigop>' + - '<children>' + - '<limlower>' + - '<children>' + - '<largeop>\u2211</largeop>' + - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<identifier>n</identifier>' + - '<number>0</number>' + - '</children>' + - '</relseq>' + - '</children>' + - '</limlower>' + - '<superscript>' + - '<children>' + - '<identifier>n</identifier>' + - '<number>2</number>' + - '</children>' + - '</superscript>' + - '</children>' + - '</bigop>'); -}); - - - -/** - * Variations of integrals. - */ -TEST_F('CvoxSemanticTreeUnitTest', 'StreeIntegrals', function() { - this.brief = true; - this.executeTreeTest( - '<mi>\u222B</mi>', - '<largeop>\u222B</largeop>'); - - this.executeTreeTest( - '<mi>\u222B</mi><mi>dx</mi>', - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<empty/>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>'); - - this.executeTreeTest( - '<mrow><mi>\u222B</mi><mi>x</mi><mi>dx</mi></mrow>', - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<identifier>x</identifier>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>'); - - this.executeTreeTest( - '<mrow><mi>\u222B</mi><mi>x</mi><mi>d</mi><mi>x</mi></mrow>', - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<identifier>x</identifier>' + - '<punctuated>' + - '<content>' + - '<punctuation>\u2063</punctuation>' + - '</content>' + - '<children>' + - '<identifier>d</identifier>' + - '<punctuation>\u2063</punctuation>' + - '<identifier>x</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</integral>'); - - this.executeTreeTest( - '<mrow><mi>\u222B</mi><mi>x</mi><mo>+' + - '</mo><mi>y</mi><mi>d</mi><mi>x</mi></mrow>', - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<identifier>x</identifier>' + - '<identifier>y</identifier>' + - '</children>' + - '</infixop>' + - '<punctuated>' + - '<content>' + - '<punctuation>\u2063</punctuation>' + - '</content>' + - '<children>' + - '<identifier>d</identifier>' + - '<punctuation>\u2063</punctuation>' + - '<identifier>x</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</integral>'); - - this.executeTreeTest( - '<munderover><mi>\u222B</mi><mn>0</mn><mn>10</mn></munderover>', - '<limboth>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<number>0</number>' + - '<number>10</number>' + - '</children>' + - '</limboth>'); - - this.executeTreeTest( - '<munder><mi>\u222B</mi><mn>X</mn></munder>', - '<limlower>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<identifier>X</identifier>' + - '</children>' + - '</limlower>'); - - this.executeTreeTest( - '<munderover><mi>\u222B</mi><mn>0</mn><mn>10</mn></munderover><mi>x</mi>' + - '<mi>d</mi><mi>x</mi>', - '<integral>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<number>0</number>' + - '<number>10</number>' + - '</children>' + - '</limboth>' + - '<identifier>x</identifier>' + - '<punctuated>' + - '<content>' + - '<punctuation>\u2063</punctuation>' + - '</content>' + - '<children>' + - '<identifier>d</identifier>' + - '<punctuation>\u2063</punctuation>' + - '<identifier>x</identifier>' + - '</children>' + - '</punctuated>' + - '</children>' + - '</integral>'); - - this.executeTreeTest( - '<munder><mi>\u222B</mi><mn>X</mn></munder><mi>x</mi><mi>dx</mi>', - '<integral>' + - '<children>' + - '<limlower>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<identifier>X</identifier>' + - '</children>' + - '</limlower>' + - '<identifier>x</identifier>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>'); - - this.executeTreeTest( - '<munderover><mi>\u222B</mi><mn>0</mn><mn>10</mn></munderover><mi>x</mi>' + - '<mi>dx</mi><mo>+</mo><munderover><mi>\u222B</mi><mn>10</mn><mn>20</mn>' + - '</munderover><mi>x</mi><mi>dx</mi><mo>=</mo><munderover><mi>\u222B</mi>' + - '<mn>0</mn><mn>20</mn></munderover><mi>x</mi><mi>dx</mi>', - '<relseq>=' + - '<content>' + - '<relation>=</relation>' + - '</content>' + - '<children>' + - '<infixop>+' + - '<content>' + - '<operator>+</operator>' + - '</content>' + - '<children>' + - '<integral>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<number>0</number>' + - '<number>10</number>' + - '</children>' + - '</limboth>' + - '<identifier>x</identifier>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>' + - '<integral>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<number>10</number>' + - '<number>20</number>' + - '</children>' + - '</limboth>' + - '<identifier>x</identifier>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>' + - '</children>' + - '</infixop>' + - '<integral>' + - '<children>' + - '<limboth>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<number>0</number>' + - '<number>20</number>' + - '</children>' + - '</limboth>' + - '<identifier>x</identifier>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>' + - '</children>' + - '</relseq>'); - - this.executeTreeTest( - '<mi>\u222B</mi><mi>\u222B</mi><mi>\u222B</mi>' + - '<mi>dx</mi><mi>dy</mi><mi>dz</mi>', - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<integral>' + - '<children>' + - '<largeop>\u222B</largeop>' + - '<empty/>' + - '<identifier>dx</identifier>' + - '</children>' + - '</integral>' + - '<identifier>dy</identifier>' + - '</children>' + - '</integral>' + - '<identifier>dz</identifier>' + - '</children>' + - '</integral>'); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_util.js deleted file mode 100644 index 8d996874372..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_semantic_util.js +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Utility functions for semantic tree computations. - */ - -goog.provide('cvox.SemanticUtil'); - - -/** - * @constructor - */ -cvox.SemanticUtil = function() { }; - - -/** - * Merges keys of objects into an array. - * @param {...Object<string>} objects Optional objects. - * @return {Array<string>} Array of all keys of the objects. - */ -cvox.SemanticUtil.objectsToKeys = function(objects) { - objects = Array.prototype.slice.call(arguments, 0); - var keys = []; - return keys.concat.apply(keys, objects.map(Object.keys)); -}; - - -/** - * Merges values of objects into an array. - * @param {...Object<string>} objects Optional objects. - * @return {Array<string>} Array of all values of the objects. - */ -cvox.SemanticUtil.objectsToValues = function(objects) { - objects = Array.prototype.slice.call(arguments, 0); - var result = []; - var collectValues = function(obj) { - for (var key in obj) { - result.push(obj[key]); - } - }; - objects.forEach(collectValues); - return result; -}; - - -/** - * Transforms a unicode character into numeric representation. Returns null if - * the input string is not a valid unicode character. - * @param {string} unicode Character. - * @return {?number} The decimal representation if it exists. - */ -cvox.SemanticUtil.unicodeToNumber = function(unicode) { - if (!unicode || unicode.length > 2) { - return null; - } - // Treating surrogate pairs. - if (unicode.length == 2) { - var hi = unicode.charCodeAt(0); - var low = unicode.charCodeAt(1); - if (0xD800 <= hi && hi <= 0xDBFF && !isNaN(low)) { - return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000; - } - return null; - } - return unicode.charCodeAt(0); -}; - - -/** - * Transforms a numberic representation of a unicode character into its - * corresponding string. - * @param {number} number Unicode point. - * @return {string} The string representation. - */ -cvox.SemanticUtil.numberToUnicode = function(number) { - if (number >= 0x10000) { - var hi = (number - 0x10000) / 0x0400 + 0xD800; - var lo = (number - 0x10000) % 0x0400 + 0xDC00; - return String.fromCharCode(hi, lo); - } - return String.fromCharCode(number); -}; - - -/** - * Returns the tagname of an element node in upper case. - * @param {Element} node The node. - * @return {string} The node's tagname. - */ -cvox.SemanticUtil.tagName = function(node) { - return node.tagName.toUpperCase(); -}; - - -/** - * List of MathML Tags that are to be ignored. - * @type {Array<string>} - * @const - */ -cvox.SemanticUtil.IGNORETAGS = [ - 'MERROR', 'MPHANTOM', 'MSPACE', 'MACTION', 'MALIGNGROUP', 'MALIGNMARK', - 'MACTION' -]; - - -/** - * List of MathML Tags to be ignore if they have no children. - * @type {Array<string>} - * @const - */ -cvox.SemanticUtil.EMPTYTAGS = ['MATH', 'MROW', 'MPADDED', 'MSTYLE']; - - -/** - * Removes elements from a list of MathML nodes that are either to be ignored or - * ignored if they have empty children. - * Observe that this is currently not recursive, i.e. will not take care of - * pathological cases, where content is hidden in incorrectly used tags! - * @param {Array<Element>} nodes The node list to be cleaned. - * @return {Array<Element>} The cleansed list. - */ -cvox.SemanticUtil.purgeNodes = function(nodes) { - var nodeArray = []; - for (var i = 0, node; node = nodes[i]; i++) { - var tagName = cvox.SemanticUtil.tagName(node); - if (cvox.SemanticUtil.IGNORETAGS.indexOf(tagName) != -1) continue; - if (cvox.SemanticUtil.EMPTYTAGS.indexOf(tagName) != -1 && - node.childNodes.length == 0) - continue; - nodeArray.push(node); - } - return nodeArray; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/math_util.js deleted file mode 100644 index 4de27ae3913..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/math_util.js +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview DOM utility functions to aid in math expressions navigation. - */ - -goog.provide('cvox.MathUtil'); - -goog.require('cvox.ChromeVox'); -goog.require('cvox.XpathUtil'); - - -/** - * Checks if a node is in a given class of MathML nodes. - * @private - * @param {!Node} node The node to test. - * @param {Array<string>} tags List of tag names. - * @return {boolean} True if node has a tag name included in tags. - */ -cvox.MathUtil.isMathmlNodeOfClass_ = function(node, tags) { - return tags.indexOf(node.tagName.toUpperCase()) != -1; -}; - - -/** - * Checks if a node is in a given class of MathJax nodes. - * @private - * @param {!Node} node The node to test. - * @param {Array<string>} tags List of tag names. - * @return {boolean} True if node has a tag name included in tags. - */ -cvox.MathUtil.isMathjaxNodeOfClass_ = function(node, tags) { - if (node.tagName == 'SPAN') { - var classes = node.className.split(' '); - return classes.some(function(x) - {return tags.indexOf(x.toUpperCase()) != -1;}); - } - return false; -}; - - -/** - * Checks if a node is an element node that belongs to a given class - * of MathML or MathJax nodes. - * @private - * @param {!Node} node The node to test. - * @param {Array<string>} tags List of tag names. - * @return {boolean} True if node has a tag name included in tags. - */ -cvox.MathUtil.isMathNodeOfClass_ = function(node, tags) { - return (node.nodeType == Node.ELEMENT_NODE && - (cvox.MathUtil.isMathmlNodeOfClass_(node, tags) || - cvox.MathUtil.isMathjaxNodeOfClass_(node, tags))); -}; - - -/** - * Array of MathML Token Elements. - * @type {!Array<string>} - */ -cvox.MathUtil.TOKEN_LIST = ['MI', 'MN', 'MO', 'MTEXT', 'MSPACE', 'MS']; - - -/** - * Checks if an element of a math expression is a Token Element. - * Token elements are the following: - * <mi> identifier. - * <mn> number. - * <mo> operator, fence, or separator. - * <mtext> text. - * <mspace> space. - * <ms> string literal. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a token. - */ -cvox.MathUtil.isToken = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, cvox.MathUtil.TOKEN_LIST); -}; - - -/** - * Array of MathML Layout Schemata. - * @type {!Array<string>} - */ -cvox.MathUtil.LAYOUT_LIST = ['MROW', 'MFRAC', 'MSQRT', 'MROOT', 'MSTYLE', - 'MERROR', 'MPADDED', 'MPHANTOM', 'MFENCED', - 'MENCLOSE']; - - -/** - * Checks if an element of a math expression is a Layout Schema. - * Layout elements are the following: - * <mrow> group any number of sub-expressions horizontally - * <mfrac> form a fraction from two sub-expressions - * <msqrt> form a square root (radical without an index) - * <mroot> form a radical with specified index - * <mstyle> style change - * <merror> enclose a syntax error message from a preprocessor - * <mpadded> adjust space around content - * <mphantom> make content invisible but preserve its size - * <mfenced> surround content with a pair of fences - * <menclose> enclose content with a stretching symbol such as a long - * division sign. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a layout schema. - */ -cvox.MathUtil.isLayout = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, cvox.MathUtil.LAYOUT_LIST); -}; - - -/** - * Array of MathML Script Schemata. - * @type {!Array<string>} - */ -cvox.MathUtil.SCRIPT_LIST = ['MSUB', 'MSUP', 'MSUBSUP', 'MUNDER', 'MOVER', - 'MUNDEROVER', 'MMULTISCRIPTS', 'MPRESCRIPTS']; - - -/** - * Checks if an element of a math expression is a Script Schema. - * Script elements are the following: - * <msub> attach a subscript to a base. - * <msup> attach a superscript to a base. - * <msubsup> attach a subscript-superscript pair to a base. - * <munder> attach an underscript to a base. - * <mover> attach an overscript to a base. - * <munderover> attach an underscript-overscript pair to a base. - * <mmultiscripts> attach prescripts and tensor indices to a base. - * Prescripts are optional. - * <mprescripts> two elements prescripts of mmultiscripts. Only makes sense - * in that environment (although not illegal outside)! Two - * arguments mandatory (can be <none/>). - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a script schema. - */ -cvox.MathUtil.isScript = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, cvox.MathUtil.SCRIPT_LIST); -}; - - -/** - * Array of MathML Table and Matrix tokens. - * @type {!Array<string>} - */ -cvox.MathUtil.TABLES_LIST = ['MTABLE', 'MLABELEDTR', 'MTR', 'MTD', - 'MALIGNGROUP', 'MALIGNMARK']; - - -/** - * Checks if an element of a math expression is a Tables Schema. - * Tables elements are the following: - * <mtable> table or matrix. - * <mlabeledtr> row in a table or matrix with a label or equation number. - * <mtr> row in a table or matrix. - * <mtd> one entry in a table or matrix. - * <maligngroup> and - * <malignmark> alignment markers. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a tables schema. - */ -cvox.MathUtil.isTables = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, cvox.MathUtil.TABLES_LIST); -}; - - -/** - * Array of MathML Elementary Layout Schemata. - * @type {!Array<string>} - */ -cvox.MathUtil.ELEMENTARY_LIST = ['MSTACK', 'MLONGDIV', 'MSGROUP', 'MSROW', - 'MSCARRIES', 'MSCARRY', 'MSLINE']; - - -/** - * Checks if an element of a math expression is a Elementary Schema. - * Elementary elements are the following: - * <mstack> columns of aligned characters. - * <mlongdiv> similar to msgroup, with the addition of a divisor and result. - * <msgroup> a group of rows in an mstack that are shifted by similar amounts. - * <msrow> a row in an mstack. - * <mscarries> row in an mstack that whose contents represent carries - * or borrows. - * <mscarry> one entry in an mscarries. - * <msline> horizontal line inside of mstack. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a elementary schema. - */ -cvox.MathUtil.isElementary = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, - cvox.MathUtil.ELEMENTARY_LIST); -}; - - -/** - * Array of all valid tags in a MathML expression. - * This is a union of all other token lists. - * @type {!Array<string>} - */ -cvox.MathUtil.MATHML_TAG_LIST = [cvox.MathUtil.TOKEN_LIST, - cvox.MathUtil.LAYOUT_LIST, - cvox.MathUtil.SCRIPT_LIST, - cvox.MathUtil.TABLES_LIST, - cvox.MathUtil.ELEMENTARY_LIST].reduce( - function(x, y) { return x.concat(y); }); - - -/** - * Checks if a node is valid element of a MathML expression. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element has a valid MathML tag. - */ -cvox.MathUtil.isMathmlTag = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, - cvox.MathUtil.MATHML_TAG_LIST); -}; - - -/** - * Array of MathML Whitespace and Alignment tokens. - * These are elements that can occur in the other token lists. - * @type {!Array<string>} - */ -cvox.MathUtil.WHITESPACE_LIST = ['MSROW', 'MROW', 'MSPACE', - 'MPHANTOM', 'MPADDED']; - - -/** - * Checks if an element of a math expression is whitespace or an - * alignment marker. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a whitespace node. - */ -cvox.MathUtil.isWhitespace = function(element) { - return cvox.MathUtil.isMathNodeOfClass_(element, - cvox.MathUtil.WHITESPACE_LIST); -}; - - -/** - * Checks if an element of a math expression is a legal mathml markup element - * but not a whitespace or an alignment marker. - * @param {!Node} element The element of the math expression. - * @return {boolean} True if element is a non-whitespace node. - */ -cvox.MathUtil.isNotWhitespace = function(element) { - return (cvox.MathUtil.isMathmlTag(element) && - !cvox.MathUtil.isWhitespace(element)); -}; - - -/** - * Computes the union of two arrays (not in a strictly set theoretical sense - * as all duplicate elements in either array still remain as duplicates!). - * @param {Array} a An array. - * @param {Array} b Another array. - * @return {Array} Union of a and b. - */ -cvox.MathUtil.union = function(a, b) { - return a.concat(b.filter(function(x) {return a.indexOf(x) < 0;})); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/media_widget.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/media_widget.js deleted file mode 100644 index 7b6dd05ed89..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/media_widget.js +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.ChromeVoxHTMLMediaWidget'); - -/** - * @fileoverview Gives the user spoken feedback as they interact with the HTML5 - * media widgets (<video> and <audio>) + makes the widget keyboard accessible. - * - */ - -/** - * A class containing the information needed to speak - * a media element to the user. - * - * @constructor - * @param {Element} mediaElem The media widget element. - * @param {cvox.TtsInterface} tts The TTS object from ChromeVox. - */ -cvox.ChromeVoxHTMLMediaWidget = function(mediaElem, tts){ - var self = this; - this.mediaElem_ = mediaElem; - this.mediaTts_ = tts; - - this.keyListener_ = function(evt) { - self.eventHandler_(evt); - } - this.blurListener_ = function(evt) { - self.shutdown(); - } - - this.mediaElem_.addEventListener('keydown', this.keyListener_, false); - this.mediaElem_.addEventListener('keyup', this.keyListener_, false); - this.mediaElem_.addEventListener('blur', this.blurListener_, false); -}; - -/** - * Removes the key listeners for the media widget. - */ -cvox.ChromeVoxHTMLMediaWidget.prototype.shutdown = function() { - this.mediaElem_.removeEventListener('blur', this.blurListener_, false); - this.mediaElem_.removeEventListener('keydown', this.keyListener_, false); - this.mediaElem_.removeEventListener('keyup', this.keyListener_, false); -}; - -cvox.ChromeVoxHTMLMediaWidget.prototype.jumpToTime_ = function(targetTime) { - if (targetTime < 0) { - targetTime = 0; - } - if (targetTime > this.mediaElem_.duration) { - targetTime = this.mediaElem_.duration; - } - this.mediaElem_.currentTime = targetTime; -}; - -cvox.ChromeVoxHTMLMediaWidget.prototype.setVolume_ = function(targetVolume) { - if (targetVolume < 0) { - targetVolume = 0; - } - if (targetVolume > 1.0) { - targetVolume = 1.0; - } - this.mediaElem_.volume = targetVolume; -}; - -/** - * Adds basic keyboard handlers to the media widget. - */ -cvox.ChromeVoxHTMLMediaWidget.prototype.eventHandler_ = function(evt) { - if (evt.type == 'keydown') { - // Space/Enter for play/pause toggle. - if ((evt.keyCode == 13) || (evt.keyCode == 32)) { - if (this.mediaElem_.paused){ - this.mediaElem_.play(); - } else { - this.mediaElem_.pause(); - } - } else if (evt.keyCode == 39) { // Right - FF - this.jumpToTime_( - this.mediaElem_.currentTime + (this.mediaElem_.duration/10)); - } else if (evt.keyCode == 37) { // Left - REW - this.jumpToTime_( - this.mediaElem_.currentTime - (this.mediaElem_.duration/10)); - } else if (evt.keyCode == 38) { // Up - Vol. Up - this.setVolume_(this.mediaElem_.volume + .1); - } else if (evt.keyCode == 40) { // Down - Vol. Down - this.setVolume_(this.mediaElem_.volume - .1); - } - } -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/memoize.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/memoize.js deleted file mode 100644 index 0ac3f6386ac..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/memoize.js +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Provides a system for memoizing computations applied to - * DOM nodes within the same call stack. - * - * To make a function memoizable - suppose you have a function - * isAccessible that takes a node and returns a boolean: - * - * function isAccessible(node) { - * return expensiveComputation(node); - * } - * - * Make it memoizable like this: - * - * function isAccessible(node) { - * return cvox.Memoize.memoize(computeIsAccessible_, 'isAccessible', node); - * } - * - * function computeIsAccessible_(node) { - * return expensiveComputation(node); - * } - * - * To take advantage of memoization, you need to wrap a sequence of - * computations in a call to memoize.scope() - memoization is only - * enabled while in that scope, and all cached data is thrown away at - * the end. You should use this only when you're sure the computation - * being memoized will not change within the scope. - * - * cvox.Memoize.scope(function() { - * console.log(isAccessible(document.body)); - * }); - * - */ - - -goog.provide('cvox.Memoize'); - - -/** - * Create the namespace. - * @constructor - */ -cvox.Memoize = function() { -}; - -/** - * The cache: a map from string function name to a WeakMap from DOM node - * to function result. This variable is null when we're out of scope, and it's - * a map from string to WeakMap to result when we're in scope. - * - * @type {?Object<WeakMap<Node, *> >} - * @private - */ -cvox.Memoize.nodeMap_ = null; - -/** - * Keeps track of how many nested times scope() has been called. - * @type {number} - * @private - */ -cvox.Memoize.scopeCount_ = 0; - - -/** - * Enables memoization within the scope of the given function. You should - * ensure that the DOM is not modified within this scope. - * - * It's safe to nest calls to scope. The nested calls have - * no effect, only the outermost one. - * - * @param {Function} functionScope The function to call with memoization - * enabled. - * @return {*} The value returned by |functionScope|. - */ -cvox.Memoize.scope = function(functionScope) { - var result; - try { - cvox.Memoize.scopeCount_++; - if (cvox.Memoize.scopeCount_ == 1) { - cvox.Memoize.nodeMap_ = {}; - } - result = functionScope(); - } finally { - cvox.Memoize.scopeCount_--; - if (cvox.Memoize.scopeCount_ == 0) { - cvox.Memoize.nodeMap_ = null; - } - } - return result; -}; - -/** - * Memoizes the result of a function call, so if you call this again - * with the same exact parameters and memoization is currently enabled - * (via a call to scope()), the second time the cached result - * will just be returned directly. - * - * @param {Function} functionClosure The function to call and cache the - * result of. - * @param {string} functionName The name of the function you're calling. - * This string is used to store and retrieve the cached result, so - * it should be unique. If the function to be memoized takes simple - * arguments in addition to a DOM node, you can incorporate those - * arguments into the function name. - * @param {Node} node The DOM node that should be passed as the argument - * to the function. - * @return {*} The return value of |functionClosure|. - */ -cvox.Memoize.memoize = function(functionClosure, functionName, node) { - if (cvox.Memoize.nodeMap_ && - cvox.Memoize.nodeMap_[functionName] === undefined) { - cvox.Memoize.nodeMap_[functionName] = new WeakMap(); - } - - // If we're not in scope, just call the function directly. - if (!cvox.Memoize.nodeMap_) { - return functionClosure(node); - } - - var result = cvox.Memoize.nodeMap_[functionName].get(node); - if (result === undefined) { - result = functionClosure(node); - if (result === undefined) { - throw 'A memoized function cannot return undefined.'; - } - cvox.Memoize.nodeMap_[functionName].set(node, result); - } - - return result; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/msgs.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/msgs.js deleted file mode 100644 index 8a9628e1d46..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/msgs.js +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -/** - * @fileoverview Defines methods related to retrieving translated messages. - */ - -goog.provide('Msgs'); - -/** - * @constructor - */ -Msgs = function() {}; - -/** - * The namespace for all Chromevox messages. - * @type {string} - * @const - * @private - */ -Msgs.NAMESPACE_ = 'chromevox_'; - -/** - * Dictionary of locale names. - * @type {Object<string>} - * @private - */ -Msgs.localeNameDict_ = null; - -/** - * Return the current locale. - * @return {string} The locale. - */ -Msgs.getLocale = function() { - return chrome.i18n.getMessage('locale'); -}; - -/** - * Returns the message with the given message id from the ChromeVox namespace. - * - * If we can't find a message, throw an exception. This allows us to catch - * typos early. - * - * @param {string} messageId The id. - * @param {Array<string>=} opt_subs Substitution strings. - * @return {string} The localized message. - */ -Msgs.getMsg = function(messageId, opt_subs) { - var message = Msgs.Untranslated[messageId.toUpperCase()]; - if (message !== undefined) - return message; - message = chrome.i18n.getMessage( - Msgs.NAMESPACE_ + messageId, opt_subs); - if (message == undefined || message == '') { - throw new Error('Invalid ChromeVox message id: ' + messageId); - } - return message; -}; - -/** - * Processes an HTML DOM, replacing text content with translated text messages - * on elements marked up for translation. Elements whose class attributes - * contain the 'i18n' class name are expected to also have an msgid - * attribute. The value of the msgid attributes are looked up as message - * IDs and the resulting text is used as the text content of the elements. - * - * @param {Node} root The root node where the translation should be performed. - */ -Msgs.addTranslatedMessagesToDom = function(root) { - var elts = root.querySelectorAll('.i18n'); - for (var i = 0; i < elts.length; i++) { - var msgid = elts[i].getAttribute('msgid'); - if (!msgid) { - throw new Error('Element has no msgid attribute: ' + elts[i]); - } - elts[i].textContent = this.getMsg(msgid); - elts[i].classList.add('i18n-processed'); - } -}; - -/** - * Retuns a number formatted correctly. - * - * @param {number} num The number. - * @return {string} The number in the correct locale. - */ -Msgs.getNumber = function(num) { - return '' + num; -}; - -/** - * Gets a localized display name for a locale. - * NOTE: Only a subset of locale identifiers are supported. See the - * |CHROMEVOX_LOCALE_DICT| message. - * @param {string} locale On the form |ll| or |ll_CC|, where |ll| is - * the language code and |CC| the country code. - * @return {string} The display name. - */ -Msgs.getLocaleDisplayName = function(locale) { - if (!Msgs.localeNameDict_) { - Msgs.localeNameDict_ = /** @type {!Object<string>} */( - JSON.parse(this.getMsg('locale_dict'))); - } - var name = Msgs.localeNameDict_[locale]; - if (!name) { - throw Error('Unsupported locale identifier: ' + locale); - } - return name; -}; - -/** - * Strings that are displayed in the user interface but don't need - * be translated. - * @type {Object<string>} - */ -Msgs.Untranslated = { - /** The unchecked state for a checkbox in braille. */ - CHECKBOX_UNCHECKED_STATE_BRL: '( )', - /** The checked state for a checkbox in braille. */ - CHECKBOX_CHECKED_STATE_BRL: '(x)', - /** The unselected state for a radio button in braille. */ - RADIO_UNSELECTED_STATE_BRL: '( )', - /** The selected state for a radio button in braille. */ - RADIO_SELECTED_STATE_BRL: '(x)', - /** Brailled after a menu if the menu has a submenu. */ - ARIA_HAS_SUBMENU_BRL: '->', - /** Describes an element with the ARIA role option. */ - ROLE_OPTION: ' ', - /** Braille of element with the ARIA role option. */ - ROLE_OPTION_BRL: ' ', - /** Braille of element with the ARIA attribute aria-checked=true. */ - ARIA_CHECKED_TRUE_BRL: '(x)', - /** Braille of element with the ARIA attribute aria-checked=false. */ - ARIA_CHECKED_FALSE_BRL: '( )', - /** Braille of element with the ARIA attribute aria-checked=mixed. */ - ARIA_CHECKED_MIXED_BRL: '(-)', - /** Braille of element with the ARIA attribute aria-disabled=true. */ - ARIA_DISABLED_TRUE_BRL: 'xx', - /** Braille of element with the ARIA attribute aria-expanded=true. */ - ARIA_EXPANDED_TRUE_BRL: '-', - /** Braille of element with the ARIA attribute aria-expanded=false. */ - ARIA_EXPANDED_FALSE_BRL: '+', - /** Braille of element with the ARIA attribute aria-invalid=true. */ - ARIA_INVALID_TRUE_BRL: '!', - /** Braille of element with the ARIA attribute aria-pressed=true. */ - ARIA_PRESSED_TRUE_BRL: '=', - /** Braille of element with the ARIA attribute aria-pressed=false. */ - ARIA_PRESSED_FALSE_BRL: ' ', - /** Braille of element with the ARIA attribute aria-pressed=mixed. */ - ARIA_PRESSED_MIXED_BRL: '-', - /** Braille of element with the ARIA attribute aria-selected=true. */ - ARIA_SELECTED_TRUE_BRL: '(x)', - /** Braille of element with the ARIA attribute aria-selected=false. */ - ARIA_SELECTED_FALSE_BRL: '( )', - /** Brailled after a menu if it has a submenu. */ - HAS_SUBMENU_BRL: '->', - /** Brailled to describe a <time> tag. */ - TAG_TIME_BRL: ' ', -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/nav_description.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/nav_description.js deleted file mode 100644 index a158b5746c8..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/nav_description.js +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A simple container object for the description of a - * navigation from one object to another. - * - */ - - -goog.provide('cvox.NavDescription'); - -goog.require('cvox.AbstractTts'); -goog.require('cvox.ChromeVox'); -goog.require('cvox.CursorSelection'); -goog.require('cvox.QueueMode'); - -/** - * A class representing the description of navigation from one object to - * another. - * @param {{context: (undefined|string), - * text: (string), - * userValue: (undefined|string), - * annotation: (undefined|string), - * earcons: (undefined|Array<cvox.Earcon>), - * personality: (undefined|Object), - * hint: (undefined|string), - category: (undefined|string)}} kwargs The arguments for this - * description. - * context The context, for example descriptions of objects - * that were crossed into, like "Toolbar" or "Menu Bar" or "List with - * 5 items". This is all spoken with an annotation voice. - * text The text of the object itself, including text from - * titles, labels, etc. - * userValue The text that the user has entered. - * annotation The role and state of the object. - * earcons A list of the earcon ids to play along - * with the spoken description of this object. - * personality Optional TTS personality to use for the text. - * hint Optional Hint text (.e.g. aria-describedby). - * category Optional category (for speech queueing behavior). - * @constructor - */ -cvox.NavDescription = function(kwargs) { - this.context = kwargs.context ? kwargs.context : ''; - this.text = kwargs.text ? kwargs.text : ''; - this.userValue = kwargs.userValue ? kwargs.userValue : ''; - this.annotation = kwargs.annotation ? kwargs.annotation : ''; - this.earcons = kwargs.earcons ? kwargs.earcons : []; - this.personality = kwargs.personality; - this.hint = kwargs.hint ? kwargs.hint : ''; - this.category = kwargs.category ? kwargs.category : null; -}; - - -/** - * @return {boolean} true if this description is empty. - */ -cvox.NavDescription.prototype.isEmpty = function() { - return (this.context.length == 0 && - this.earcons.length == 0 && - this.text.length == 0 && - this.userValue.length == 0 && - this.annotation.length == 0); -}; - - -/** - * @return {string} A string representation of this object. - */ -cvox.NavDescription.prototype.toString = function() { - return 'NavDescription(context="' + this.context + '" ' + - ' text="' + this.text + '" ' + - ' userValue="' + this.userValue + '" ' + - ' annotation="' + this.annotation + - (this.category ? '" category="' + this.category + '")' : '') + - '")'; -}; - - -/** - * Modifies the earcon to play along with the spoken description of the object. - * @param {cvox.Earcon} earconId An earcon id to be pushed on to the list of - * earcon ids to play along with the spoken description of this object. - */ -cvox.NavDescription.prototype.pushEarcon = function(earconId) { - this.earcons.push(earconId); -}; - - -/** - * Speak this nav description with the given queue mode. - * @param {cvox.QueueMode=} queueMode The queue mode. - * @param {function()=} opt_startCallback Function called when this - * starts speaking. - * @param {function()=} opt_endCallback Function called when this ends speaking. - */ -cvox.NavDescription.prototype.speak = function( - queueMode, opt_startCallback, opt_endCallback) { - /** - * Return a deep copy of PERSONALITY_ANNOTATION for modifying. - * @return {Object} The newly created properties object. - */ - function makeAnnotationProps() { - var properties = {}; - var src = cvox.AbstractTts.PERSONALITY_ANNOTATION; - for (var key in src) { - properties[key] = src[key]; - } - return properties; - } - - var speakArgs = new Array(); - if (this.context) { - speakArgs.push([this.context, queueMode, makeAnnotationProps()]); - queueMode = cvox.QueueMode.QUEUE; - } - - speakArgs.push([this.text, - queueMode, - this.personality ? this.personality : {}]); - queueMode = cvox.QueueMode.QUEUE; - - if (this.userValue) { - speakArgs.push([this.userValue, queueMode, {}]); - } - - if (this.annotation) { - speakArgs.push([this.annotation, queueMode, makeAnnotationProps()]); - } - - if (this.hint) { - speakArgs.push([this.hint, queueMode, makeAnnotationProps()]); - } - - var length = speakArgs.length; - for (var i = 0; i < length; i++) { - if (i == 0 && opt_startCallback) { - speakArgs[i][2]['startCallback'] = opt_startCallback; - } - if (i == length - 1 && opt_endCallback) { - speakArgs[i][2]['endCallback'] = opt_endCallback; - } - if (this.category) { - speakArgs[i][2]['category'] = this.category; - } - cvox.ChromeVox.tts.speak.apply(cvox.ChromeVox.tts, speakArgs[i]); - } -}; - - -/** - * Compares two NavDescriptions. - * @param {cvox.NavDescription} that A NavDescription. - * @return {boolean} True if equal. - */ -cvox.NavDescription.prototype.equals = function(that) { - return this.context == that.context && - this.text == that.text && - this.userValue == that.userValue && - this.annotation == that.annotation; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/nav_math_description.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/nav_math_description.js deleted file mode 100644 index e76c6886804..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/nav_math_description.js +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A subclass of the navigation description container - * specialising on math objects. - * - */ - - -goog.provide('cvox.NavMathDescription'); - -goog.require('cvox.NavDescription'); - - -/** - * Class specialising navigation descriptions for mathematics. - * @param {{context: (undefined|string), - * text: (string), - * userValue: (undefined|string), - * annotation: (undefined|string), - * earcons: (undefined|Array<cvox.Earcon>), - * personality: (undefined|Object), - * hint: (undefined|string), - * category: (undefined|string), - * domain: (undefined|string), - * style: (undefined|string)}} kwargs The arguments for - * the specialised math navigationdescription. See arguments of nav - * description plus the following: - * domain Domain for translation. - * style Style for translation. - * @constructor - * @extends {cvox.NavDescription} - */ -cvox.NavMathDescription = function(kwargs) { - goog.base(this, kwargs); - - var newPersonality = this.personality ? this.personality : {}; - var mathDescr = new Object(); - - mathDescr['domain'] = kwargs.domain ? kwargs.domain : ''; - // TODO (sorge) Collate and document styles in an enum structure. - mathDescr['style'] = kwargs.style ? kwargs.style : ''; - newPersonality['math'] = mathDescr; - this.personality = newPersonality; -}; -goog.inherits(cvox.NavMathDescription, cvox.NavDescription); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/node_state.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/node_state.js deleted file mode 100644 index d7f97fec382..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/node_state.js +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview The cvox.NodeState typedef. - */ - -goog.provide('cvox.NodeState'); -goog.provide('cvox.NodeStateUtil'); - -goog.require('Msgs'); - -/** - * Holds the state of a node. It is an Array or Arrays of strings and numbers. - * Each sub array is in the format: - * [state, opt_arg, opt_arg, ...]. These sub arrays map directly to a - * cvox.ChromeVox.getMsg() call. For example [list_position, 3, 5] maps to - * getMsg('list_position', [3, 5]); - * - * @typedef {!Array<!Array<string|number>>} - */ -cvox.NodeState; - -/** - * Returns a localized, readable string with the NodeState. - * - * NOTE(deboer): Once AriaUtil and DomUtil are using NodeState exclusively, this - * function can be moved into DescriptionUtil, removing the cvox.ChromeVox - * dependency here. - * - * @param {cvox.NodeState} state The node state. - * @return {string} The readable state string. - */ -cvox.NodeStateUtil.expand = function(state) { - try { - return state.map(function(s) { - if (s.length < 1) { - throw new Error('cvox.NodeState must have at least one entry'); - } - var args = s.slice(1).map(function(a) { - if (typeof a == 'number') { - return Msgs.getNumber(a); - } - return a; - }); - return Msgs.getMsg(/** @type {string} */ (s[0]), args); - }).join(' '); - } catch (e) { - throw new Error('error: ' + e + ' state: ' + state); - } -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/page_selection.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/page_selection.js deleted file mode 100644 index 7a46db20669..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/page_selection.js +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A class representing a DOM selection conveyed through - * CursorSelection idioms. - * A PageSelection is just a DOM selection. The class itself manages a single - * CursorSelection that surrounds a fragment on the page. It also provides an - * extend operation to either grow or shrink the selection given a - * CursorSelection. The class handles correctly moving the internal - * CursorSelection and providing immediate access to a full description of the - * selection at any time. - */ - -goog.provide('cvox.PageSelection'); - -goog.require('cvox.AbstractEarcons'); -goog.require('cvox.CursorSelection'); -goog.require('cvox.NavDescription'); - -/** - * @constructor - * @param {!cvox.CursorSelection} sel The initial selection. - */ -cvox.PageSelection = function(sel) { - this.sel_ = sel.clone(); - this.sel_.select(); - this.wasBegin_ = true; -}; - - -/** - * Gets a description for the DOM selection during the course of navigation. - * @param {cvox.AbstractShifter} navShifter Used to obtain walker-based - * descriptions. - * @param {!cvox.CursorSelection} prevSel Previous CursorSelection in - * navigation. - * @param {!cvox.CursorSelection} curSel Current CursorSelection in navigation. - * @return {Array<cvox.NavDescription>} The new description. - */ -cvox.PageSelection.prototype.getDescription = - function(navShifter, prevSel, curSel) { - var desc = []; - if (this.sel_.isReversed() != curSel.isReversed()) { - // A shrinking selection. - desc = navShifter.getDescription(curSel, prevSel); - desc[0].annotation = Msgs.getMsg('describe_unselected'); - desc[0].pushEarcon(cvox.Earcon.SELECTION_REVERSE); - } else { - // A growing selection. - desc = navShifter.getDescription(prevSel, curSel); - desc[0].annotation = Msgs.getMsg('describe_selected'); - desc[0].pushEarcon(cvox.Earcon.SELECTION); - if (!this.wasBegin_ && this.sel_.absEquals(curSel.clone().normalize())) { - // A selection has inverted across the start cursor. Describe it. - var prevDesc = navShifter.getDescription(curSel, prevSel); - prevDesc[0].annotation = - Msgs.getMsg('describe_unselected'); - prevDesc[0].pushEarcon(cvox.Earcon.SELECTION_REVERSE); - prevDesc[0].pushEarcon(cvox.Earcon.WRAP); - desc = prevDesc.concat(desc); - } - } - return desc; -}; - - -/** - * Gets a full description for the entire DOM selection. - * Use this description when you want to describe the entire selection - * represented by this instance. - * - * @return {Array<cvox.NavDescription>} The new description. - */ -cvox.PageSelection.prototype.getFullDescription = function() { - return [new cvox.NavDescription( - {text: window.getSelection().toString(), - context: Msgs.getMsg('selection_is')})]; -}; - - -/** - * Extends this selection. - * @param {!cvox.CursorSelection} sel Extend DOM selection to the selection. - * @return {boolean} True if the extension occurred, false if the PageSelection - * was reset to sel. - */ -cvox.PageSelection.prototype.extend = function(sel) { - if (!this.sel_.directedBefore(sel)) { - // Do not allow for crossed selections. This restarts a page selection that - // has been collapsed. This occurs when two CursorSelection's point away - // from one another. - this.sel_ = sel.clone(); - } else { - // Otherwise, it is assumed that the CursorSelection's are in directed - // document order. The CursorSelection's are either pointing in the same - // direction or towards one another. In the first case, shrink/extend this - // PageSelection to the end of "sel". In the second case, shrink/extend this - // PageSelection to the start of "sel". - this.sel_.end = this.sel_.isReversed() == sel.isReversed() ? - sel.end.clone() : sel.start.clone(); - } - this.sel_.select(); - this.wasBegin_ = false; - return !this.sel_.absEquals(sel); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs deleted file mode 100644 index 413b9292415..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/page_selection_test.unitjs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxPageSelectionUnitTest() {} - -CvoxPageSelectionUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.CursorSelection', - 'cvox.PageSelection', - ], - - /** @override */ - setUp: function() { - this.loadDoc(function() {/*! - <p id='p1'>The quick</p> - <a id='a1' href='#'>brown fox</a> - <h1 id='h1'>jumped over</h1> - */}); - this.pSel = cvox.CursorSelection.fromNode($('p1')); - this.pSel.start.index = 0; - this.pSel.end.index = 1; - this.aSel = cvox.CursorSelection.fromNode($('a1')); - this.aSel.start.index = 0; - this.aSel.end.index = 1; - this.hSel = cvox.CursorSelection.fromNode($('h1')); - this.hSel.start.index = 0; - this.hSel.end.index = 1; - }, - - /** - * Asserts a selection. - * @param {string} str The expected contents of selection. - * @private - */ - assertSelection_: function(str) { - assertEquals(str, window.getSelection().toString()); - } -}; - -TEST_F('CvoxPageSelectionUnitTest', 'BasicExtend', function() { - var pageSel = new cvox.PageSelection(this.pSel); - pageSel.extend(this.hSel); - this.assertSelection_('The quick\n\nbrown fox\njumped over'); - this.hSel.end.node = this.hSel.end.node.firstChild; - this.hSel.end.index = 6; - pageSel.extend(this.hSel); - this.assertSelection_('The quick\n\nbrown fox\njumped'); -}); - - -/** Tests a reverse extension. */ -TEST_F('CvoxPageSelectionUnitTest', 'ReverseExtend', function() { - var pageSel = new cvox.PageSelection(this.pSel); - this.assertSelection_('The quick'); - pageSel.extend(this.hSel); - this.assertSelection_('The quick\n\nbrown fox\njumped over'); - pageSel.extend(this.aSel.setReversed(true)); - this.assertSelection_('The quick\n\nbrown fox'); - this.pSel.setReversed(true); - pageSel.extend(this.pSel); - this.assertSelection_('The quick'); - this.pSel.start.node = this.pSel.start.node.firstChild; - this.pSel.start.index = 3; - pageSel.extend(this.pSel); - this.assertSelection_('The'); -}); - - -/** Tests all possible configurations of PageSelection's and extending - * CursorSelection's. - */ -TEST_F('CvoxPageSelectionUnitTest', 'ExtendDirections', function() { - // A normal page selection, with a normal extension. - var pageSel = new cvox.PageSelection(this.aSel); - assertTrue(pageSel.extend(this.hSel)); - - // A normal page selection, with a reversed extension. - assertTrue(pageSel.extend(this.hSel.setReversed(true))); - - // A reversed page selection, with a normal cursor selection. - var rPageSel = new cvox.PageSelection(this.aSel.setReversed(true)); - assertTrue(rPageSel.extend(this.pSel)); - - // A reversed page selection, with a reversed extension. - assertTrue(rPageSel.extend(this.pSel.setReversed(true))); -}); - - -/** Tests degenerate extensions. */ -TEST_F('CvoxPageSelectionUnitTest', 'DegenerateExtensions', function() { - var pageSel = new cvox.PageSelection(this.aSel); - - // A normal page selection, with a normal extension not in document order. - assertFalse(pageSel.extend(this.pSel)); - - // And, this causes a reset of page selection. - assertTrue(pageSel.sel_.equals(this.pSel)); - - // Reinitialize. - pageSel = new cvox.PageSelection(this.aSel.setReversed(false)); - - // A normal page selection, with a reversed extension not in document order. - assertFalse(pageSel.extend(this.pSel.setReversed(true))); - - // And, again, it causes reset of page selection. -assertTrue(pageSel.sel_.equals(this.pSel)); - - // Reverse page selections. - var rPageSel = new cvox.PageSelection(this.aSel.setReversed(true)); - - // A reversed page selection, with a normal extension not in document order. - assertFalse(rPageSel.extend(this.hSel)); - assertTrue(rPageSel.sel_.equals(this.hSel)); - - // A reversed page selection, with a reversed extension not in document order. - rPageSel = new cvox.PageSelection(this.aSel.setReversed(true)); - assertFalse(rPageSel.extend(this.hSel.setReversed(true))); - assertTrue(rPageSel.sel_.equals(this.hSel)); - - // And, finally, try extending to oneself in either direction. - pageSel = new cvox.PageSelection(this.aSel.setReversed(false)); - - // A normal page selection, with an extension to itself. - assertFalse(pageSel.extend(this.aSel.setReversed(false))); - assertFalse(pageSel.extend(this.aSel.setReversed(true))); - - // A reversed page selection, with an extension to itself. - var rPageSel = new cvox.PageSelection(this.aSel.setReversed(true)); - assertFalse(rPageSel.extend(this.aSel.setReversed(true))); - assertFalse(rPageSel.extend(this.aSel.setReversed(false))); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/platform_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/platform_util.js deleted file mode 100644 index c1c67ed3807..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/platform_util.js +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Utilities for working with platforms. - */ - - -goog.provide('cvox.PlatformFilter'); -goog.provide('cvox.PlatformUtil'); - -goog.require('cvox.ChromeVox'); - -/** - * @enum - */ -cvox.PlatformFilter = { - NONE: 0, - WINDOWS: 1, - MAC: 2, - LINUX: 4, - WML: 7, - CHROMEOS: 8, - ANDROID: 16 -}; - - -/** - *Checks whether the given filter matches the current platform. An undefined - * filter always matches the current platform. - * @param {undefined|cvox.PlatformFilter|number} filter The filter. - * @return {boolean} Whether the filter matches the current platform. - */ -cvox.PlatformUtil.matchesPlatform = function(filter) { - var uA = navigator.userAgent; - if (filter == undefined) { - return true; - } else if (uA.indexOf('Android') != -1) { - return (filter & cvox.PlatformFilter.ANDROID) != 0; - } else if (uA.indexOf('Win') != -1) { - return (filter & cvox.PlatformFilter.WINDOWS) != 0; - } else if (uA.indexOf('Mac') != -1) { - return (filter & cvox.PlatformFilter.MAC) != 0; - } else if (uA.indexOf('Linux') != -1) { - return (filter & cvox.PlatformFilter.LINUX) != 0; - } else if (uA.indexOf('CrOS') != -1) { - return (filter & cvox.PlatformFilter.CHROMEOS) != 0; - } - return false; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/selection_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/selection_util.js deleted file mode 100644 index 3057d2efc9b..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/selection_util.js +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A collection of JavaScript utilities used to improve selection - * at different granularities. - */ - - -goog.provide('cvox.SelectionUtil'); - -goog.require('cvox.DomUtil'); -goog.require('cvox.XpathUtil'); - -/** - * Utilities for improving selection. - * @constructor - */ -cvox.SelectionUtil = function() {}; - -/** - * Cleans up a paragraph selection acquired by extending forward. - * In this context, a paragraph selection is 'clean' when the focus - * node (the end of the selection) is not on a text node. - * @param {Selection} sel The paragraph-length selection. - * @return {boolean} True if the selection has been cleaned. - * False if the selection cannot be cleaned without invalid extension. - */ -cvox.SelectionUtil.cleanUpParagraphForward = function(sel) { - var expand = true; - - // nodeType:3 == TEXT_NODE - while (sel.focusNode.nodeType == 3) { - // Ending with a text node, which is incorrect. Keep extending forward. - var fnode = sel.focusNode; - var foffset = sel.focusOffset; - - sel.modify('extend', 'forward', 'sentence'); - if ((fnode == sel.focusNode) && (foffset == sel.focusOffset)) { - // Nothing more to be done, cannot extend forward further. - return false; - } - } - - return true; -}; - -/** - * Cleans up a paragraph selection acquired by extending backward. - * In this context, a paragraph selection is 'clean' when the focus - * node (the end of the selection) is not on a text node. - * @param {Selection} sel The paragraph-length selection. - * @return {boolean} True if the selection has been cleaned. - * False if the selection cannot be cleaned without invalid extension. - */ -cvox.SelectionUtil.cleanUpParagraphBack = function(sel) { - var expand = true; - - var fnode; - var foffset; - - // nodeType:3 == TEXT_NODE - while (sel.focusNode.nodeType == 3) { - // Ending with a text node, which is incorrect. Keep extending backward. - fnode = sel.focusNode; - foffset = sel.focusOffset; - - sel.modify('extend', 'backward', 'sentence'); - - if ((fnode == sel.focusNode) && (foffset == sel.focusOffset)) { - // Nothing more to be done, cannot extend backward further. - return true; - } - } - - return true; -}; - -/** - * Cleans up a sentence selection by extending forward. - * In this context, a sentence selection is 'clean' when the focus - * node (the end of the selection) is either: - * - not on a text node - * - on a text node that ends with a period or a space - * @param {Selection} sel The sentence-length selection. - * @return {boolean} True if the selection has been cleaned. - * False if the selection cannot be cleaned without invalid extension. - */ -cvox.SelectionUtil.cleanUpSentence = function(sel) { - var expand = true; - var lastSelection; - var lastSelectionOffset; - - while (expand) { - - // nodeType:3 == TEXT_NODE - if (sel.focusNode.nodeType == 3) { - // The focus node is of type text, check end for period - - var fnode = sel.focusNode; - var foffset = sel.focusOffset; - - if (sel.rangeCount > 0 && sel.getRangeAt(0).endOffset > 0) { - if (fnode.substringData(sel.getRangeAt(0).endOffset - 1, 1) == '.') { - // Text node ends with period. - return true; - } else if (fnode.substringData(sel.getRangeAt(0).endOffset - 1, 1) == - ' ') { - // Text node ends with space. - return true; - } else { - // Text node does not end with period or space. Extend forward. - sel.modify('extend', 'forward', 'sentence'); - - if ((fnode == sel.focusNode) && (foffset == sel.focusOffset)) { - // Nothing more to be done, cannot extend forward any further. - return false; - } - } - } else { - return true; - } - } else { - // Focus node is not text node, no further cleaning required. - return true; - } - } - - return true; -}; - -/** - * Finds the starting position (height from top and left width) of a - * selection in a document. - * @param {Selection} sel The selection. - * @return {Array} The coordinates [top, left] of the selection. - */ -cvox.SelectionUtil.findSelPosition = function(sel) { - if (sel.rangeCount == 0) { - return [0, 0]; - } - - var clientRect = sel.getRangeAt(0).getBoundingClientRect(); - - if (!clientRect) { - return [0, 0]; - } - - var top = window.pageYOffset + clientRect.top; - var left = window.pageXOffset + clientRect.left; - return [top, left]; -}; - -/** - * Calculates the horizontal and vertical position of a node - * @param {Node} targetNode The node. - * @return {Array} The coordinates [top, left] of the node. - */ -cvox.SelectionUtil.findTopLeftPosition = function(targetNode) { - var left = 0; - var top = 0; - var obj = targetNode; - - if (obj.offsetParent) { - left = obj.offsetLeft; - top = obj.offsetTop; - obj = obj.offsetParent; - - while (obj !== null) { - left += obj.offsetLeft; - top += obj.offsetTop; - obj = obj.offsetParent; - } - } - - return [top, left]; -}; - - -/** - * Checks the contents of a selection for meaningful content. - * @param {Selection} sel The selection. - * @return {boolean} True if the selection is valid. False if the selection - * contains only whitespace or is an empty string. - */ -cvox.SelectionUtil.isSelectionValid = function(sel) { - var regExpWhiteSpace = new RegExp(/^\s+$/); - return (! ((regExpWhiteSpace.test(sel.toString())) || - (sel.toString() == ''))); -}; - -/** - * Checks the contents of a range for meaningful content. - * @param {Range} range The range. - * @return {boolean} True if the range is valid. False if the range - * contains only whitespace or is an empty string. - */ -cvox.SelectionUtil.isRangeValid = function(range) { - var text = range.cloneContents().textContent; - var regExpWhiteSpace = new RegExp(/^\s+$/); - return (! ((regExpWhiteSpace.test(text)) || - (text == ''))); -}; - -/** - * Returns absolute top and left positions of an element. - * - * @param {!Node} node The element for which to compute the position. - * @return {Array<number>} Index 0 is the left; index 1 is the top. - * @private - */ -cvox.SelectionUtil.findPos_ = function(node) { - var curLeft = 0; - var curTop = 0; - if (node.offsetParent) { - do { - curLeft += node.offsetLeft; - curTop += node.offsetTop; - } while (node = node.offsetParent); - } - return [curLeft, curTop]; -}; - -/** - * Scrolls node in its parent node such the given node is visible. - * @param {Node} focusNode The node. - */ -cvox.SelectionUtil.scrollElementsToView = function(focusNode) { - // First, walk up the DOM until we find a node with a bounding rectangle. - while (focusNode && !focusNode.getBoundingClientRect) { - focusNode = focusNode.parentElement; - } - if (!focusNode) { - return; - } - - // Walk up the DOM, ensuring each element is visible inside its parent. - var node = focusNode; - var parentNode = node.parentElement; - while (node != document.body && parentNode) { - node.scrollTop = node.offsetTop; - node.scrollLeft = node.offsetLeft; - node = parentNode; - parentNode = node.parentElement; - } - - // Center the active element on the page once we know it's visible. - var pos = cvox.SelectionUtil.findPos_(focusNode); - window.scrollTo(pos[0] - window.innerWidth / 2, - pos[1] - window.innerHeight / 2); -}; - -/** - * Scrolls the selection into view if it is out of view in the current window. - * Inspired by workaround for already-on-screen elements @ - * http:// - * www.performantdesign.com/2009/08/26/scrollintoview-but-only-if-out-of-view/ - * @param {Selection} sel The selection to be scrolled into view. - */ -cvox.SelectionUtil.scrollToSelection = function(sel) { - if (sel.rangeCount == 0) { - return; - } - - // First, scroll all parent elements into view. Later, move the body - // which works slightly differently. - - cvox.SelectionUtil.scrollElementsToView(sel.focusNode); - - var pos = cvox.SelectionUtil.findSelPosition(sel); - var top = pos[0]; - var left = pos[1]; - - var scrolledVertically = window.pageYOffset || - document.documentElement.scrollTop || - document.body.scrollTop; - var pageHeight = window.innerHeight || - document.documentElement.clientHeight || document.body.clientHeight; - var pageWidth = window.innerWidth || - document.documentElement.innerWidth || document.body.clientWidth; - - if (left < pageWidth) { - left = 0; - } - - // window.scroll puts specified pixel in upper left of window - if ((scrolledVertically + pageHeight) < top) { - // Align with bottom of page - var diff = top - pageHeight; - window.scroll(left, diff + 100); - } else if (top < scrolledVertically) { - // Align with top of page - window.scroll(left, top - 100); - } -}; - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Determine whether a node's text content is entirely whitespace. - * - * Throughout, whitespace is defined as one of the characters - * "\t" TAB \u0009 - * "\n" LF \u000A - * "\r" CR \u000D - * " " SPC \u0020 - * - * This does not use Javascript's "\s" because that includes non-breaking - * spaces (and also some other characters). - * - * @param {Node} node A node implementing the |CharacterData| interface (i.e., - * a |Text|, |Comment|, or |CDATASection| node. - * @return {boolean} True if all of the text content of |node| is whitespace, - * otherwise false. - */ -cvox.SelectionUtil.isAllWs = function(node) { - // Use ECMA-262 Edition 3 String and RegExp features - return !(/[^\t\n\r ]/.test(node.data)); -}; - - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Determine if a node should be ignored by the iterator functions. - * - * @param {Node} node An object implementing the DOM1 |Node| interface. - * @return {boolean} True if the node is: - * 1) A |Text| node that is all whitespace - * 2) A |Comment| node - * and otherwise false. - */ - -cvox.SelectionUtil.isIgnorable = function(node) { - return (node.nodeType == 8) || // A comment node - ((node.nodeType == 3) && - cvox.SelectionUtil.isAllWs(node)); // a text node, all ws -}; - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Version of |previousSibling| that skips nodes that are entirely - * whitespace or comments. (Normally |previousSibling| is a property - * of all DOM nodes that gives the sibling node, the node that is - * a child of the same parent, that occurs immediately before the - * reference node.) - * - * @param {Node} sib The reference node. - * @return {Node} Either: - * 1) The closest previous sibling to |sib| that is not - * ignorable according to |isIgnorable|, or - * 2) null if no such node exists. - */ -cvox.SelectionUtil.nodeBefore = function(sib) { - while ((sib = sib.previousSibling)) { - if (!cvox.SelectionUtil.isIgnorable(sib)) { - return sib; - } - } - return null; -}; - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Version of |nextSibling| that skips nodes that are entirely - * whitespace or comments. - * - * @param {Node} sib The reference node. - * @return {Node} Either: - * 1) The closest next sibling to |sib| that is not - * ignorable according to |isIgnorable|, or - * 2) null if no such node exists. - */ -cvox.SelectionUtil.nodeAfter = function(sib) { - while ((sib = sib.nextSibling)) { - if (!cvox.SelectionUtil.isIgnorable(sib)) { - return sib; - } - } - return null; -}; - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Version of |lastChild| that skips nodes that are entirely - * whitespace or comments. (Normally |lastChild| is a property - * of all DOM nodes that gives the last of the nodes contained - * directly in the reference node.) - * - * @param {Node} par The reference node. - * @return {Node} Either: - * 1) The last child of |sib| that is not - * ignorable according to |isIgnorable|, or - * 2) null if no such node exists. - */ -cvox.SelectionUtil.lastChildNode = function(par) { - var res = par.lastChild; - while (res) { - if (!cvox.SelectionUtil.isIgnorable(res)) { - return res; - } - res = res.previousSibling; - } - return null; -}; - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Version of |firstChild| that skips nodes that are entirely - * whitespace and comments. - * - * @param {Node} par The reference node. - * @return {Node} Either: - * 1) The first child of |sib| that is not - * ignorable according to |isIgnorable|, or - * 2) null if no such node exists. - */ -cvox.SelectionUtil.firstChildNode = function(par) { - var res = par.firstChild; - while (res) { - if (!cvox.SelectionUtil.isIgnorable(res)) { - return res; - } - res = res.nextSibling; - } - return null; -}; - -/** - * This is from https://developer.mozilla.org/en/Whitespace_in_the_DOM - * Version of |data| that doesn't include whitespace at the beginning - * and end and normalizes all whitespace to a single space. (Normally - * |data| is a property of text nodes that gives the text of the node.) - * - * @param {Node} txt The text node whose data should be returned. - * @return {string} A string giving the contents of the text node with - * whitespace collapsed. - */ -cvox.SelectionUtil.dataOf = function(txt) { - var data = txt.data; - // Use ECMA-262 Edition 3 String and RegExp features - data = data.replace(/[\t\n\r ]+/g, ' '); - if (data.charAt(0) == ' ') { - data = data.substring(1, data.length); - } - if (data.charAt(data.length - 1) == ' ') { - data = data.substring(0, data.length - 1); - } - return data; -}; - -/** - * Returns true if the selection has content from at least one node - * that has the specified tagName. - * - * @param {Selection} sel The selection. - * @param {string} tagName Tagname that the selection should be checked for. - * @return {boolean} True if the selection has content from at least one node - * with the specified tagName. - */ -cvox.SelectionUtil.hasContentWithTag = function(sel, tagName) { - if (!sel || !sel.anchorNode || !sel.focusNode) { - return false; - } - if (sel.anchorNode.tagName && (sel.anchorNode.tagName == tagName)) { - return true; - } - if (sel.focusNode.tagName && (sel.focusNode.tagName == tagName)) { - return true; - } - if (sel.anchorNode.parentNode.tagName && - (sel.anchorNode.parentNode.tagName == tagName)) { - return true; - } - if (sel.focusNode.parentNode.tagName && - (sel.focusNode.parentNode.tagName == tagName)) { - return true; - } - var docFrag = sel.getRangeAt(0).cloneContents(); - var span = document.createElement('span'); - span.appendChild(docFrag); - return (span.getElementsByTagName(tagName).length > 0); -}; - -/** - * Selects text within a text node. - * - * Note that the input node MUST be of type TEXT; otherwise, the offset - * count would not mean # of characters - this is because of the way Range - * works in JavaScript. - * - * @param {Node} textNode The text node to select text within. - * @param {number} start The start of the selection. - * @param {number} end The end of the selection. - */ -cvox.SelectionUtil.selectText = function(textNode, start, end) { - var newRange = document.createRange(); - newRange.setStart(textNode, start); - newRange.setEnd(textNode, end); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(newRange); -}; - -/** - * Selects all the text in a given node. - * - * @param {Node} node The target node. - */ -cvox.SelectionUtil.selectAllTextInNode = function(node) { - var newRange = document.createRange(); - newRange.setStart(node, 0); - newRange.setEndAfter(node); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(newRange); -}; - -/** - * Collapses the selection to the start. If nothing is selected, - * selects the beginning of the given node. - * - * @param {Node} node The target node. - */ -cvox.SelectionUtil.collapseToStart = function(node) { - var sel = window.getSelection(); - var cursorNode = sel.anchorNode; - var cursorOffset = sel.anchorOffset; - if (cursorNode == null) { - cursorNode = node; - cursorOffset = 0; - } - var newRange = document.createRange(); - newRange.setStart(cursorNode, cursorOffset); - newRange.setEnd(cursorNode, cursorOffset); - sel.removeAllRanges(); - sel.addRange(newRange); -}; - -/** - * Collapses the selection to the end. If nothing is selected, - * selects the end of the given node. - * - * @param {Node} node The target node. - */ -cvox.SelectionUtil.collapseToEnd = function(node) { - var sel = window.getSelection(); - var cursorNode = sel.focusNode; - var cursorOffset = sel.focusOffset; - if (cursorNode == null) { - cursorNode = node; - cursorOffset = 0; - } - var newRange = document.createRange(); - newRange.setStart(cursorNode, cursorOffset); - newRange.setEnd(cursorNode, cursorOffset); - sel.removeAllRanges(); - sel.addRange(newRange); -}; - -/** - * Retrieves all the text within a selection. - * - * Note that this can be different than simply using the string from - * window.getSelection() as this will account for IMG nodes, etc. - * - * @return {string} The string of text contained in the current selection. - */ -cvox.SelectionUtil.getText = function() { - var sel = window.getSelection(); - if (cvox.SelectionUtil.hasContentWithTag(sel, 'IMG')) { - var text = ''; - var docFrag = sel.getRangeAt(0).cloneContents(); - var span = document.createElement('span'); - span.appendChild(docFrag); - var leafNodes = cvox.XpathUtil.getLeafNodes(span); - for (var i = 0, node; node = leafNodes[i]; i++) { - text = text + ' ' + cvox.DomUtil.getName(node); - } - return text; - } else { - return this.getSelectionText_(); - } -}; - -/** - * Returns the selection as text instead of a selection object. Note that this - * function must be used in place of getting text directly from the DOM - * if you want i18n tests to pass. - * - * @return {string} The text. - * @private - */ -cvox.SelectionUtil.getSelectionText_ = function() { - return '' + window.getSelection(); -}; - - -/** - * Returns a range as text instead of a selection object. Note that this - * function must be used in place of getting text directly from the DOM - * if you want i18n tests to pass. - * - * @param {Range} range A range. - * @return {string} The text. - */ -cvox.SelectionUtil.getRangeText = function(range) { - if (range) - return range.cloneContents().textContent.replace(/\s+/g, ' '); - else - return ''; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs deleted file mode 100644 index 7adbc57a8d3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/selection_util_test.unitjs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxSelectionUtilUnitTest() {} - -CvoxSelectionUtilUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.SelectionUtil' - ] -}; - -TEST_F('CvoxSelectionUtilUnitTest', 'SimpleFindPos', function() { - this.loadDoc(function() {/*! - <div id="foo" style="position:absolute;top:50px"> - </div> - */}); - element = $('foo'); - assertEquals(cvox.SelectionUtil.findPos_(element)[1], 50); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/spannable.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/spannable.js deleted file mode 100644 index be77ba04ae7..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/spannable.js +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Class which allows construction of annotated strings. - */ - -goog.provide('cvox.Spannable'); - -goog.require('goog.object'); - -/** - * @constructor - * @param {string|!cvox.Spannable=} opt_string Initial value of the spannable. - * @param {*=} opt_annotation Initial annotation for the entire string. - */ -cvox.Spannable = function(opt_string, opt_annotation) { - /** - * Underlying string. - * @type {string} - * @private - */ - this.string_ = opt_string instanceof cvox.Spannable ? '' : opt_string || ''; - - /** - * Spans (annotations). - * @type {!Array<!{ value: *, start: number, end: number }>} - * @private - */ - this.spans_ = []; - - // Append the initial spannable. - if (opt_string instanceof cvox.Spannable) - this.append(opt_string); - - // Optionally annotate the entire string. - if (goog.isDef(opt_annotation)) { - var len = this.string_.length; - this.spans_.push({ value: opt_annotation, start: 0, end: len }); - } -}; - - -/** @override */ -cvox.Spannable.prototype.toString = function() { - return this.string_; -}; - - -/** - * Returns the length of the string. - * @return {number} Length of the string. - */ -cvox.Spannable.prototype.getLength = function() { - return this.string_.length; -}; - - -/** - * Adds a span to some region of the string. - * @param {*} value Annotation. - * @param {number} start Starting index (inclusive). - * @param {number} end Ending index (exclusive). - */ -cvox.Spannable.prototype.setSpan = function(value, start, end) { - this.removeSpan(value); - if (0 <= start && start <= end && end <= this.string_.length) { - // Zero-length spans are explicitly allowed, because it is possible to - // query for position by annotation as well as the reverse. - this.spans_.push({ value: value, start: start, end: end }); - this.spans_.sort(function(a, b) { - var ret = a.start - b.start; - if (ret == 0) - ret = a.end - b.end; - return ret; - }); - } else { - throw new RangeError('span out of range (start=' + start + - ', end=' + end + ', len=' + this.string_.length + ')'); - } -}; - - -/** - * Removes a span. - * @param {*} value Annotation. - */ -cvox.Spannable.prototype.removeSpan = function(value) { - for (var i = this.spans_.length - 1; i >= 0; i--) { - if (this.spans_[i].value === value) { - this.spans_.splice(i, 1); - } - } -}; - - -/** - * Appends another Spannable or string to this one. - * @param {string|!cvox.Spannable} other String or spannable to concatenate. - */ -cvox.Spannable.prototype.append = function(other) { - if (other instanceof cvox.Spannable) { - var otherSpannable = /** @type {!cvox.Spannable} */ (other); - var originalLength = this.getLength(); - this.string_ += otherSpannable.string_; - other.spans_.forEach(goog.bind(function(span) { - this.setSpan( - span.value, - span.start + originalLength, - span.end + originalLength); - }, this)); - } else if (typeof other === 'string') { - this.string_ += /** @type {string} */ (other); - } -}; - - -/** - * Returns the first value matching a position. - * @param {number} position Position to query. - * @return {*} Value annotating that position, or undefined if none is found. - */ -cvox.Spannable.prototype.getSpan = function(position) { - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.start <= position && position < span.end) { - return span.value; - } - } -}; - - -/** - * Returns the first span value which is an instance of a given constructor. - * @param {!Function} constructor Constructor. - * @return {!Object|undefined} Object if found; undefined otherwise. - */ -cvox.Spannable.prototype.getSpanInstanceOf = function(constructor) { - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.value instanceof constructor) { - return span.value; - } - } -}; - -/** - * Returns all span values which are an instance of a given constructor. - * Spans are returned in the order of their starting index and ending index - * for spans with equals tarting indices. - * @param {!Function} constructor Constructor. - * @return {!Array<Object>} Array of object. - */ -cvox.Spannable.prototype.getSpansInstanceOf = function(constructor) { - var ret = []; - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.value instanceof constructor) { - ret.push(span.value); - } - } - return ret; -}; - - -/** - * Returns all spans matching a position. - * @param {number} position Position to query. - * @return {!Array} Values annotating that position. - */ -cvox.Spannable.prototype.getSpans = function(position) { - var results = []; - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.start <= position && position < span.end) { - results.push(span.value); - } - } - return results; -}; - - -/** - * Returns the start of the requested span. - * @param {*} value Annotation. - * @return {number|undefined} Start of the span, or undefined if not attached. - */ -cvox.Spannable.prototype.getSpanStart = function(value) { - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.value === value) { - return span.start; - } - } - return undefined; -}; - - -/** - * Returns the end of the requested span. - * @param {*} value Annotation. - * @return {number|undefined} End of the span, or undefined if not attached. - */ -cvox.Spannable.prototype.getSpanEnd = function(value) { - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.value === value) { - return span.end; - } - } - return undefined; -}; - - -/** - * Returns a substring of this spannable. - * Note that while similar to String#substring, this function is much less - * permissive about its arguments. It does not accept arguments in the wrong - * order or out of bounds. - * - * @param {number} start Start index, inclusive. - * @param {number=} opt_end End index, exclusive. - * If excluded, the length of the string is used instead. - * @return {!cvox.Spannable} Substring requested. - */ -cvox.Spannable.prototype.substring = function(start, opt_end) { - var end = goog.isDef(opt_end) ? opt_end : this.string_.length; - - if (start < 0 || end > this.string_.length || start > end) { - throw new RangeError('substring indices out of range'); - } - - var result = new cvox.Spannable(this.string_.substring(start, end)); - for (var i = 0; i < this.spans_.length; i++) { - var span = this.spans_[i]; - if (span.start <= end && span.end >= start) { - var newStart = Math.max(0, span.start - start); - var newEnd = Math.min(end - start, span.end - start); - result.spans_.push({ value: span.value, start: newStart, end: newEnd }); - } - } - return result; -}; - - -/** - * Trims whitespace from the beginning. - * @return {!cvox.Spannable} String with whitespace removed. - */ -cvox.Spannable.prototype.trimLeft = function() { - return this.trim_(true, false); -}; - - -/** - * Trims whitespace from the end. - * @return {!cvox.Spannable} String with whitespace removed. - */ -cvox.Spannable.prototype.trimRight = function() { - return this.trim_(false, true); -}; - - -/** - * Trims whitespace from the beginning and end. - * @return {!cvox.Spannable} String with whitespace removed. - */ -cvox.Spannable.prototype.trim = function() { - return this.trim_(true, true); -}; - - -/** - * Trims whitespace from either the beginning and end or both. - * @param {boolean} trimStart Trims whitespace from the start of a string. - * @param {boolean} trimEnd Trims whitespace from the end of a string. - * @return {!cvox.Spannable} String with whitespace removed. - * @private - */ -cvox.Spannable.prototype.trim_ = function(trimStart, trimEnd) { - if (!trimStart && !trimEnd) { - return this; - } - - // Special-case whitespace-only strings, including the empty string. - // As an arbitrary decision, we treat this as trimming the whitespace off the - // end, rather than the beginning, of the string. - // This choice affects which spans are kept. - if (/^\s*$/.test(this.string_)) { - return this.substring(0, 0); - } - - // Otherwise, we have at least one non-whitespace character to use as an - // anchor when trimming. - var trimmedStart = trimStart ? this.string_.match(/^\s*/)[0].length : 0; - var trimmedEnd = trimEnd ? - this.string_.match(/\s*$/).index : this.string_.length; - return this.substring(trimmedStart, trimmedEnd); -}; - - -/** - * Returns this spannable to a json serializable form, including the text and - * span objects whose types have been registered with registerSerializableSpan - * or registerStatelessSerializableSpan. - * @return {!cvox.Spannable.SerializedSpannable_} the json serializable form. - */ -cvox.Spannable.prototype.toJson = function() { - var result = {}; - result.string = this.string_; - result.spans = []; - for (var i = 0; i < this.spans_.length; ++i) { - var span = this.spans_[i]; - // Use linear search, since using functions as property keys - // is not reliable. - var serializeInfo = goog.object.findValue( - cvox.Spannable.serializableSpansByName_, - function(v) { return v.ctor === span.value.constructor; }); - if (serializeInfo) { - var spanObj = {type: serializeInfo.name, - start: span.start, - end: span.end}; - if (serializeInfo.toJson) { - spanObj.value = serializeInfo.toJson.apply(span.value); - } - result.spans.push(spanObj); - } - } - return result; -}; - - -/** - * Creates a spannable from a json serializable representation. - * @param {!cvox.Spannable.SerializedSpannable_} obj object containing the - * serializable representation. - * @return {!cvox.Spannable} - */ -cvox.Spannable.fromJson = function(obj) { - if (typeof obj.string !== 'string') { - throw 'Invalid spannable json object: string field not a string'; - } - if (!(obj.spans instanceof Array)) { - throw 'Invalid spannable json object: no spans array'; - } - var result = new cvox.Spannable(obj.string); - for (var i = 0, span; span = obj.spans[i]; ++i) { - if (typeof span.type !== 'string') { - throw 'Invalid span in spannable json object: type not a string'; - } - if (typeof span.start !== 'number' || typeof span.end !== 'number') { - throw 'Invalid span in spannable json object: start or end not a number'; - } - var serializeInfo = cvox.Spannable.serializableSpansByName_[span.type]; - var value = serializeInfo.fromJson(span.value); - result.setSpan(value, span.start, span.end); - } - return result; -}; - - -/** - * Registers a type that can be converted to a json serializable format. - * @param {!Function} constructor The type of object that can be converted. - * @param {string} name String identifier used in the serializable format. - * @param {function(!Object): !Object} fromJson A function that converts - * the serializable object to an actual object of this type. - * @param {function(!Object): !Object} toJson A function that converts - * this object to a json serializable object. The function will - * be called with this set to the object to convert. - */ -cvox.Spannable.registerSerializableSpan = function( - constructor, name, fromJson, toJson) { - var obj = {name: name, ctor: constructor, - fromJson: fromJson, toJson: toJson}; - cvox.Spannable.serializableSpansByName_[name] = obj; -}; - - -/** - * Registers an object type that can be converted to/from a json serializable - * form. Objects of this type carry no state that will be preserved - * when serialized. - * @param {!Function} constructor The type of the object that can be converted. - * This constructor will be called with no arguments to construct - * new objects. - * @param {string} name Name of the type used in the serializable object. - */ -cvox.Spannable.registerStatelessSerializableSpan = function( - constructor, name) { - var obj = {name: name, ctor: constructor, toJson: undefined}; - /** - * @param {!Object} obj - * @return {!Object} - */ - obj.fromJson = function(obj) { - return new constructor(); - }; - cvox.Spannable.serializableSpansByName_[name] = obj; -}; - - -/** - * Describes how to convert a span type to/from serializable json. - * @typedef {{ctor: !Function, name: string, - * fromJson: function(!Object): !Object, - * toJson: ((function(!Object): !Object)|undefined)}} - * @private - */ -cvox.Spannable.SerializeInfo_; - - -/** - * The serialized format of a spannable. - * @typedef {{string: string, spans: Array<cvox.Spannable.SerializedSpan_>}} - * @private - */ -cvox.Spannable.SerializedSpannable_; - - -/** - * The format of a single annotation in a serialized spannable. - * @typedef {{type: string, value: !Object, start: number, end: number}} - * @private - */ -cvox.Spannable.SerializedSpan_; - -/** - * Maps type names to serialization info objects. - * @type {Object<cvox.Spannable.SerializeInfo_>} - * @private - */ -cvox.Spannable.serializableSpansByName_ = {}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs deleted file mode 100644 index 8a26cfeb5d3..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/spannable_test.unitjs +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -UnserializableSpan = function() {}; - -StatelessSerializableSpan = function() {}; - -NonStatelessSerializableSpan = function(value) { - this.value = value; -}; - -/** - * @param {!Object} obj object containing the - * serializable representation. - * @return {!Object} The Spannable. - */ -NonStatelessSerializableSpan.fromJson = function(obj) { - return new NonStatelessSerializableSpan(obj.value / 2); -}; - -/** - * @return {Object} the json serializable form. - */ -NonStatelessSerializableSpan.prototype.toJson = function() { - return {value: this.value * 2}; -}; - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function CvoxSpannableUnitTest() {} - -CvoxSpannableUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'cvox.Spannable', - ], - - /** @override */ - setUp: function() { - cvox.Spannable.registerStatelessSerializableSpan( - StatelessSerializableSpan, 'StatelessSerializableSpan'); - - cvox.Spannable.registerSerializableSpan( - NonStatelessSerializableSpan, 'NonStatelessSerializableSpan', - NonStatelessSerializableSpan.fromJson, - NonStatelessSerializableSpan.prototype.toJson); - } -}; - -TEST_F('CvoxSpannableUnitTest', 'ToStringUnannotated', function() { - assertEquals('', new cvox.Spannable().toString()); - assertEquals('hello world', new cvox.Spannable('hello world').toString()); -}); - -/** Tests that toString works correctly on annotated strings. */ -TEST_F('CvoxSpannableUnitTest', 'ToStringAnnotated', function() { - var spannable = new cvox.Spannable('Hello Google'); - spannable.setSpan('http://www.google.com/', 6, 12); - assertEquals('Hello Google', spannable.toString()); -}); - -/** Tests the length calculation. */ -TEST_F('CvoxSpannableUnitTest', 'GetLength', function() { - var spannable = new cvox.Spannable('Hello'); - spannable.setSpan({}, 0, 3); - assertEquals(5, spannable.getLength()); - spannable.append(' world'); - assertEquals(11, spannable.getLength()); - spannable.append(new cvox.Spannable(' from cvox.Spannable')); - assertEquals(31, spannable.getLength()); -}); - -/** Tests that a span can be added and retrieved at the beginning. */ -TEST_F('CvoxSpannableUnitTest', 'SpanBeginning', function() { - var annotation = {}; - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(annotation, 0, 5); - assertSame(annotation, spannable.getSpan(0)); - assertSame(annotation, spannable.getSpan(3)); - assertUndefined(spannable.getSpan(5)); - assertUndefined(spannable.getSpan(8)); -}); - -/** Tests that a span can be added and retrieved at the beginning. */ -TEST_F('CvoxSpannableUnitTest', 'SpanEnd', function() { - var annotation = {}; - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(annotation, 6, 11); - assertUndefined(spannable.getSpan(3)); - assertUndefined(spannable.getSpan(5)); - assertSame(annotation, spannable.getSpan(6)); - assertSame(annotation, spannable.getSpan(10)); -}); - -/** Tests that a zero-length span is not retrieved. */ -TEST_F('CvoxSpannableUnitTest', 'SpanZeroLength', function() { - var annotation = {}; - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(annotation, 3, 3); - assertUndefined(spannable.getSpan(2)); - assertUndefined(spannable.getSpan(3)); - assertUndefined(spannable.getSpan(4)); -}); - -/** Tests that a removed span is not returned. */ -TEST_F('CvoxSpannableUnitTest', 'RemoveSpan', function() { - var annotation = {}; - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(annotation, 0, 3); - assertSame(annotation, spannable.getSpan(1)); - spannable.removeSpan(annotation); - assertUndefined(spannable.getSpan(1)); -}); - -/** Tests that adding a span in one place removes it from another. */ -TEST_F('CvoxSpannableUnitTest', 'SetSpanMoves', function() { - var annotation = {}; - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(annotation, 0, 3); - assertSame(annotation, spannable.getSpan(1)); - assertUndefined(spannable.getSpan(4)); - spannable.setSpan(annotation, 3, 6); - assertUndefined(spannable.getSpan(1)); - assertSame(annotation, spannable.getSpan(4)); -}); - -/** Tests that setSpan objects to out-of-range arguments. */ -TEST_F('CvoxSpannableUnitTest', 'SetSpanRangeError', function() { - var spannable = new cvox.Spannable('Hello world'); - - // Start index out of range. - assertException('expected range error', function() { - spannable.setSpan({}, -1, 0); - }, 'RangeError'); - - // End index out of range. - assertException('expected range error', function() { - spannable.setSpan({}, 0, 12); - }, 'RangeError'); - - // End before start. - assertException('expected range error', function() { - spannable.setSpan({}, 1, 0); - }, 'RangeError'); -}); - -/** - * Tests that multiple spans can be retrieved at one point. - * The first one added which applies should be returned by getSpan. - */ -TEST_F('CvoxSpannableUnitTest', 'MultipleSpans', function() { - var annotation1 = { number: 1 }; - var annotation2 = { number: 2 }; - assertNotSame(annotation1, annotation2); - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(annotation1, 1, 4); - spannable.setSpan(annotation2, 2, 7); - assertSame(annotation1, spannable.getSpan(1)); - assertThat([annotation1], eqJSON(spannable.getSpans(1))); - assertSame(annotation1, spannable.getSpan(3)); - assertThat([annotation1, annotation2], eqJSON(spannable.getSpans(3))); - assertSame(annotation2, spannable.getSpan(6)); - assertThat([annotation2], eqJSON(spannable.getSpans(6))); -}); - -/** Tests that appending appends the strings. */ -TEST_F('CvoxSpannableUnitTest', 'AppendToString', function() { - var spannable = new cvox.Spannable('Google'); - assertEquals('Google', spannable.toString()); - spannable.append(' Chrome'); - assertEquals('Google Chrome', spannable.toString()); - spannable.append(new cvox.Spannable('Vox')); - assertEquals('Google ChromeVox', spannable.toString()); -}); - -/** - * Tests that appending Spannables combines annotations. - */ -TEST_F('CvoxSpannableUnitTest', 'AppendAnnotations', function() { - var annotation1 = { number: 1 }; - var annotation2 = { number: 2 }; - assertNotSame(annotation1, annotation2); - var left = new cvox.Spannable('hello'); - left.setSpan(annotation1, 0, 3); - var right = new cvox.Spannable(' world'); - right.setSpan(annotation2, 0, 3); - left.append(right); - assertSame(annotation1, left.getSpan(1)); - assertSame(annotation2, left.getSpan(6)); -}); - -/** - * Tests that a span's bounds can be retrieved. - */ -TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEnd', function() { - var annotation = {}; - var spannable = new cvox.Spannable('potato wedges'); - spannable.setSpan(annotation, 8, 12); - assertEquals(8, spannable.getSpanStart(annotation)); - assertEquals(12, spannable.getSpanEnd(annotation)); -}); - -/** - * Tests that an absent span's bounds are reported correctly. - */ -TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndAbsent', function() { - var annotation = {}; - var spannable = new cvox.Spannable('potato wedges'); - assertUndefined(spannable.getSpanStart(annotation)); - assertUndefined(spannable.getSpanEnd(annotation)); -}); - -/** - * Test that a zero length span can still be found. - */ -TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndZeroLength', function() { - var annotation = {}; - var spannable = new cvox.Spannable('potato wedges'); - spannable.setSpan(annotation, 8, 8); - assertEquals(8, spannable.getSpanStart(annotation)); - assertEquals(8, spannable.getSpanEnd(annotation)); -}); - -/** - * Tests that == (but not ===) objects are treated distinctly when getting - * span bounds. - */ -TEST_F('CvoxSpannableUnitTest', 'GetSpanStartAndEndEquality', function() { - // Note that 0 == '' and '' == 0 in JavaScript. - var spannable = new cvox.Spannable('wat'); - spannable.setSpan(0, 0, 0); - spannable.setSpan('', 1, 3); - assertEquals(0, spannable.getSpanStart(0)); - assertEquals(0, spannable.getSpanEnd(0)); - assertEquals(1, spannable.getSpanStart('')); - assertEquals(3, spannable.getSpanEnd('')); -}); - -/** - * Tests that substrings have the correct character sequence. - */ -TEST_F('CvoxSpannableUnitTest', 'Substring', function() { - var assertSubstringResult = function(expected, initial, start, opt_end) { - var spannable = new cvox.Spannable(initial); - var substring = spannable.substring(start, opt_end); - assertEquals(expected, substring.toString()); - }; - assertSubstringResult('Page', 'Google PageRank', 7, 11); - assertSubstringResult('Goog', 'Google PageRank', 0, 4); - assertSubstringResult('Rank', 'Google PageRank', 11, 15); - assertSubstringResult('Rank', 'Google PageRank', 11); -}); - -/** - * Tests that substring arguments are validated properly. - */ -TEST_F('CvoxSpannableUnitTest', 'SubstringRangeError', function() { - var assertRangeError = function(initial, start, opt_end) { - var spannable = new cvox.Spannable(initial); - assertException('expected range error', function() { - spannable.substring(start, opt_end); - }, 'RangeError'); - }; - assertRangeError('Google PageRank', -1, 5); - assertRangeError('Google PageRank', 0, 99); - assertRangeError('Google PageRank', 5, 2); -}); - -/** - * Tests that spans in the substring range are preserved. - */ -TEST_F('CvoxSpannableUnitTest', 'SubstringSpansIncluded', function() { - var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd, - initial, initialSpanStart, initialSpanEnd, start, opt_end) { - var annotation = {}; - var spannable = new cvox.Spannable(initial); - spannable.setSpan(annotation, initialSpanStart, initialSpanEnd); - var substring = spannable.substring(start, opt_end); - assertEquals(expectedSpanStart, substring.getSpanStart(annotation)); - assertEquals(expectedSpanEnd, substring.getSpanEnd(annotation)); - }; - assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7); - assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7, 13); - assertSpanIncluded(1, 5, 'potato wedges', 8, 12, 7, 12); - assertSpanIncluded(0, 4, 'potato wedges', 8, 12, 8, 12); - assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0); - assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0, 3); - assertSpanIncluded(0, 3, 'potato wedges', 0, 3, 0, 6); - assertSpanIncluded(0, 5, 'potato wedges', 8, 13, 8); - assertSpanIncluded(0, 5, 'potato wedges', 8, 13, 8, 13); - assertSpanIncluded(1, 6, 'potato wedges', 8, 13, 7, 13); - - // Note: we should keep zero-length spans, even at the edges of the range. - assertSpanIncluded(0, 0, 'potato wedges', 0, 0, 0, 0); - assertSpanIncluded(0, 0, 'potato wedges', 0, 0, 0, 6); - assertSpanIncluded(1, 1, 'potato wedges', 8, 8, 7, 13); - assertSpanIncluded(6, 6, 'potato wedges', 6, 6, 0, 6); -}); - -/** - * Tests that spans outside the range are omitted. - * It's fine to keep zero-length spans at the ends, though. - */ -TEST_F('CvoxSpannableUnitTest', 'SubstringSpansExcluded', function() { - var assertSpanExcluded = function(initial, spanStart, spanEnd, - start, opt_end) { - var annotation = {}; - var spannable = new cvox.Spannable(initial); - spannable.setSpan(annotation, spanStart, spanEnd); - var substring = spannable.substring(start, opt_end); - assertUndefined(substring.getSpanStart(annotation)); - assertUndefined(substring.getSpanEnd(annotation)); - }; - assertSpanExcluded('potato wedges', 8, 12, 0, 6); - assertSpanExcluded('potato wedges', 7, 12, 0, 6); - assertSpanExcluded('potato wedges', 0, 6, 8); - assertSpanExcluded('potato wedges', 6, 6, 8); -}); - -/** - * Tests that spans which cross the boundary are clipped. - */ -TEST_F('CvoxSpannableUnitTest', 'SubstringSpansClipped', function() { - var assertSpanIncluded = function(expectedSpanStart, expectedSpanEnd, - initial, initialSpanStart, initialSpanEnd, start, opt_end) { - var annotation = {}; - var spannable = new cvox.Spannable(initial); - spannable.setSpan(annotation, initialSpanStart, initialSpanEnd); - var substring = spannable.substring(start, opt_end); - assertEquals(expectedSpanStart, substring.getSpanStart(annotation)); - assertEquals(expectedSpanEnd, substring.getSpanEnd(annotation)); - }; - assertSpanIncluded(0, 4, 'potato wedges', 7, 13, 8, 12); - assertSpanIncluded(0, 0, 'potato wedges', 0, 6, 0, 0); - assertSpanIncluded(0, 0, 'potato wedges', 0, 6, 6, 6); - - // The first of the above should produce "edge". - assertEquals('edge', - new cvox.Spannable('potato wedges').substring(8, 12).toString()); -}); - -/** - * Tests that whitespace is trimmed. - */ -TEST_F('CvoxSpannableUnitTest', 'Trim', function() { - var assertTrimResult = function(expected, initial) { - assertEquals(expected, new cvox.Spannable(initial).trim().toString()); - }; - assertTrimResult('John F. Kennedy', 'John F. Kennedy'); - assertTrimResult('John F. Kennedy', ' John F. Kennedy'); - assertTrimResult('John F. Kennedy', 'John F. Kennedy '); - assertTrimResult('John F. Kennedy', ' \r\t \nJohn F. Kennedy\n\n \n'); - assertTrimResult('', ''); - assertTrimResult('', ' \t\t \n\r'); -}); - -/** - * Tests that trim keeps, drops and clips spans. - */ -TEST_F('CvoxSpannableUnitTest', 'TrimSpans', function() { - var spannable = new cvox.Spannable(' \t Kennedy\n'); - spannable.setSpan('tab', 1, 2); - spannable.setSpan('jfk', 3, 10); - spannable.setSpan('jfk-newline', 3, 11); - var trimmed = spannable.trim(); - assertUndefined(trimmed.getSpanStart('tab')); - assertUndefined(trimmed.getSpanEnd('tab')); - assertEquals(0, trimmed.getSpanStart('jfk')); - assertEquals(7, trimmed.getSpanEnd('jfk')); - assertEquals(0, trimmed.getSpanStart('jfk-newline')); - assertEquals(7, trimmed.getSpanEnd('jfk-newline')); -}); - -/** - * Tests that when a string is all whitespace, we trim off the *end*. - */ -TEST_F('CvoxSpannableUnitTest', 'TrimAllWhitespace', function() { - var spannable = new cvox.Spannable(' '); - spannable.setSpan('cursor 1', 0, 0); - spannable.setSpan('cursor 2', 2, 2); - var trimmed = spannable.trim(); - assertEquals(0, trimmed.getSpanStart('cursor 1')); - assertEquals(0, trimmed.getSpanEnd('cursor 1')); - assertUndefined(trimmed.getSpanStart('cursor 2')); - assertUndefined(trimmed.getSpanEnd('cursor 2')); -}); - -/** - * Tests finding a span which is an instance of a given class. - */ -TEST_F('CvoxSpannableUnitTest', 'GetSpanInstanceOf', function() { - function ExampleConstructorBase() {} - function ExampleConstructor1() {} - function ExampleConstructor2() {} - function ExampleConstructor3() {} - ExampleConstructor1.prototype = new ExampleConstructorBase(); - ExampleConstructor2.prototype = new ExampleConstructorBase(); - ExampleConstructor3.prototype = new ExampleConstructorBase(); - var ex1 = new ExampleConstructor1(); - var ex2 = new ExampleConstructor2(); - var spannable = new cvox.Spannable('Hello world'); - spannable.setSpan(ex1, 0, 0); - spannable.setSpan(ex2, 1, 1); - assertEquals(ex1, spannable.getSpanInstanceOf(ExampleConstructor1)); - assertEquals(ex2, spannable.getSpanInstanceOf(ExampleConstructor2)); - assertUndefined(spannable.getSpanInstanceOf(ExampleConstructor3)); - assertEquals(ex1, spannable.getSpanInstanceOf(ExampleConstructorBase)); -}); - -/** Tests trimming only left or right. */ -TEST_F('CvoxSpannableUnitTest', 'TrimLeftOrRight', function() { - var spannable = new cvox.Spannable(' '); - spannable.setSpan('cursor 1', 0, 0); - spannable.setSpan('cursor 2', 2, 2); - var trimmed = spannable.trimLeft(); - assertEquals(0, trimmed.getSpanStart('cursor 1')); - assertEquals(0, trimmed.getSpanEnd('cursor 1')); - assertUndefined(trimmed.getSpanStart('cursor 2')); - assertUndefined(trimmed.getSpanEnd('cursor 2')); - - var spannable2 = new cvox.Spannable('0 '); - spannable2.setSpan('cursor 1', 0, 0); - spannable2.setSpan('cursor 2', 2, 2); - var trimmed2 = spannable2.trimLeft(); - assertEquals(0, trimmed2.getSpanStart('cursor 1')); - assertEquals(0, trimmed2.getSpanEnd('cursor 1')); - assertEquals(2, trimmed2.getSpanStart('cursor 2')); - assertEquals(2, trimmed2.getSpanEnd('cursor 2')); - trimmed2 = trimmed2.trimRight(); - assertEquals(0, trimmed2.getSpanStart('cursor 1')); - assertEquals(0, trimmed2.getSpanEnd('cursor 1')); - assertUndefined(trimmed2.getSpanStart('cursor 2')); - assertUndefined(trimmed2.getSpanEnd('cursor 2')); - - var spannable3 = new cvox.Spannable(' 0'); - spannable3.setSpan('cursor 1', 0, 0); - spannable3.setSpan('cursor 2', 2, 2); - var trimmed3 = spannable3.trimRight(); - assertEquals(0, trimmed3.getSpanStart('cursor 1')); - assertEquals(0, trimmed3.getSpanEnd('cursor 1')); - assertEquals(2, trimmed3.getSpanStart('cursor 2')); - assertEquals(2, trimmed3.getSpanEnd('cursor 2')); - trimmed3 = trimmed3.trimLeft(); - assertUndefined(trimmed3.getSpanStart('cursor 1')); - assertUndefined(trimmed3.getSpanEnd('cursor 1')); - assertEquals(0, trimmed3.getSpanStart('cursor 2')); - assertEquals(0, trimmed3.getSpanEnd('cursor 2')); -}); - -TEST_F('CvoxSpannableUnitTest', 'Serialize', function() { - var fresh = new cvox.Spannable('text'); - var freshStatelessSerializable = new StatelessSerializableSpan(); - var freshNonStatelessSerializable = new NonStatelessSerializableSpan(14); - fresh.setSpan(new UnserializableSpan(), 0, 1); - fresh.setSpan(freshStatelessSerializable, 0, 2); - fresh.setSpan(freshNonStatelessSerializable, 3, 4); - var thawn = cvox.Spannable.fromJson(fresh.toJson()); - var thawnStatelessSerializable = thawn.getSpanInstanceOf( - StatelessSerializableSpan); - var thawnNonStatelessSerializable = thawn.getSpanInstanceOf( - NonStatelessSerializableSpan); - assertThat('text', eqJSON(thawn.toString())); - assertUndefined(thawn.getSpanInstanceOf(UnserializableSpan)); - assertThat( - fresh.getSpanStart(freshStatelessSerializable), - eqJSON(thawn.getSpanStart(thawnStatelessSerializable))); - assertThat( - fresh.getSpanEnd(freshStatelessSerializable), - eqJSON(thawn.getSpanEnd(thawnStatelessSerializable))); - assertThat(freshNonStatelessSerializable, - eqJSON(thawnNonStatelessSerializable)); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/string_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/string_util.js deleted file mode 100644 index 5b771c5a774..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/string_util.js +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Utilities for strings. - */ - -goog.provide('StringUtil'); - -/** - * @constructor - */ -StringUtil = function() {}; - -/** - * Returns the length of the longest common prefix of two strings. - * @param {string} first The first string. - * @param {string} second The second string. - * @return {number} The length of the longest common prefix, which may be 0 - * for an empty common prefix. - */ -StringUtil.longestCommonPrefixLength = function(first, second) { - var limit = Math.min(first.length, second.length); - var i; - for (i = 0; i < limit; ++i) { - if (first.charAt(i) != second.charAt(i)) { - break; - } - } - return i; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/string_util_test.unitjs b/chromium/chrome/browser/resources/chromeos/chromevox/common/string_util_test.unitjs deleted file mode 100644 index efd31a5afdb..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/string_util_test.unitjs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Include test fixture. -GEN_INCLUDE(['../testing/chromevox_unittest_base.js']); - -/** - * Test fixture. - * @constructor - * @extends {ChromeVoxUnitTestBase} - */ -function StringUtilUnitTest() { - ChromeVoxUnitTestBase.call(this); -} - -StringUtilUnitTest.prototype = { - __proto__: ChromeVoxUnitTestBase.prototype, - - /** @override */ - closureModuleDeps: [ - 'StringUtil', - ], -}; - -TEST_F('StringUtilUnitTest', 'longestCommonPrefixLength', function() { - var lcpl = StringUtil.longestCommonPrefixLength; - assertEquals(0, lcpl('', '')); - assertEquals(0, lcpl('', 'hello')); - assertEquals(0, lcpl('hello', '')); - assertEquals(1, lcpl('hi', 'hello')); -}); diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/table_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/table_util.js deleted file mode 100644 index ec22512c1a5..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/table_util.js +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview DOM utility functions to aid in table navigation. - */ - -goog.provide('cvox.TableUtil'); - -goog.require('cvox.XpathUtil'); - - -/** - * Utility function to check if a particular table cell is a candidate - * header cell. - * @param {Node} cell The table cell. - * @return {boolean} Whether or not the table cell is acting as a header cell. - */ -cvox.TableUtil.checkIfHeader = function(cell) { - /* - * Headers are defined here as <TH> or <TD> elements. <TD> elements when - * serving as header cells must have either: - * - The scope attribute defined - * - Their IDs referenced in the header content attribute of another <TD> or - * <TH> element. - * This function does not check whether this cell is referenced by another - * <TD>. So this function by itself will not be able to gather all possible - * header cells when applied across all table cells. - * - * Please Note: - * The HTML5 spec specifies that only header <TH> elements can be headers - * ( http://dev.w3.org/html5/spec/tabular-data.html#row-header ) but the - * HTML4 spec says that <TD> elements can act as headers if they have a - * scope attribute defined - * ( http://www.w3.org/TR/html401/struct/tables.html#h-11.2.6 ). In the - * interest of providing meaningful header information for all tables, here - * we take the position that <TD> elements can act as headers. - */ - return ((cell.tagName == 'TH') || - cell.hasAttribute('scope') || (cell.hasAttribute('role') && - ((cell.getAttribute('role') == 'rowheader') || - (cell.getAttribute('role') == 'columnheader')))); -}; - - -/** - * Utility function to determine colgroup structure. Builds an array that - * associates a column number to a particular col group. - * @param {Array} colGroups An array of all the colgroup elements in a - * particular table. - * @return {Array} An array that maps indexes representing table columns - * to indexes into the colGroups array. - */ -cvox.TableUtil.determineColGroups = function(colGroups) { - var colToColGroup = []; - - if (colGroups.length == 0) { - return colToColGroup; - } - // A colgroup has either a series of col element children or a span - // attribute. If it has col children, ignore the span attribute - for (var colGroupCtr = 0; colGroupCtr < colGroups.length; - colGroupCtr++) { - - var currentColGroup = colGroups[colGroupCtr]; - - var childCols = cvox.TableUtil.getColNodes(currentColGroup); - if (childCols.length > 0) { - for (var colNodeCtr = 0; colNodeCtr < childCols.length; - colNodeCtr++) { - var colElement = childCols[colNodeCtr]; - - if (colElement.hasAttribute('span')) { - var span = parseInt(colElement.getAttribute('span'), 10); - - for (var s = 0; s < span; s++) { - colToColGroup.push(colGroupCtr); - } - } else { - colToColGroup.push(colGroupCtr); - } - } - } else { - // No children of the current colgroup. Does it have a span attribute? - if (currentColGroup.hasAttribute('span')) { - var span = parseInt(currentColGroup.getAttribute('span'), 10); - - for (var s = 0; s < span; s++) { - colToColGroup.push(colGroupCtr); - } - } else { - // Default span value is 1 - colToColGroup.push(colGroupCtr); - } - } - } - return colToColGroup; - -}; - - -/** - * Utility function to push an element into a given array only if that element - * is not already contained in the array. - * @param {Array} givenArray The given array. - * @param {Node} givenElement The given element. - */ -cvox.TableUtil.pushIfNotContained = function(givenArray, givenElement) { - if (givenArray.indexOf(givenElement) == -1) { - givenArray.push(givenElement); - } -}; - - -/** - * Returns a JavaScript array of all the non-nested rows in the given table. - * - * @param {Node} table A table node. - * @return {Array} An array of all the child rows of the active table. - */ -cvox.TableUtil.getChildRows = function(table) { - return cvox.XpathUtil.evalXPath('child::tbody/tr | child::thead/tr | ' + - 'child::*[attribute::role="row"]', table); -}; - - -/** - * Returns a JavaScript array of all the child cell <TD> or <TH> or - * role='gridcell' nodes of the given row. - * - * @param {Node} rowNode The specified row node. - * @return {Array} An array of all the child cells of the given row node. - */ -cvox.TableUtil.getChildCells = function(rowNode) { - return cvox.XpathUtil.evalXPath('child::td | child::th | ' + - 'child::*[attribute::role="gridcell"] |' + - 'child::*[attribute::role="rowheader"] |' + - 'child::*[attribute::role="columnheader"]', rowNode); -}; - - -/** - * Returns a JavaScript array containing the cell in the active table - * with the given ID. - * - * @param {Node} table A table node. - * @param {string} cellID The specified ID. - * @return {Array} An array containing the cell with the specified ID. - */ -cvox.TableUtil.getCellWithID = function(table, cellID) { - return cvox.XpathUtil.evalXPath('id(\'' + cellID + '\')', table); -}; - - -/** - * Returns a Javascript array containing the colgroup elements in the - * active table. - * - * @param {Node} table A table node. - * @return {Array} An array of all the colgroup elements in the active table. - */ -cvox.TableUtil.getColGroups = function(table) { - return cvox.XpathUtil.evalXPath('child::colgroup', table); -}; - - -/** - * Returns a Javascript array containing the child col elements of the given - * colgroup element. - * - * @param {Node} colGroupNode The specified <COLGROUP> element. - * @return {Array} An array of all of the child col elements of the given - * colgroup element. - */ -cvox.TableUtil.getColNodes = function(colGroupNode) { - return cvox.XpathUtil.evalXPath('child::col', colGroupNode); -}; - diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/time_widget.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/time_widget.js deleted file mode 100644 index 8691d63fb85..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/time_widget.js +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.ChromeVoxHTMLTimeWidget'); - -/** - * @fileoverview Gives the user spoken feedback as they interact with the time - * widget (input type=time). - * - */ - -/** - * A class containing the information needed to speak - * a text change event to the user. - * - * @constructor - * @param {Element} timeElem The time widget element. - * @param {cvox.TtsInterface} tts The TTS object from ChromeVox. - */ -cvox.ChromeVoxHTMLTimeWidget = function(timeElem, tts) { - var self = this; - this.timeElem_ = timeElem; - this.timeTts_ = tts; - this.pHours_ = -1; - this.pMinutes_ = -1; - this.pSeconds_ = 0; - this.pMilliseconds_ = 0; - this.pAmpm_ = ''; - this.pos_ = 0; - this.maxPos_ = 2; - this.keyListener_ = function(evt) { - self.eventHandler_(evt); - }; - this.blurListener_ = function(evt) { - self.shutdown(); - }; - if (this.timeElem_.hasAttribute('step')) { - var step = this.timeElem_.getAttribute('step'); - if (step > 0) { // 0 or invalid values show hh:mm AM/PM - if (step >= 1) { - this.maxPos_ = 3; // Anything larger than 1 shows hh:mm:ss AM/PM - } else { - this.maxPos_ = 4; // Anything less than 1 shows hh:mm:ss.ms AM/PM - } - } - } - - // Ensure we have a reasonable value to start with. - if (this.timeElem_.value.length == 0) { - this.forceInitTime_(); - } - - // Move the cursor to the first position so that we are guaranteed to start - // off at the hours position. - for (var i = 0; i < this.maxPos_; i++) { - var evt = document.createEvent('KeyboardEvent'); - evt.initKeyboardEvent( - 'keydown', true, true, window, 'Left', 0, false, false, false, false); - this.timeElem_.dispatchEvent(evt); - evt = document.createEvent('KeyboardEvent'); - evt.initKeyboardEvent( - 'keyup', true, true, window, 'Left', 0, false, false, false, false); - this.timeElem_.dispatchEvent(evt); - } - - this.timeElem_.addEventListener('keydown', this.keyListener_, false); - this.timeElem_.addEventListener('keyup', this.keyListener_, false); - this.timeElem_.addEventListener('blur', this.blurListener_, false); - this.update_(true); -}; - -/** - * Removes the key listeners for the time widget. - * - */ -cvox.ChromeVoxHTMLTimeWidget.prototype.shutdown = function() { - this.timeElem_.removeEventListener('blur', this.blurListener_, false); - this.timeElem_.removeEventListener('keydown', this.keyListener_, false); - this.timeElem_.removeEventListener('keyup', this.keyListener_, false); -}; - -/** - * Initialize to midnight. - * @private - */ -cvox.ChromeVoxHTMLTimeWidget.prototype.forceInitTime_ = function() { - this.timeElem_.setAttribute('value', '12:00'); -}; - -/** - * Called when the position changes. - * @private - */ -cvox.ChromeVoxHTMLTimeWidget.prototype.handlePosChange_ = function() { - if (this.pos_ < 0) { - this.pos_ = 0; - } - if (this.pos_ > this.maxPos_) { - this.pos_ = this.maxPos_; - } - // Reset the cached state of the new field so that the field will be spoken - // in the update. - if (this.pos_ == this.maxPos_) { - this.pAmpm_ = ''; - return; - } - switch (this.pos_) { - case 0: - this.pHours_ = -1; - break; - case 1: - this.pMinutes_ = -1; - break; - case 2: - this.pSeconds_ = -1; - break; - case 3: - this.pMilliseconds_ = -1; - break; - } -}; - -/** - * @param {boolean} shouldSpeakLabel True if the label should be spoken. - * @private - */ -cvox.ChromeVoxHTMLTimeWidget.prototype.update_ = function(shouldSpeakLabel) { - var splitTime = this.timeElem_.value.split(':'); - if (splitTime.length < 1) { - this.forceInitTime_(); - return; - } - - var hours = splitTime[0]; - var minutes = -1; - var seconds = 0; - var milliseconds = 0; - var ampm = Msgs.getMsg('timewidget_am'); - if (splitTime.length > 1) { - minutes = splitTime[1]; - } - if (splitTime.length > 2) { - var splitSecondsAndMilliseconds = splitTime[2].split('.'); - seconds = splitSecondsAndMilliseconds[0]; - if (splitSecondsAndMilliseconds.length > 1) { - milliseconds = splitSecondsAndMilliseconds[1]; - } - } - if (hours > 12) { - hours = hours - 12; - ampm = Msgs.getMsg('timewidget_pm'); - } - if (hours == 12) { - ampm = Msgs.getMsg('timewidget_pm'); - } - if (hours == 0) { - hours = 12; - ampm = Msgs.getMsg('timewidget_am'); - } - - var changeMessage = ''; - - if (shouldSpeakLabel) { - changeMessage = cvox.DomUtil.getName(this.timeElem_, true, true) + '\n'; - } - - if (hours != this.pHours_) { - changeMessage = changeMessage + hours + ' ' + - Msgs.getMsg('timewidget_hours') + '\n'; - this.pHours_ = hours; - } - - if (minutes != this.pMinutes_) { - changeMessage = changeMessage + minutes + ' ' + - Msgs.getMsg('timewidget_minutes') + '\n'; - this.pMinutes_ = minutes; - } - - if (seconds != this.pSeconds_) { - changeMessage = changeMessage + seconds + ' ' + - Msgs.getMsg('timewidget_seconds') + '\n'; - this.pSeconds_ = seconds; - } - - if (milliseconds != this.pMilliseconds_) { - changeMessage = changeMessage + milliseconds + ' ' + - Msgs.getMsg('timewidget_milliseconds') + '\n'; - this.pMilliseconds_ = milliseconds; - } - - if (ampm != this.pAmpm_) { - changeMessage = changeMessage + ampm; - this.pAmpm_ = ampm; - } - - if (changeMessage.length > 0) { - this.timeTts_.speak(changeMessage, cvox.QueueMode.FLUSH, null); - } -}; - -/** - * @param {Object} evt The event to handle. - * @private - */ -cvox.ChromeVoxHTMLTimeWidget.prototype.eventHandler_ = function(evt) { - var shouldSpeakLabel = false; - if (evt.type == 'keydown') { - if (((evt.keyCode == 9) && !evt.shiftKey) || (evt.keyCode == 39)) { - this.pos_++; - this.handlePosChange_(); - shouldSpeakLabel = true; - } - if (((evt.keyCode == 9) && evt.shiftKey) || (evt.keyCode == 37)) { - this.pos_--; - this.handlePosChange_(); - shouldSpeakLabel = true; - } - } - this.update_(shouldSpeakLabel); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_content.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_content.js deleted file mode 100644 index 4ca1ec4183c..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_content.js +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -/** - * @fileoverview A DOM traversal interface for moving a selection around a - * webpage. Provides multiple granularities: - * 1. Move by paragraph. - * 2. Move by sentence. - * 3. Move by line. - * 4. Move by word. - * 5. Move by character. - */ - -goog.provide('cvox.TraverseContent'); - -goog.require('cvox.CursorSelection'); -goog.require('cvox.DomUtil'); -goog.require('cvox.SelectionUtil'); -goog.require('cvox.TraverseUtil'); - -/** - * Moves a selection around a document or within a provided DOM object. - * - * @constructor - * @param {Node=} domObj a DOM node (optional). - */ -cvox.TraverseContent = function(domObj) { - if (domObj != null) { - this.currentDomObj = domObj; - } else { - this.currentDomObj = document.body; - } - var range = document.createRange(); - // TODO (dmazzoni): Switch this to avoid using range methods. Range methods - // can cause exceptions (such as if the node is not attached to the DOM). - try { - range.selectNode(this.currentDomObj); - this.startCursor_ = new cvox.Cursor( - range.startContainer, range.startOffset, - cvox.TraverseUtil.getNodeText(range.startContainer)); - this.endCursor_ = new cvox.Cursor( - range.endContainer, range.endOffset, - cvox.TraverseUtil.getNodeText(range.endContainer)); - } catch (e) { - // Ignoring this error so that it will not break everything else. - window.console.log('Error: Unselectable node:'); - window.console.log(domObj); - } -}; -goog.addSingletonGetter(cvox.TraverseContent); - -/** - * Whether the last navigated selection only contained whitespace. - * @type {boolean} - */ -cvox.TraverseContent.prototype.lastSelectionWasWhitespace = false; - -/** - * Whether we should skip whitespace when traversing individual characters. - * @type {boolean} - */ -cvox.TraverseContent.prototype.skipWhitespace = false; - -/** - * If moveNext and movePrev should skip past an invalid selection, - * so the user never gets stuck. Ideally the navigation code should never - * return a range that's not a valid selection, but this keeps the user from - * getting stuck if that code fails. This is set to false for unit testing. - * @type {boolean} - */ -cvox.TraverseContent.prototype.skipInvalidSelections = true; - -/** - * If line and sentence navigation should break at <a> links. - * @type {boolean} - */ -cvox.TraverseContent.prototype.breakAtLinks = true; - -/** - * The string constant for character granularity. - * @type {string} - * @const - */ -cvox.TraverseContent.kCharacter = 'character'; - -/** - * The string constant for word granularity. - * @type {string} - * @const - */ -cvox.TraverseContent.kWord = 'word'; - -/** - * The string constant for sentence granularity. - * @type {string} - * @const - */ -cvox.TraverseContent.kSentence = 'sentence'; - -/** - * The string constant for line granularity. - * @type {string} - * @const - */ -cvox.TraverseContent.kLine = 'line'; - -/** - * The string constant for paragraph granularity. - * @type {string} - * @const - */ -cvox.TraverseContent.kParagraph = 'paragraph'; - -/** - * A constant array of all granularities. - * @type {Array<string>} - * @const - */ -cvox.TraverseContent.kAllGrains = - [cvox.TraverseContent.kParagraph, - cvox.TraverseContent.kSentence, - cvox.TraverseContent.kLine, - cvox.TraverseContent.kWord, - cvox.TraverseContent.kCharacter]; - -/** - * Set the current position to match the current WebKit selection. - */ -cvox.TraverseContent.prototype.syncToSelection = function() { - this.normalizeSelection(); - - var selection = window.getSelection(); - if (!selection || !selection.anchorNode || !selection.focusNode) { - return; - } - this.startCursor_ = new cvox.Cursor( - selection.anchorNode, selection.anchorOffset, - cvox.TraverseUtil.getNodeText(selection.anchorNode)); - this.endCursor_ = new cvox.Cursor( - selection.focusNode, selection.focusOffset, - cvox.TraverseUtil.getNodeText(selection.focusNode)); -}; - -/** - * Set the start and end cursors to the selection. - * @param {cvox.CursorSelection} sel The selection. - */ -cvox.TraverseContent.prototype.syncToCursorSelection = function(sel) { - this.startCursor_ = sel.start.clone(); - this.endCursor_ = sel.end.clone(); -}; - -/** - * Get the cursor selection. - * @return {cvox.CursorSelection} The selection. - */ -cvox.TraverseContent.prototype.getCurrentCursorSelection = function() { - return new cvox.CursorSelection(this.startCursor_, this.endCursor_); -}; - -/** - * Set the WebKit selection based on the current position. - */ -cvox.TraverseContent.prototype.updateSelection = function() { - cvox.TraverseUtil.setSelection(this.startCursor_, this.endCursor_); - cvox.SelectionUtil.scrollToSelection(window.getSelection()); -}; - -/** - * Get the current position as a range. - * @return {Range} The current range. - */ -cvox.TraverseContent.prototype.getCurrentRange = function() { - var range = document.createRange(); - try { - range.setStart(this.startCursor_.node, this.startCursor_.index); - range.setEnd(this.endCursor_.node, this.endCursor_.index); - } catch (e) { - console.log('Invalid range '); - } - return range; -}; - -/** - * Get the current text content as a string. - * @return {string} The current spanned content. - */ -cvox.TraverseContent.prototype.getCurrentText = function() { - return cvox.SelectionUtil.getRangeText(this.getCurrentRange()); -}; - -/** - * Collapse to the end of the range. - */ -cvox.TraverseContent.prototype.collapseToEnd = function() { - this.startCursor_ = this.endCursor_.clone(); -}; - -/** - * Collapse to the start of the range. - */ -cvox.TraverseContent.prototype.collapseToStart = function() { - this.endCursor_ = this.startCursor_.clone(); -}; - -/** - * Moves selection forward. - * - * @param {string} grain specifies "sentence", "word", "character", - * or "paragraph" granularity. - * @return {?string} Either: - * 1) The new selected text. - * 2) null if the end of the domObj has been reached. - */ -cvox.TraverseContent.prototype.moveNext = function(grain) { - var breakTags = this.getBreakTags(); - - // As a special case, if the current selection is empty or all - // whitespace, ensure that the next returned selection will NOT be - // only whitespace - otherwise you can get trapped. - var skipWhitespace = this.skipWhitespace; - - var range = this.getCurrentRange(); - if (!cvox.SelectionUtil.isRangeValid(range)) { - skipWhitespace = true; - } - - var elementsEntered = []; - var elementsLeft = []; - var str; - do { - if (grain === cvox.TraverseContent.kSentence) { - str = cvox.TraverseUtil.getNextSentence( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - breakTags); - } else if (grain === cvox.TraverseContent.kWord) { - str = cvox.TraverseUtil.getNextWord( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft); - } else if (grain === cvox.TraverseContent.kCharacter) { - str = cvox.TraverseUtil.getNextChar( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - skipWhitespace); - } else if (grain === cvox.TraverseContent.kParagraph) { - str = cvox.TraverseUtil.getNextParagraph( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft); - } else if (grain === cvox.TraverseContent.kLine) { - str = cvox.TraverseUtil.getNextLine( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - breakTags); - } else { - // User has provided an invalid string. - // Fall through to default: extend by sentence - window.console.log('Invalid selection granularity: "' + grain + '"'); - grain = cvox.TraverseContent.kSentence; - str = cvox.TraverseUtil.getNextSentence( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - breakTags); - } - - if (str == null) { - // We reached the end of the document. - return null; - } - - range = this.getCurrentRange(); - var isInvalid = !range.getBoundingClientRect(); - } while (this.skipInvalidSelections && isInvalid); - - if (!cvox.SelectionUtil.isRangeValid(range)) { - // It's OK if the selection navigation lands on whitespace once (in - // character granularity), but if it hits whitespace more than once, then - // skip forward until there is real content. - if (!this.lastSelectionWasWhitespace && - grain == cvox.TraverseContent.kCharacter) { - this.lastSelectionWasWhitespace = true; - } else { - while (!cvox.SelectionUtil.isRangeValid(this.getCurrentRange())) { - if (this.moveNext(grain) == null) { - break; - } - } - } - } else { - this.lastSelectionWasWhitespace = false; - } - - return this.getCurrentText(); -}; - - -/** - * Moves selection backward. - * - * @param {string} grain specifies "sentence", "word", "character", - * or "paragraph" granularity. - * @return {?string} Either: - * 1) The new selected text. - * 2) null if the beginning of the domObj has been reached. - */ -cvox.TraverseContent.prototype.movePrev = function(grain) { - var breakTags = this.getBreakTags(); - - // As a special case, if the current selection is empty or all - // whitespace, ensure that the next returned selection will NOT be - // only whitespace - otherwise you can get trapped. - var skipWhitespace = this.skipWhitespace; - - var range = this.getCurrentRange(); - if (!cvox.SelectionUtil.isRangeValid(range)) { - skipWhitespace = true; - } - - var elementsEntered = []; - var elementsLeft = []; - var str; - do { - if (grain === cvox.TraverseContent.kSentence) { - str = cvox.TraverseUtil.getPreviousSentence( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - breakTags); - } else if (grain === cvox.TraverseContent.kWord) { - str = cvox.TraverseUtil.getPreviousWord( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft); - } else if (grain === cvox.TraverseContent.kCharacter) { - str = cvox.TraverseUtil.getPreviousChar( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - skipWhitespace); - } else if (grain === cvox.TraverseContent.kParagraph) { - str = cvox.TraverseUtil.getPreviousParagraph( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft); - } else if (grain === cvox.TraverseContent.kLine) { - str = cvox.TraverseUtil.getPreviousLine( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - breakTags); - } else { - // User has provided an invalid string. - // Fall through to default: extend by sentence - window.console.log('Invalid selection granularity: "' + grain + '"'); - grain = cvox.TraverseContent.kSentence; - str = cvox.TraverseUtil.getPreviousSentence( - this.startCursor_, this.endCursor_, elementsEntered, elementsLeft, - breakTags); - } - - if (str == null) { - // We reached the end of the document. - return null; - } - - range = this.getCurrentRange(); - var isInvalid = !range.getBoundingClientRect(); - } while (this.skipInvalidSelections && isInvalid); - - if (!cvox.SelectionUtil.isRangeValid(range)) { - // It's OK if the selection navigation lands on whitespace once (in - // character granularity), but if it hits whitespace more than once, then - // skip forward until there is real content. - if (!this.lastSelectionWasWhitespace && - grain == cvox.TraverseContent.kCharacter) { - this.lastSelectionWasWhitespace = true; - } else { - while (!cvox.SelectionUtil.isRangeValid(this.getCurrentRange())) { - if (this.movePrev(grain) == null) { - break; - } - } - } - } else { - this.lastSelectionWasWhitespace = false; - } - - return this.getCurrentText(); -}; - -/** - * Get the tag names that should break a sentence or line. Currently - * just an anchor 'A' should break a sentence or line if the breakAtLinks - * flag is true, but in the future we might have other rules for breaking. - * - * @return {Object} An associative array mapping a tag name to true if - * it should break a sentence or line. - */ -cvox.TraverseContent.prototype.getBreakTags = function() { - return { - 'A': this.breakAtLinks, - 'BR': true, - 'HR': true - }; -}; - -/** - * Selects the next element of the document or within the provided DOM object. - * Scrolls the window as appropriate. - * - * @param {string} grain specifies "sentence", "word", "character", - * or "paragraph" granularity. - * @param {Node=} domObj a DOM node (optional). - * @return {?string} Either: - * 1) The new selected text. - * 2) null if the end of the domObj has been reached. - */ -cvox.TraverseContent.prototype.nextElement = function(grain, domObj) { - if (domObj != null) { - this.currentDomObj = domObj; - } - - var result = this.moveNext(grain); - if (result != null && - (!cvox.DomUtil.isDescendantOfNode( - this.startCursor_.node, this.currentDomObj) || - !cvox.DomUtil.isDescendantOfNode( - this.endCursor_.node, this.currentDomObj))) { - return null; - } - - return result; -}; - - -/** - * Selects the previous element of the document or within the provided DOM - * object. Scrolls the window as appropriate. - * - * @param {string} grain specifies "sentence", "word", "character", - * or "paragraph" granularity. - * @param {Node=} domObj a DOM node (optional). - * @return {?string} Either: - * 1) The new selected text. - * 2) null if the beginning of the domObj has been reached. - */ -cvox.TraverseContent.prototype.prevElement = function(grain, domObj) { - if (domObj != null) { - this.currentDomObj = domObj; - } - - var result = this.movePrev(grain); - if (result != null && - (!cvox.DomUtil.isDescendantOfNode( - this.startCursor_.node, this.currentDomObj) || - !cvox.DomUtil.isDescendantOfNode( - this.endCursor_.node, this.currentDomObj))) { - return null; - } - - return result; -}; - -/** - * Make sure that exactly one item is selected. If there's no selection, - * set the selection to the start of the document. - */ -cvox.TraverseContent.prototype.normalizeSelection = function() { - var selection = window.getSelection(); - if (selection.rangeCount < 1) { - // Before the user has clicked a freshly-loaded page - - var range = document.createRange(); - range.setStart(this.currentDomObj, 0); - range.setEnd(this.currentDomObj, 0); - - selection.removeAllRanges(); - selection.addRange(range); - - } else if (selection.rangeCount > 1) { - // Multiple ranges exist - remove all ranges but the last one - for (var i = 0; i < (selection.rangeCount - 1); i++) { - selection.removeRange(selection.getRangeAt(i)); - } - } -}; - -/** - * Resets the selection. - * - * @param {Node=} domObj a DOM node. Optional. - * - */ -cvox.TraverseContent.prototype.reset = function(domObj) { - window.getSelection().removeAllRanges(); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_math.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_math.js deleted file mode 100644 index f14d8c29242..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_math.js +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -goog.provide('cvox.TraverseMath'); - -goog.require('cvox.ChromeVox'); -goog.require('cvox.DomUtil'); -goog.require('cvox.SemanticTree'); - - -/** - * Initializes the traversal with the provided math node. - * - * @constructor - */ -cvox.TraverseMath = function() { - /** - * The active math <MATH> node. In this context, "active" means that this is - * the math expression the TraverseMath object is navigating. - * @type {Node} - */ - this.activeMath = null; - - /** - * The node currently under inspection. - * @type {Node} - */ - this.activeNode = null; - - /** - * Dictionary of all LaTeX elements in the page if there are any. - * @type {!Object<!Node>} - * @private - */ - this.allTexs_ = {}; - - /** - * Dictionary of all MathJaxs elements in the page if there are any. - * @type {!Object<!Node>} - * @private - */ - this.allMathjaxs_ = {}; - - /** - * Dictionary of all MathJaxs elements that have not yet been translated at - * page load or during MathJax rendering. - * @type {!Object<!Node>} - * @private - */ - this.todoMathjaxs_ = {}; - - /** - * When traversing a Mathjax node this will contain the internal - * MathML representation of the node. - * @type {Node} - */ - this.activeMathmlHost = null; - - /** - * Semantic representation of the current node. - * @type {Node} - */ - this.activeSemanticHost = null; - - /** - * List of domain names. - * @type {Array<string>} - */ - this.allDomains = []; - - /** - * List of style names. - * @type {Array<string>} - */ - this.allStyles = []; - - /** - * Current domain. - * @type {string} - */ - this.domain = 'default'; - - /** - * Current style. - * @type {string} - */ - this.style = 'short'; - - /** - * Initialize special objects if necessary. - */ - if (cvox.ChromeVox.mathJax) { - this.initializeMathjaxs(); - this.initializeAltMaths(); - } -}; -goog.addSingletonGetter(cvox.TraverseMath); - - -/** - * @type {boolean} - * @private - */ -cvox.TraverseMath.setSemantic_ = false; - - -/** - * Toggles the semantic setting. - * @return {boolean} True if semantic interpretation is switched on. False - * otherwise. - */ -cvox.TraverseMath.toggleSemantic = function() { - return cvox.TraverseMath.setSemantic_ = !cvox.TraverseMath.setSemantic_; -}; - - -/** - * Initializes a traversal of a math expression. - * @param {Node} node A MathML node. - */ -cvox.TraverseMath.prototype.initialize = function(node) { - if (cvox.DomUtil.isMathImg(node)) { - // If a node has a cvoxid attribute we know that it contains a LaTeX - // expression that we have rewritten into its corresponding MathML - // representation, which we can speak and walk. - if (!node.hasAttribute('cvoxid')) { - return; - } - var cvoxid = node.getAttribute('cvoxid'); - node = this.allTexs_[cvoxid]; - } - if (cvox.DomUtil.isMathJax(node)) { - this.activeMathmlHost = this.allMathjaxs_[node.getAttribute('id')]; - } - this.activeMath = this.activeMathmlHost || node; - this.activeNode = this.activeMathmlHost || node; - if (this.activeNode && cvox.TraverseMath.setSemantic_ && - this.activeNode.nodeType == Node.ELEMENT_NODE) { - this.activeNode = - (new cvox.SemanticTree(/** @type {!Element} */ (this.activeNode))).xml(); - } -}; - - -/** - * Adds a mapping of a MathJax node to its MathML representation to the - * dictionary of MathJax elements. - * @param {!Node} mml The MathML node. - * @param {string} id The MathJax node id. - */ -cvox.TraverseMath.prototype.addMathjax = function(mml, id) { - var spanId = cvox.DomUtil.getMathSpanId(id); - if (spanId) { - this.allMathjaxs_[spanId] = mml; - } else { - this.redoMathjaxs(mml, id); - } -}; - - -/** - * Retries to compute MathML representations of MathJax elements, if - * they have not been filled in during rendering. - * @param {!Node} mml The MathML node. - * @param {string} id The MathJax node id. - */ -cvox.TraverseMath.prototype.redoMathjaxs = function(mml, id) { - var fetch = goog.bind(function() {this.addMathjax(mml, id);}, this); - setTimeout(fetch, 500); -}; - - -/** - * Initializes the MathJax to MathML mapping. - * We first try to get all MathJax elements that are already being rendered. - * Secondly, we register a signal to get updated on all elements that are - * rendered or re-rendered later. - */ -cvox.TraverseMath.prototype.initializeMathjaxs = function() { - var callback = - goog.bind(function(mml, id) { - this.addMathjax(mml, id); - }, this); - cvox.ChromeVox.mathJax.isMathjaxActive( - function(bool) { - if (bool) { - cvox.ChromeVox.mathJax.getAllJax(callback); - cvox.ChromeVox.mathJax.registerSignal(callback, 'New Math'); - } - }); -}; - - -/** - * Initializes the elements in the page that we identify as potentially - * containing tex or asciimath alt text. - */ -cvox.TraverseMath.prototype.initializeAltMaths = function() { - if (!document.querySelector( - cvox.DomUtil.altMathQuerySelector('tex') + ', ' + - cvox.DomUtil.altMathQuerySelector('asciimath'))) { - return; - } - var callback = goog.bind( - function(mml, id) { - this.allTexs_[id] = mml; - }, this); - // Inject a minimalistic version of MathJax into the page. - cvox.ChromeVox.mathJax.injectScripts(); - // Once MathJax is injected we harvest all Latex and AsciiMath in alt - // attributes and translate them to MathML expression. - cvox.ChromeVox.mathJax.isMathjaxActive( - function(active) { - if (active) { - cvox.ChromeVox.mathJax.configMediaWiki(); - cvox.ChromeVox.mathJax.getAllTexs(callback); - cvox.ChromeVox.mathJax.getAllAsciiMaths(callback); - } - }); -}; - - -/** - * Moves to the next leaf node in the current Math expression if it exists. - * @param {boolean} reverse True if reversed. False by default. - * @param {function(!Node):boolean} pred Predicate deciding what a leaf is. - * @return {Node} The next node. - */ -cvox.TraverseMath.prototype.nextLeaf = function(reverse, pred) { - if (this.activeNode && this.activeMath) { - var next = pred(this.activeNode) ? - cvox.DomUtil.directedFindNextNode( - this.activeNode, this.activeMath, reverse, pred) : - cvox.DomUtil.directedFindFirstNode(this.activeNode, reverse, pred); - if (next) { - this.activeNode = next; - } - } - return this.activeNode; -}; - - -// TODO (sorge) Refactor this logic into single walkers. -/** - * Returns a string with the content of the active node. - * @return {string} The active content. - */ -cvox.TraverseMath.prototype.activeContent = function() { - return this.activeNode.textContent; -}; - - -/** - * Moves to the next subtree from a given node in a depth first fashion. - * @param {boolean} reverse True if reversed. False by default. - * @param {function(!Node):boolean} pred Predicate deciding what a subtree is. - * @return {Node} The next subtree. - */ -cvox.TraverseMath.prototype.nextSubtree = function(reverse, pred) { - if (!this.activeNode || !this.activeMath) { - return null; - } - if (!reverse) { - var child = cvox.DomUtil.directedFindFirstNode( - this.activeNode, reverse, pred); - if (child) { - this.activeNode = child; - } else { - var next = cvox.DomUtil.directedFindNextNode( - this.activeNode, this.activeMath, reverse, pred); - if (next) { - this.activeNode = next; - } - } - } else { - if (this.activeNode == this.activeMath) { - var child = cvox.DomUtil.directedFindDeepestNode( - this.activeNode, reverse, pred); - if (child != this.activeNode) { - this.activeNode = child; - return this.activeNode; - } - } - var prev = cvox.DomUtil.directedFindNextNode( - this.activeNode, this.activeMath, reverse, pred, true, true); - if (prev) { - this.activeNode = prev; - } - } - return this.activeNode; -}; - - -/** - * left or right in the math expression. - * Navigation is bounded by the presence of a sibling. - * @param {boolean} r True to move left; false to move right. - * @return {Node} The result. - */ -cvox.TraverseMath.prototype.nextSibling = function(r) { - if (!this.activeNode || !this.activeMath) { - return null; - } - var node = this.activeNode; - node = r ? node.previousSibling : node.nextSibling; - if (!node) { - return null; - } - this.activeNode = node; - return this.activeNode; -}; - - -/** - * Moves up or down the math expression. - * Navigation is bounded by the root math expression. - * @param {boolean} r True to move up; false to move down. - * @return {Node} The result. - */ -cvox.TraverseMath.prototype.nextParentChild = function(r) { - if (!this.activeNode || !this.activeMath) { - return null; - } - if (this.activeNode == this.activeMath && r) { - return null; - } - var node = this.activeNode; - node = r ? node.parentNode : node.firstChild; - if (!node) { - return null; - } - this.activeNode = node; - return this.activeNode; -}; - - -/** - * Adds a list of domains and styles to the existing one. - * @param {Array<string>} domains List of domain names. - * @param {Array<string>} styles List of style names. - */ -cvox.TraverseMath.prototype.addDomainsAndStyles = function(domains, styles) { - this.allDomains.push.apply( - this.allDomains, - domains.filter( - goog.bind(function(x) {return this.allDomains.indexOf(x) < 0;}, - this))); - this.allStyles.push.apply( - this.allStyles, - styles.filter( - goog.bind(function(x) {return this.allStyles.indexOf(x) < 0;}, - this))); -}; - - -/** - * Gets a list of domains and styles from the symbol and function mappings. - * Depending on the platform they either live in the background page or - * in the android math map. - */ -cvox.TraverseMath.prototype.initDomainsAndStyles = function() { - if (cvox.ChromeVox.host['mathMap']) { - this.addDomainsAndStyles( - cvox.ChromeVox.host['mathMap'].allDomains, - cvox.ChromeVox.host['mathMap'].allStyles); - } else { - cvox.ChromeVox.host.sendToBackgroundPage( - {'target': 'Math', - 'action': 'getDomains'}); - } -}; - - -/** - * Sets the domain for the TraverseMath object to the next one in the list - * restarting from the first, if necessary. - * @return {string} The name of the newly set domain. - */ -cvox.TraverseMath.prototype.cycleDomain = function() { - this.initDomainsAndStyles(); - var index = this.allDomains.indexOf(this.domain); - if (index == -1) { - return this.domain; - } - this.domain = this.allDomains[(++index) % this.allDomains.length]; - return this.domain; -}; - - -/** - * Sets the style for the TraverseMath object to the next one in the list - * restarting from the first, if necessary. - * @return {string} The name of the newly set style. - */ -cvox.TraverseMath.prototype.cycleStyle = function() { - this.initDomainsAndStyles(); - var index = this.allStyles.indexOf(this.style); - if (index == -1) { - return this.domain; - } - this.style = this.allStyles[(++index) % this.allStyles.length]; - return this.style; -}; - - -/** - * Sets the domain for the TraverseMath object. - * @param {string} domain Name of the domain. - * @private - */ -cvox.TraverseMath.prototype.setDomain_ = function(domain) { - if (this.allDomains.indexOf(domain) != -1) { - this.domain = domain; - } else { - this.domain = 'default'; - } -}; - - -/** - * Sets the style for the TraverseMath object. - * @param {string} style Name of the style. - * @private - */ -cvox.TraverseMath.prototype.setStyle_ = function(style) { - if (this.allStyles.indexOf(style) != -1) { - this.style = style; - } else { - this.style = 'default'; - } -}; - - -/** - * Gets the active node attached to the current document. - * @return {Node} The active node, if it exists. - */ -cvox.TraverseMath.prototype.getAttachedActiveNode = function() { - var node = this.activeNode; - if (!node || node.nodeType != Node.ELEMENT_NODE) { - return null; - } - var id = node.getAttribute('spanID'); - return document.getElementById(id); -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js deleted file mode 100644 index 8927c1d8846..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_table.js +++ /dev/null @@ -1,1309 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * TODO(stoarca): This class has become obsolete except for the shadow table. - * Chop most of it away. - * @fileoverview A DOM traversal interface for navigating data in tables. - */ - -goog.provide('cvox.TraverseTable'); - -goog.require('cvox.DomPredicates'); -goog.require('cvox.DomUtil'); -goog.require('cvox.SelectionUtil'); -goog.require('cvox.TableUtil'); -goog.require('cvox.TraverseUtil'); - - - -/** - * An object that represents an active table cell inside the shadow table. - * @constructor - */ -function ShadowTableNode() { - /** - * The cells that are row headers of the corresponding active table cell - * @type {!Array} - */ - this.rowHeaderCells = []; - - /** - * The cells that are column headers of the corresponding active table cell - * @type {!Array} - */ - this.colHeaderCells = []; -} - - -/** - * Whether or not the active cell is spanned by a preceding cell. - * @type {boolean} - */ -ShadowTableNode.prototype.spanned; - - -/** - * Whether or not this cell is spanned by a rowSpan. - * @type {?boolean} - */ -ShadowTableNode.prototype.rowSpan; - - -/** - * Whether or not this cell is spanned by a colspan - * @type {?boolean} - */ -ShadowTableNode.prototype.colSpan; - - -/** - * The row index of the corresponding active table cell - * @type {?number} - */ -ShadowTableNode.prototype.i; - - -/** - * The column index of the corresponding active table cell - * @type {?number} - */ -ShadowTableNode.prototype.j; - - -/** - * The corresponding <TD> or <TH> node in the active table. - * @type {?Node} - */ -ShadowTableNode.prototype.activeCell; - - -/** - * Initializes the traversal with the provided table node. - * - * @constructor - * @param {Node} tableNode The table to be traversed. - */ -cvox.TraverseTable = function(tableNode) { - - /** - * The active table <TABLE> node. In this context, "active" means that this is - * the table the TraverseTable object is navigating. - * @type {Node} - * @private - */ - this.activeTable_ = null; - - /** - * A 2D array "shadow table" that contains pointers to nodes in the active - * table. More specifically, each cell of the shadow table contains a special - * object ShadowTableNode that has as one of its member variables the - * corresponding cell in the active table. - * - * The shadow table will allow us efficient navigation of tables with - * rowspans and colspans without needing to repeatedly scan the table. For - * example, if someone requests a cell at (1,3), predecessor cells with - * rowspans/colspans mean the cell you eventually return could actually be - * one located at (0,2) that spans out to (1,3). - * - * This shadow table will contain a ShadowTableNode with the (0, 2) index at - * the (1,3) position, eliminating the need to check for predecessor cells - * with rowspan/colspan every time we traverse the table. - * - * @type {!Array<Array<ShadowTableNode>>} - * @private - */ - this.shadowTable_ = []; - - /** - * An array of shadow table nodes that have been determined to contain header - * cells or information about header cells. This array is collected at - * initialization and then only recalculated if the table changes. - * This array is used by findHeaderCells() to determine table row headers - * and column headers. - * @type {Array<ShadowTableNode>} - * @private - */ - this.candidateHeaders_ = []; - - /** - * An array that associates cell IDs with their corresponding shadow nodes. - * If there are two shadow nodes for the same cell (i.e. when a cell spans - * other cells) then the first one will be associated with the ID. This means - * that shadow nodes that have spanned set to true will not be included in - * this array. - * @type {Array<ShadowTableNode>} - * @private - */ - this.idToShadowNode_ = []; - - this.initialize(tableNode); -}; - - -/** - * The cell cursor, represented by an array that stores the row and - * column location [i, j] of the active cell. These numbers are 0-based. - * In this context, "active" means that this is the cell the user is - * currently looking at. - * @type {Array} - */ -cvox.TraverseTable.prototype.currentCellCursor; - - -/** - * The number of columns in the active table. This is calculated at - * initialization and then only recalculated if the table changes. - * - * Please Note: We have chosen to use the number of columns in the shadow - * table as the canonical column count. This is important for tables that - * have colspans - the number of columns in the active table will always be - * less than the true number of columns. - * @type {?number} - */ -cvox.TraverseTable.prototype.colCount = null; - - -/** - * The number of rows in the active table. This is calculated at - * initialization and then only recalculated if the table changes. - * @type {?number} - */ -cvox.TraverseTable.prototype.rowCount = null; - - -/** - * The row headers in the active table. This is calculated at - * initialization and then only recalculated if the table changes. - * - * Please Note: - * Row headers are defined here as <TH> or <TD> elements. <TD> elements when - * serving as header cells must have either: - * - The scope attribute defined - * - Their IDs referenced in the header content attribute of another <TD> or - * <TH> element. - * - * The HTML5 spec specifies that only header <TH> elements can be row headers - * ( http://dev.w3.org/html5/spec/tabular-data.html#row-header ) but the - * HTML4 spec says that <TD> elements can act as both - * ( http://www.w3.org/TR/html401/struct/tables.html#h-11.2.6 ). In the - * interest of providing meaningful header information for all tables, here - * we take the position that <TD> elements can act as both. - * - * @type {Array} - */ -cvox.TraverseTable.prototype.tableRowHeaders = null; - - -/** - * The column headers in the active table. This is calculated at - * initialization and then only recalculated if the table changes. - * - * Please Note: see comment for tableRowHeaders. - * - * @type {Array} - */ -cvox.TraverseTable.prototype.tableColHeaders = null; - - -// TODO (stoarca): tighten up interface to {!Node} -/** - * Initializes the class member variables. - * @param {Node} tableNode The table to be traversed. - */ -cvox.TraverseTable.prototype.initialize = function(tableNode) { - if (!tableNode) { - return; - } - if (tableNode == this.activeTable_) { - return; - } - this.activeTable_ = tableNode; - this.currentCellCursor = null; - - this.tableRowHeaders = []; - this.tableColHeaders = []; - - this.buildShadowTable_(); - - this.colCount = this.shadowColCount_(); - this.rowCount = this.countRows_(); - - this.findHeaderCells_(); - - // Listen for changes to the active table. If the active table changes, - // rebuild the shadow table. - // TODO (stoarca): Is this safe? When this object goes away, doesn't the - // eventListener stay on the node? Someone with better knowledge of js - // please confirm. If so, this is a leak. - this.activeTable_.addEventListener('DOMSubtreeModified', - goog.bind(function() { - this.buildShadowTable_(); - this.colCount = this.shadowColCount_(); - this.rowCount = this.countRows_(); - - this.tableRowHeaders = []; - this.tableColHeaders = []; - this.findHeaderCells_(); - - if (this.colCount == 0 && this.rowCount == 0) { - return; - } - - if (this.getCell() == null) { - this.attachCursorToNearestCell_(); - } - }, this), false); -}; - - -/** - * Finds the cell cursor containing the specified node within the table. - * Returns null if there is no close cell. - * @param {!Node} node The node for which to find the cursor. - * @return {Array<number>} The table index for the node. - */ -cvox.TraverseTable.prototype.findNearestCursor = function(node) { - // TODO (stoarca): The current structure for representing the - // shadow table is not optimal for this query, but it's not urgent - // since this only gets executed at most once per user action. - - // In case node is in a table but above any individual cell, we go down as - // deep as we can, being careful to avoid going into nested tables. - var n = node; - - while (n.firstElementChild && - !(n.firstElementChild.tagName == 'TABLE' || - cvox.AriaUtil.isGrid(n.firstElementChild))) { - n = n.firstElementChild; - } - while (!cvox.DomPredicates.cellPredicate(cvox.DomUtil.getAncestors(n))) { - n = cvox.DomUtil.directedNextLeafNode(n); - // TODO(stoarca): Ugly logic. Captions should be part of tables. - // There have been a bunch of bugs as a result of - // DomUtil.findTableNodeInList excluding captions from tables because - // it makes them non-contiguous. - if (!cvox.DomUtil.getContainingTable(n, {allowCaptions: true})) { - return null; - } - } - for (var i = 0; i < this.rowCount; ++i) { - for (var j = 0; j < this.colCount; ++j) { - if (this.shadowTable_[i][j]) { - if (cvox.DomUtil.isDescendantOfNode( - n, this.shadowTable_[i][j].activeCell)) { - return [i, j]; - } - } - } - } - return null; -}; - -/** - * Finds the valid cell nearest to the current cell cursor and moves the cell - * cursor there. To be used when the table has changed and the current cell - * cursor is now invalid (doesn't exist anymore). - * @private - */ -cvox.TraverseTable.prototype.attachCursorToNearestCell_ = function() { - if (!this.currentCellCursor) { - // We have no idea. Just go 'somewhere'. Other code paths in this - // function go to the last cell, so let's do that! - this.goToLastCell(); - return; - } - - var currentCursor = this.currentCellCursor; - - // Does the current row still exist in the table? - var currentRow = this.shadowTable_[currentCursor[0]]; - if (currentRow) { - // Try last cell of current row - this.currentCellCursor = [currentCursor[0], (currentRow.length - 1)]; - } else { - // Current row does not exist anymore. Does current column still exist? - // Try last cell of current column - var numRows = this.shadowTable_.length; - if (numRows == 0) { - // Table has been deleted! - this.currentCellCursor = null; - return; - } - var aboveCell = - this.shadowTable_[numRows - 1][currentCursor[1]]; - if (aboveCell) { - this.currentCellCursor = [(numRows - 1), currentCursor[1]]; - } else { - // Current column does not exist anymore either. - // Move cursor to last cell in table. - this.goToLastCell(); - } - } -}; - - -/** - * Builds or rebuilds the shadow table by iterating through all of the cells - * ( <TD> or <TH> or role='gridcell' nodes) of the active table. - * @return {!Array} The shadow table. - * @private - */ -cvox.TraverseTable.prototype.buildShadowTable_ = function() { - // Clear shadow table - this.shadowTable_ = []; - - // Build shadow table structure. Initialize it as a 2D array. - var allRows = cvox.TableUtil.getChildRows(this.activeTable_); - var currentRowParent = null; - var currentRowGroup = null; - - var colGroups = cvox.TableUtil.getColGroups(this.activeTable_); - var colToColGroup = cvox.TableUtil.determineColGroups(colGroups); - - for (var ctr = 0; ctr < allRows.length; ctr++) { - this.shadowTable_.push([]); - } - - // Iterate through active table by row - for (var i = 0; i < allRows.length; i++) { - var childCells = cvox.TableUtil.getChildCells(allRows[i]); - - // Keep track of position in active table - var activeTableCol = 0; - // Keep track of position in shadow table - var shadowTableCol = 0; - - while (activeTableCol < childCells.length) { - - // Check to make sure we haven't already filled this cell. - if (this.shadowTable_[i][shadowTableCol] == null) { - - var activeTableCell = childCells[activeTableCol]; - - // Default value for colspan and rowspan is 1 - var colsSpanned = 1; - var rowsSpanned = 1; - - if (activeTableCell.hasAttribute('colspan')) { - - colsSpanned = - parseInt(activeTableCell.getAttribute('colspan'), 10); - - if ((isNaN(colsSpanned)) || (colsSpanned <= 0)) { - // The HTML5 spec defines colspan MUST be greater than 0: - // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-colspan - // - // This is a change from the HTML4 spec: - // http://www.w3.org/TR/html401/struct/tables.html#adef-colspan - // - // We will degrade gracefully by treating a colspan=0 as - // equivalent to a colspan=1. - // Tested in method testColSpan0 in rowColSpanTable_test.js - colsSpanned = 1; - } - } - if (activeTableCell.hasAttribute('rowspan')) { - rowsSpanned = - parseInt(activeTableCell.getAttribute('rowspan'), 10); - - if ((isNaN(rowsSpanned)) || (rowsSpanned <= 0)) { - // The HTML5 spec defines that rowspan can be any non-negative - // integer, including 0: - // http://dev.w3.org/html5/spec/Overview.html#attr-tdth-rowspan - // - // However, Chromium treats rowspan=0 as rowspan=1. This appears - // to be a bug from WebKit: - // https://bugs.webkit.org/show_bug.cgi?id=10300 - // Inherited from a bug (since fixed) in KDE: - // http://bugs.kde.org/show_bug.cgi?id=41063 - // - // We will follow Chromium and treat rowspan=0 as equivalent to - // rowspan=1. - // - // Tested in method testRowSpan0 in rowColSpanTable_test.js - // - // Filed as a bug in Chromium: http://crbug.com/58223 - rowsSpanned = 1; - } - } - for (var r = 0; r < rowsSpanned; r++) { - for (var c = 0; c < colsSpanned; c++) { - var shadowNode = new ShadowTableNode(); - if ((r == 0) && (c == 0)) { - // This position is not spanned. - shadowNode.spanned = false; - shadowNode.rowSpan = false; - shadowNode.colSpan = false; - shadowNode.i = i; - shadowNode.j = shadowTableCol; - shadowNode.activeCell = activeTableCell; - shadowNode.rowHeaderCells = []; - shadowNode.colHeaderCells = []; - shadowNode.isRowHeader = false; - shadowNode.isColHeader = false; - } else { - // This position is spanned. - shadowNode.spanned = true; - shadowNode.rowSpan = (rowsSpanned > 1); - shadowNode.colSpan = (colsSpanned > 1); - shadowNode.i = i; - shadowNode.j = shadowTableCol; - shadowNode.activeCell = activeTableCell; - shadowNode.rowHeaderCells = []; - shadowNode.colHeaderCells = []; - shadowNode.isRowHeader = false; - shadowNode.isColHeader = false; - } - // Check this shadowNode to see if it is a candidate header cell - if (cvox.TableUtil.checkIfHeader(shadowNode.activeCell)) { - this.candidateHeaders_.push(shadowNode); - } else if (shadowNode.activeCell.hasAttribute('headers')) { - // This shadowNode has information about other header cells - this.candidateHeaders_.push(shadowNode); - } - - // Check and update row group status. - if (currentRowParent == null) { - // This is the first row - currentRowParent = allRows[i].parentNode; - currentRowGroup = 0; - } else { - if (allRows[i].parentNode != currentRowParent) { - // We're in a different row group now - currentRowParent = allRows[i].parentNode; - currentRowGroup = currentRowGroup + 1; - } - } - shadowNode.rowGroup = currentRowGroup; - - // Check and update col group status - if (colToColGroup.length > 0) { - shadowNode.colGroup = colToColGroup[shadowTableCol]; - } else { - shadowNode.colGroup = 0; - } - - if (! shadowNode.spanned) { - if (activeTableCell.id != null) { - this.idToShadowNode_[activeTableCell.id] = shadowNode; - } - } - - this.shadowTable_[i + r][shadowTableCol + c] = shadowNode; - } - } - shadowTableCol += colsSpanned; - activeTableCol++; - } else { - // This position has already been filled (by a previous cell that has - // a colspan or a rowspan) - shadowTableCol += 1; - } - } - } - return this.shadowTable_; -}; - - -/** - * Finds header cells from the list of candidate headers and classifies them - * in two ways: - * -- Identifies them for the entire table by adding them to - * this.tableRowHeaders and this.tableColHeaders. - * -- Identifies them for each shadow table node by adding them to the node's - * rowHeaderCells or colHeaderCells arrays. - * - * @private - */ -cvox.TraverseTable.prototype.findHeaderCells_ = function() { - // Forming relationships between data cells and header cells: - // http://dev.w3.org/html5/spec/tabular-data.html - // #header-and-data-cell-semantics - - for (var i = 0; i < this.candidateHeaders_.length; i++) { - - var currentShadowNode = this.candidateHeaders_[i]; - var currentCell = currentShadowNode.activeCell; - - var assumedScope = null; - var specifiedScope = null; - - if (currentShadowNode.spanned) { - continue; - } - - if ((currentCell.tagName == 'TH') && - !(currentCell.hasAttribute('scope'))) { - // No scope specified - compute scope ourselves. - // Go left/right - if there's a header node, then this is a column - // header - if (currentShadowNode.j > 0) { - if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j - 1]. - activeCell.tagName == 'TH') { - assumedScope = 'col'; - } - } else if (currentShadowNode.j < this.shadowTable_[currentShadowNode.i]. - length - 1) { - if (this.shadowTable_[currentShadowNode.i][currentShadowNode.j + 1]. - activeCell.tagName == 'TH') { - assumedScope = 'col'; - } - } else { - // This row has a width of 1 cell, just assume this is a colum header - assumedScope = 'col'; - } - - if (assumedScope == null) { - // Go up/down - if there's a header node, then this is a row header - if (currentShadowNode.i > 0) { - if (this.shadowTable_[currentShadowNode.i - 1][currentShadowNode.j]. - activeCell.tagName == 'TH') { - assumedScope = 'row'; - } - } else if (currentShadowNode.i < this.shadowTable_.length - 1) { - if (this.shadowTable_[currentShadowNode.i + 1][currentShadowNode.j]. - activeCell.tagName == 'TH') { - assumedScope = 'row'; - } - } else { - // This column has a height of 1 cell, just assume that this is - // a row header - assumedScope = 'row'; - } - } - } else if (currentCell.hasAttribute('scope')) { - specifiedScope = currentCell.getAttribute('scope'); - } else if (currentCell.hasAttribute('role') && - (currentCell.getAttribute('role') == 'rowheader')) { - specifiedScope = 'row'; - } else if (currentCell.hasAttribute('role') && - (currentCell.getAttribute('role') == 'columnheader')) { - specifiedScope = 'col'; - } - - if ((specifiedScope == 'row') || (assumedScope == 'row')) { - currentShadowNode.isRowHeader = true; - - // Go right until you hit the edge of the table or a data - // cell after another header cell. - // Add this cell to each shadowNode.rowHeaderCells attribute as you go. - for (var rightCtr = currentShadowNode.j; - rightCtr < this.shadowTable_[currentShadowNode.i].length; - rightCtr++) { - - var rightShadowNode = this.shadowTable_[currentShadowNode.i][rightCtr]; - var rightCell = rightShadowNode.activeCell; - - if ((rightCell.tagName == 'TH') || - (rightCell.hasAttribute('scope'))) { - - if (rightCtr < this.shadowTable_[currentShadowNode.i].length - 1) { - var checkDataCell = - this.shadowTable_[currentShadowNode.i][rightCtr + 1]; - } - } - rightShadowNode.rowHeaderCells.push(currentCell); - } - this.tableRowHeaders.push(currentCell); - } else if ((specifiedScope == 'col') || (assumedScope == 'col')) { - currentShadowNode.isColHeader = true; - - // Go down until you hit the edge of the table or a data cell - // after another header cell. - // Add this cell to each shadowNode.colHeaders attribute as you go. - - for (var downCtr = currentShadowNode.i; - downCtr < this.shadowTable_.length; - downCtr++) { - - var downShadowNode = this.shadowTable_[downCtr][currentShadowNode.j]; - if (downShadowNode == null) { - break; - } - var downCell = downShadowNode.activeCell; - - if ((downCell.tagName == 'TH') || - (downCell.hasAttribute('scope'))) { - - if (downCtr < this.shadowTable_.length - 1) { - var checkDataCell = - this.shadowTable_[downCtr + 1][currentShadowNode.j]; - } - } - downShadowNode.colHeaderCells.push(currentCell); - } - this.tableColHeaders.push(currentCell); - } else if (specifiedScope == 'rowgroup') { - currentShadowNode.isRowHeader = true; - - // This cell is a row header for the rest of the cells in this row group. - var currentRowGroup = currentShadowNode.rowGroup; - - // Get the rest of the cells in this row first - for (var cellsInRow = currentShadowNode.j + 1; - cellsInRow < this.shadowTable_[currentShadowNode.i].length; - cellsInRow++) { - this.shadowTable_[currentShadowNode.i][cellsInRow]. - rowHeaderCells.push(currentCell); - } - - // Now propagate to rest of row group - for (var downCtr = currentShadowNode.i + 1; - downCtr < this.shadowTable_.length; - downCtr++) { - - if (this.shadowTable_[downCtr][0].rowGroup != currentRowGroup) { - break; - } - - for (var rightCtr = 0; - rightCtr < this.shadowTable_[downCtr].length; - rightCtr++) { - - this.shadowTable_[downCtr][rightCtr]. - rowHeaderCells.push(currentCell); - } - } - this.tableRowHeaders.push(currentCell); - - } else if (specifiedScope == 'colgroup') { - currentShadowNode.isColHeader = true; - - // This cell is a col header for the rest of the cells in this col group. - var currentColGroup = currentShadowNode.colGroup; - - // Get the rest of the cells in this colgroup first - for (var cellsInCol = currentShadowNode.j + 1; - cellsInCol < this.shadowTable_[currentShadowNode.i].length; - cellsInCol++) { - if (this.shadowTable_[currentShadowNode.i][cellsInCol].colGroup == - currentColGroup) { - this.shadowTable_[currentShadowNode.i][cellsInCol]. - colHeaderCells.push(currentCell); - } - } - - // Now propagate to rest of col group - for (var downCtr = currentShadowNode.i + 1; - downCtr < this.shadowTable_.length; - downCtr++) { - - for (var rightCtr = 0; - rightCtr < this.shadowTable_[downCtr].length; - rightCtr++) { - - if (this.shadowTable_[downCtr][rightCtr].colGroup == - currentColGroup) { - this.shadowTable_[downCtr][rightCtr]. - colHeaderCells.push(currentCell); - } - } - } - this.tableColHeaders.push(currentCell); - } - if (currentCell.hasAttribute('headers')) { - this.findAttrbHeaders_(currentShadowNode); - } - if (currentCell.hasAttribute('aria-describedby')) { - this.findAttrbDescribedBy_(currentShadowNode); - } - } -}; - - -/** - * Finds header cells from the 'headers' attribute of a given shadow node's - * active cell and classifies them in two ways: - * -- Identifies them for the entire table by adding them to - * this.tableRowHeaders and this.tableColHeaders. - * -- Identifies them for the shadow table node by adding them to the node's - * rowHeaderCells or colHeaderCells arrays. - * Please note that header cells found through the 'headers' attribute are - * difficult to attribute to being either row or column headers because a - * table cell can declare arbitrary cells as its headers. A guess is made here - * based on which axis the header cell is closest to. - * - * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell - * that has a 'headers' attribute. - * - * @private - */ -cvox.TraverseTable.prototype.findAttrbHeaders_ = function(currentShadowNode) { - var activeTableCell = currentShadowNode.activeCell; - - var idList = activeTableCell.getAttribute('headers').split(' '); - for (var idToken = 0; idToken < idList.length; idToken++) { - // Find cell(s) with this ID, add to header list - var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_, - idList[idToken]); - - for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { - if (idCellArray[idCtr].id == activeTableCell.id) { - // Skip if the ID is the same as the current cell's ID - break; - } - // Check if this list of candidate headers contains a - // shadowNode with an active cell with this ID already - var possibleHeaderNode = - this.idToShadowNode_[idCellArray[idCtr].id]; - if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { - // This listed header cell will not be handled later. - // Determine whether this is a row or col header for - // the active table cell - - var iDiff = Math.abs(possibleHeaderNode.i - currentShadowNode.i); - var jDiff = Math.abs(possibleHeaderNode.j - currentShadowNode.j); - if ((iDiff == 0) || (iDiff < jDiff)) { - cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells, - possibleHeaderNode.activeCell); - cvox.TableUtil.pushIfNotContained(this.tableRowHeaders, - possibleHeaderNode.activeCell); - } else { - // This is a column header - cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells, - possibleHeaderNode.activeCell); - cvox.TableUtil.pushIfNotContained(this.tableColHeaders, - possibleHeaderNode.activeCell); - } - } - } - } -}; - - -/** - * Finds header cells from the 'aria-describedby' attribute of a given shadow - * node's active cell and classifies them in two ways: - * -- Identifies them for the entire table by adding them to - * this.tableRowHeaders and this.tableColHeaders. - * -- Identifies them for the shadow table node by adding them to the node's - * rowHeaderCells or colHeaderCells arrays. - * - * Please note that header cells found through the 'aria-describedby' attribute - * must have the role='rowheader' or role='columnheader' attributes in order to - * be considered header cells. - * - * @param {ShadowTableNode} currentShadowNode A shadow node with an active cell - * that has an 'aria-describedby' attribute. - * - * @private - */ -cvox.TraverseTable.prototype.findAttrbDescribedBy_ = - function(currentShadowNode) { - var activeTableCell = currentShadowNode.activeCell; - - var idList = activeTableCell.getAttribute('aria-describedby').split(' '); - for (var idToken = 0; idToken < idList.length; idToken++) { - // Find cell(s) with this ID, add to header list - var idCellArray = cvox.TableUtil.getCellWithID(this.activeTable_, - idList[idToken]); - - for (var idCtr = 0; idCtr < idCellArray.length; idCtr++) { - if (idCellArray[idCtr].id == activeTableCell.id) { - // Skip if the ID is the same as the current cell's ID - break; - } - // Check if this list of candidate headers contains a - // shadowNode with an active cell with this ID already - var possibleHeaderNode = - this.idToShadowNode_[idCellArray[idCtr].id]; - if (! cvox.TableUtil.checkIfHeader(possibleHeaderNode.activeCell)) { - // This listed header cell will not be handled later. - // Determine whether this is a row or col header for - // the active table cell - - if (possibleHeaderNode.activeCell.hasAttribute('role') && - (possibleHeaderNode.activeCell.getAttribute('role') == - 'rowheader')) { - cvox.TableUtil.pushIfNotContained(currentShadowNode.rowHeaderCells, - possibleHeaderNode.activeCell); - cvox.TableUtil.pushIfNotContained(this.tableRowHeaders, - possibleHeaderNode.activeCell); - } else if (possibleHeaderNode.activeCell.hasAttribute('role') && - (possibleHeaderNode.activeCell.getAttribute('role') == - 'columnheader')) { - cvox.TableUtil.pushIfNotContained(currentShadowNode.colHeaderCells, - possibleHeaderNode.activeCell); - cvox.TableUtil.pushIfNotContained(this.tableColHeaders, - possibleHeaderNode.activeCell); - } - } - } - } -}; - - -/** - * Gets the current cell or null if there is no current cell. - * @return {?Node} The cell <TD> or <TH> or role='gridcell' node. - */ -cvox.TraverseTable.prototype.getCell = function() { - if (!this.currentCellCursor || !this.shadowTable_) { - return null; - } - - var shadowEntry = - this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; - - return shadowEntry && shadowEntry.activeCell; -}; - - -/** - * Gets the cell at the specified location. - * @param {Array<number>} index The index <i, j> of the required cell. - * @return {?Node} The cell <TD> or <TH> or role='gridcell' node at the - * specified location. Null if that cell does not exist. - */ -cvox.TraverseTable.prototype.getCellAt = function(index) { - if (((index[0] < this.rowCount) && (index[0] >= 0)) && - ((index[1] < this.colCount) && (index[1] >= 0))) { - var shadowEntry = this.shadowTable_[index[0]][index[1]]; - if (shadowEntry != null) { - return shadowEntry.activeCell; - } - } - return null; -}; - - -/** - * Gets the cells that are row headers of the current cell. - * @return {!Array} The cells that are row headers of the current cell. Empty if - * the current cell does not have row headers. - */ -cvox.TraverseTable.prototype.getCellRowHeaders = function() { - var shadowEntry = - this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; - - return shadowEntry.rowHeaderCells; -}; - - -/** - * Gets the cells that are col headers of the current cell. - * @return {!Array} The cells that are col headers of the current cell. Empty if - * the current cell does not have col headers. - */ -cvox.TraverseTable.prototype.getCellColHeaders = function() { - var shadowEntry = - this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; - - return shadowEntry.colHeaderCells; -}; - - -/** - * Whether or not the current cell is spanned by another cell. - * @return {boolean} Whether or not the current cell is spanned by another cell. - */ -cvox.TraverseTable.prototype.isSpanned = function() { - var shadowEntry = - this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; - - return shadowEntry.spanned; -}; - - -/** - * Whether or not the current cell is a row header cell. - * @return {boolean} Whether or not the current cell is a row header cell. - */ -cvox.TraverseTable.prototype.isRowHeader = function() { - var shadowEntry = - this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; - - return shadowEntry.isRowHeader; -}; - - -/** - * Whether or not the current cell is a col header cell. - * @return {boolean} Whether or not the current cell is a col header cell. - */ -cvox.TraverseTable.prototype.isColHeader = function() { - var shadowEntry = - this.shadowTable_[this.currentCellCursor[0]][this.currentCellCursor[1]]; - - return shadowEntry.isColHeader; -}; - - -/** - * Gets the active column, represented as an array of <TH> or <TD> nodes that - * make up a column. In this context, "active" means that this is the column - * that contains the cell the user is currently looking at. - * @return {Array} An array of <TH> or <TD> or role='gridcell' nodes. - */ -cvox.TraverseTable.prototype.getCol = function() { - var colArray = []; - for (var i = 0; i < this.shadowTable_.length; i++) { - - if (this.shadowTable_[i][this.currentCellCursor[1]]) { - var shadowEntry = this.shadowTable_[i][this.currentCellCursor[1]]; - - if (shadowEntry.colSpan && shadowEntry.rowSpan) { - // Look at the last element in the column cell aray. - var prev = colArray[colArray.length - 1]; - if (prev != - shadowEntry.activeCell) { - // Watch out for positions spanned by a cell with rowspan and - // colspan. We don't want the same cell showing up multiple times - // in per-column cell lists. - colArray.push( - shadowEntry.activeCell); - } - } else if ((shadowEntry.colSpan) || (!shadowEntry.rowSpan)) { - colArray.push( - shadowEntry.activeCell); - } - } - } - return colArray; -}; - - -/** - * Gets the active row <TR> node. In this context, "active" means that this is - * the row that contains the cell the user is currently looking at. - * @return {Node} The active row node. - */ -cvox.TraverseTable.prototype.getRow = function() { - var childRows = cvox.TableUtil.getChildRows(this.activeTable_); - return childRows[this.currentCellCursor[0]]; -}; - - -/** - * Gets the table summary text. - * - * @return {?string} Either: - * 1) The table summary text - * 2) Null if the table does not contain a summary attribute. - */ -cvox.TraverseTable.prototype.summaryText = function() { - // see http://code.google.com/p/chromium/issues/detail?id=46567 - // for information why this is necessary - if (!this.activeTable_.hasAttribute('summary')) { - return null; - } - return this.activeTable_.getAttribute('summary'); -}; - - -/** - * Gets the table caption text. - * - * @return {?string} Either: - * 1) The table caption text - * 2) Null if the table does not include a caption tag. - */ -cvox.TraverseTable.prototype.captionText = function() { - // If there's more than one outer <caption> element, choose the first one. - var captionNodes = cvox.XpathUtil.evalXPath('caption\[1]', - this.activeTable_); - if (captionNodes.length > 0) { - return captionNodes[0].innerHTML; - } else { - return null; - } -}; - - -/** - * Calculates the number of columns in the shadow table. - * @return {number} The number of columns in the shadow table. - * @private - */ -cvox.TraverseTable.prototype.shadowColCount_ = function() { - // As the shadow table is a 2D array, the number of columns is the - // max number of elements in the second-level arrays. - var max = 0; - for (var i = 0; i < this.shadowTable_.length; i++) { - if (this.shadowTable_[i].length > max) { - max = this.shadowTable_[i].length; - } - } - return max; -}; - - -/** - * Calculates the number of rows in the table. - * @return {number} The number of rows in the table. - * @private - */ -cvox.TraverseTable.prototype.countRows_ = function() { - // Number of rows in a table is equal to the number of TR elements contained - // by the (outer) TBODY elements. - var rowCount = cvox.TableUtil.getChildRows(this.activeTable_); - return rowCount.length; -}; - - -/** - * Calculates the number of columns in the table. - * This uses the W3C recommended algorithm for calculating number of - * columns, but it does not take rowspans or colspans into account. This means - * that the number of columns calculated here might be lower than the actual - * number of columns in the table if columns are indicated by colspans. - * @return {number} The number of columns in the table. - * @private - */ -cvox.TraverseTable.prototype.getW3CColCount_ = function() { - // See http://www.w3.org/TR/html401/struct/tables.html#h-11.2.4.3 - - var colgroupNodes = cvox.XpathUtil.evalXPath('child::colgroup', - this.activeTable_); - var colNodes = cvox.XpathUtil.evalXPath('child::col', this.activeTable_); - - if ((colgroupNodes.length == 0) && (colNodes.length == 0)) { - var maxcols = 0; - var outerChildren = cvox.TableUtil.getChildRows(this.activeTable_); - for (var i = 0; i < outerChildren.length; i++) { - var childrenCount = cvox.TableUtil.getChildCells(outerChildren[i]); - if (childrenCount.length > maxcols) { - maxcols = childrenCount.length; - } - } - return maxcols; - } else { - var sum = 0; - for (var i = 0; i < colNodes.length; i++) { - if (colNodes[i].hasAttribute('span')) { - sum += colNodes[i].getAttribute('span'); - } else { - sum += 1; - } - } - for (i = 0; i < colgroupNodes.length; i++) { - var colChildren = cvox.XpathUtil.evalXPath('child::col', - colgroupNodes[i]); - if (colChildren.length == 0) { - if (colgroupNodes[i].hasAttribute('span')) { - sum += colgroupNodes[i].getAttribute('span'); - } else { - sum += 1; - } - } - } - } - return sum; -}; - - -/** - * Moves to the next row in the table. Updates the cell cursor. - * - * @return {boolean} Either: - * 1) True if the update has been made. - * 2) False if the end of the table has been reached and the update has not - * happened. - */ -cvox.TraverseTable.prototype.nextRow = function() { - if (!this.currentCellCursor) { - // We have not started moving through the table yet - return this.goToRow(0); - } else { - return this.goToRow(this.currentCellCursor[0] + 1); - } - -}; - - -/** - * Moves to the previous row in the table. Updates the cell cursor. - * - * @return {boolean} Either: - * 1) True if the update has been made. - * 2) False if the end of the table has been reached and the update has not - * happened. - */ -cvox.TraverseTable.prototype.prevRow = function() { - if (!this.currentCellCursor) { - // We have not started moving through the table yet - return this.goToRow(this.rowCount - 1); - } else { - return this.goToRow(this.currentCellCursor[0] - 1); - } -}; - - -/** - * Moves to the next column in the table. Updates the cell cursor. - * - * @return {boolean} Either: - * 1) True if the update has been made. - * 2) False if the end of the table has been reached and the update has not - * happened. - */ -cvox.TraverseTable.prototype.nextCol = function() { - if (!this.currentCellCursor) { - // We have not started moving through the table yet - return this.goToCol(0); - } else { - return this.goToCol(this.currentCellCursor[1] + 1); - } -}; - - -/** - * Moves to the previous column in the table. Updates the cell cursor. - * - * @return {boolean} Either: - * 1) True if the update has been made. - * 2) False if the end of the table has been reached and the update has not - * happened. - */ -cvox.TraverseTable.prototype.prevCol = function() { - if (!this.currentCellCursor) { - // We have not started moving through the table yet - return this.goToCol(this.shadowColCount_() - 1); - } else { - return this.goToCol(this.currentCellCursor[1] - 1); - } -}; - - -/** - * Moves to the row at the specified index in the table. Updates the cell - * cursor. - * @param {number} index The index of the required row. - * @return {boolean} Either: - * 1) True if the index is valid and the update has been made. - * 2) False if the index is not valid (either less than 0 or greater than - * the number of rows in the table). - */ -cvox.TraverseTable.prototype.goToRow = function(index) { - if (this.shadowTable_[index] != null) { - if (this.currentCellCursor == null) { - // We haven't started moving through the table yet - this.currentCellCursor = [index, 0]; - } else { - this.currentCellCursor = [index, this.currentCellCursor[1]]; - } - return true; - } else { - return false; - } -}; - - -/** - * Moves to the column at the specified index in the table. Updates the cell - * cursor. - * @param {number} index The index of the required column. - * @return {boolean} Either: - * 1) True if the index is valid and the update has been made. - * 2) False if the index is not valid (either less than 0 or greater than - * the number of rows in the table). - */ -cvox.TraverseTable.prototype.goToCol = function(index) { - if (index < 0 || index >= this.colCount) { - return false; - } - if (this.currentCellCursor == null) { - // We haven't started moving through the table yet - this.currentCellCursor = [0, index]; - } else { - this.currentCellCursor = [this.currentCellCursor[0], index]; - } - return true; -}; - - -/** - * Moves to the cell at the specified index <i, j> in the table. Updates the - * cell cursor. - * @param {Array<number>} index The index <i, j> of the required cell. - * @return {boolean} Either: - * 1) True if the index is valid and the update has been made. - * 2) False if the index is not valid (either less than 0, greater than - * the number of rows or columns in the table, or there is no cell - * at that location). - */ -cvox.TraverseTable.prototype.goToCell = function(index) { - if (((index[0] < this.rowCount) && (index[0] >= 0)) && - ((index[1] < this.colCount) && (index[1] >= 0))) { - var cell = this.shadowTable_[index[0]][index[1]]; - if (cell != null) { - this.currentCellCursor = index; - return true; - } - } - return false; -}; - - -/** - * Moves to the cell at the last index in the table. Updates the cell cursor. - * @return {boolean} Either: - * 1) True if the index is valid and the update has been made. - * 2) False if the index is not valid (there is no cell at that location). - */ -cvox.TraverseTable.prototype.goToLastCell = function() { - var numRows = this.shadowTable_.length; - if (numRows == 0) { - return false; - } - var lastRow = this.shadowTable_[numRows - 1]; - var lastIndex = [(numRows - 1), (lastRow.length - 1)]; - var cell = - this.shadowTable_[lastIndex[0]][lastIndex[1]]; - if (cell != null) { - this.currentCellCursor = lastIndex; - return true; - } - return false; -}; - - -/** - * Moves to the cell at the last index in the current row of the table. Update - * the cell cursor. - * @return {boolean} Either: - * 1) True if the index is valid and the update has been made. - * 2) False if the index is not valid (there is no cell at that location). - */ -cvox.TraverseTable.prototype.goToRowLastCell = function() { - var currentRow = this.currentCellCursor[0]; - var lastIndex = [currentRow, (this.shadowTable_[currentRow].length - 1)]; - var cell = - this.shadowTable_[lastIndex[0]][lastIndex[1]]; - if (cell != null) { - this.currentCellCursor = lastIndex; - return true; - } - return false; -}; - - -/** - * Moves to the cell at the last index in the current column of the table. - * Update the cell cursor. - * @return {boolean} Either: - * 1) True if the index is valid and the update has been made. - * 2) False if the index is not valid (there is no cell at that location). - */ -cvox.TraverseTable.prototype.goToColLastCell = function() { - var currentCol = this.getCol(); - var lastIndex = [(currentCol.length - 1), this.currentCellCursor[1]]; - var cell = - this.shadowTable_[lastIndex[0]][lastIndex[1]]; - if (cell != null) { - this.currentCellCursor = lastIndex; - return true; - } - return false; -}; - - -/** - * Resets the table cursors. - * - */ -cvox.TraverseTable.prototype.resetCursor = function() { - this.currentCellCursor = null; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_util.js deleted file mode 100644 index a77ac3eb284..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/traverse_util.js +++ /dev/null @@ -1,927 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview Low-level DOM traversal utility functions to find the - * next (or previous) character, word, sentence, line, or paragraph, - * in a completely stateless manner without actually manipulating the - * selection. - */ - -goog.provide('cvox.TraverseUtil'); - -goog.require('cvox.Cursor'); -goog.require('cvox.DomPredicates'); -goog.require('cvox.DomUtil'); - -/** - * Utility functions for stateless DOM traversal. - * @constructor - */ -cvox.TraverseUtil = function() {}; - -/** - * Gets the text representation of a node. This allows us to substitute - * alt text, names, or titles for html elements that provide them. - * @param {Node} node A DOM node. - * @return {string} A text string representation of the node. - */ -cvox.TraverseUtil.getNodeText = function(node) { - if (node.constructor == Text) { - return node.data; - } else { - return ''; - } -}; - -/** - * Return true if a node should be treated as a leaf node, because - * its children are properties of the object that shouldn't be traversed. - * - * TODO(dmazzoni): replace this with a predicate that detects nodes with - * ARIA roles and other objects that have their own description. - * For now we just detect a couple of common cases. - * - * @param {Node} node A DOM node. - * @return {boolean} True if the node should be treated as a leaf node. - */ -cvox.TraverseUtil.treatAsLeafNode = function(node) { - return node.childNodes.length == 0 || - node.nodeName == 'SELECT' || - node.getAttribute('role') == 'listbox' || - node.nodeName == 'OBJECT'; -}; - -/** - * Return true only if a single character is whitespace. - * From https://developer.mozilla.org/en/Whitespace_in_the_DOM, - * whitespace is defined as one of the characters - * "\t" TAB \u0009 - * "\n" LF \u000A - * "\r" CR \u000D - * " " SPC \u0020. - * - * @param {string} c A string containing a single character. - * @return {boolean} True if the character is whitespace, otherwise false. - */ -cvox.TraverseUtil.isWhitespace = function(c) { - return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); -}; - -/** - * Set the selection to the range between the given start and end cursors. - * @param {cvox.Cursor} start The desired start of the selection. - * @param {cvox.Cursor} end The desired end of the selection. - * @return {Selection} the selection object. - */ -cvox.TraverseUtil.setSelection = function(start, end) { - var sel = window.getSelection(); - sel.removeAllRanges(); - var range = document.createRange(); - range.setStart(start.node, start.index); - range.setEnd(end.node, end.index); - sel.addRange(range); - - return sel; -}; - -// TODO(dtseng): Combine with cvox.DomUtil.hasContent. -/** - * Check if this DOM node has the attribute aria-hidden='true', which should - * hide it from screen readers. - * @param {Node} node An HTML DOM node. - * @return {boolean} Whether or not the html node should be traversed. - */ -cvox.TraverseUtil.isHidden = function(node) { - if (node instanceof HTMLElement && - node.getAttribute('aria-hidden') == 'true') { - return true; - } - switch (node.tagName) { - case 'SCRIPT': - case 'NOSCRIPT': - return true; - } - return false; -}; - -/** - * Moves the cursor forwards until it has crossed exactly one character. - * @param {cvox.Cursor} cursor The cursor location where the search should - * start. On exit, the cursor will be immediately to the right of the - * character returned. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @return {?string} The character found, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.forwardsChar = function( - cursor, elementsEntered, elementsLeft) { - while (true) { - // Move down until we get to a leaf node. - var childNode = null; - if (!cvox.TraverseUtil.treatAsLeafNode(cursor.node)) { - for (var i = cursor.index; i < cursor.node.childNodes.length; i++) { - var node = cursor.node.childNodes[i]; - if (cvox.TraverseUtil.isHidden(node)) { - if (node instanceof HTMLElement) { - elementsEntered.push(node); - } - continue; - } - if (cvox.DomUtil.isVisible(node, {checkAncestors: false})) { - childNode = node; - break; - } - } - } - if (childNode) { - cursor.node = childNode; - cursor.index = 0; - cursor.text = cvox.TraverseUtil.getNodeText(cursor.node); - if (cursor.node instanceof HTMLElement) { - elementsEntered.push(cursor.node); - } - continue; - } - - // Return the next character from this leaf node. - if (cursor.index < cursor.text.length) - return cursor.text[cursor.index++]; - - // Move to the next sibling, going up the tree as necessary. - while (cursor.node != null) { - // Try to move to the next sibling. - var siblingNode = null; - for (var node = cursor.node.nextSibling; - node != null; - node = node.nextSibling) { - if (cvox.TraverseUtil.isHidden(node)) { - if (node instanceof HTMLElement) { - elementsEntered.push(node); - } - continue; - } - if (cvox.DomUtil.isVisible(node, {checkAncestors: false})) { - siblingNode = node; - break; - } - } - if (siblingNode) { - if (cursor.node instanceof HTMLElement) { - elementsLeft.push(cursor.node); - } - - cursor.node = siblingNode; - cursor.text = cvox.TraverseUtil.getNodeText(siblingNode); - cursor.index = 0; - - if (cursor.node instanceof HTMLElement) { - elementsEntered.push(cursor.node); - } - - break; - } - - // Otherwise, move to the parent. - if (cursor.node.parentNode && - cursor.node.parentNode.constructor != HTMLBodyElement) { - if (cursor.node instanceof HTMLElement) { - elementsLeft.push(cursor.node); - } - cursor.node = cursor.node.parentNode; - cursor.text = null; - cursor.index = 0; - } else { - return null; - } - } - } -}; - -/** - * Moves the cursor backwards until it has crossed exactly one character. - * @param {cvox.Cursor} cursor The cursor location where the search should - * start. On exit, the cursor will be immediately to the left of the - * character returned. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @return {?string} The previous character, or null if the top of the - * document has been reached. - */ -cvox.TraverseUtil.backwardsChar = function( - cursor, elementsEntered, elementsLeft) { - while (true) { - // Move down until we get to a leaf node. - var childNode = null; - if (!cvox.TraverseUtil.treatAsLeafNode(cursor.node)) { - for (var i = cursor.index - 1; i >= 0; i--) { - var node = cursor.node.childNodes[i]; - if (cvox.TraverseUtil.isHidden(node)) { - if (node instanceof HTMLElement) { - elementsEntered.push(node); - } - continue; - } - if (cvox.DomUtil.isVisible(node, {checkAncestors: false})) { - childNode = node; - break; - } - } - } - if (childNode) { - cursor.node = childNode; - cursor.text = cvox.TraverseUtil.getNodeText(cursor.node); - if (cursor.text.length) - cursor.index = cursor.text.length; - else - cursor.index = cursor.node.childNodes.length; - if (cursor.node instanceof HTMLElement) { - elementsEntered.push(cursor.node); - } - continue; - } - - // Return the previous character from this leaf node. - if (cursor.text.length > 0 && cursor.index > 0) { - return cursor.text[--cursor.index]; - } - - // Move to the previous sibling, going up the tree as necessary. - while (true) { - // Try to move to the previous sibling. - var siblingNode = null; - for (var node = cursor.node.previousSibling; - node != null; - node = node.previousSibling) { - if (cvox.TraverseUtil.isHidden(node)) { - if (node instanceof HTMLElement) { - elementsEntered.push(node); - } - continue; - } - if (cvox.DomUtil.isVisible(node, {checkAncestors: false})) { - siblingNode = node; - break; - } - } - if (siblingNode) { - if (cursor.node instanceof HTMLElement) { - elementsLeft.push(cursor.node); - } - - cursor.node = siblingNode; - cursor.text = cvox.TraverseUtil.getNodeText(siblingNode); - if (cursor.text.length) - cursor.index = cursor.text.length; - else - cursor.index = cursor.node.childNodes.length; - - if (cursor.node instanceof HTMLElement) { - elementsEntered.push(cursor.node); - } - break; - } - - // Otherwise, move to the parent. - if (cursor.node.parentNode && - cursor.node.parentNode.constructor != HTMLBodyElement) { - if (cursor.node instanceof HTMLElement) { - elementsLeft.push(cursor.node); - } - cursor.node = cursor.node.parentNode; - cursor.text = null; - cursor.index = 0; - } else { - return null; - } - } - } -}; - -/** - * Finds the next character, starting from endCursor. Upon exit, startCursor - * and endCursor will surround the next character. If skipWhitespace is - * true, will skip until a real character is found. Otherwise, it will - * attempt to select all of the whitespace between the initial position - * of endCursor and the next non-whitespace character. - * @param {!cvox.Cursor} startCursor On exit, points to the position before - * the char. - * @param {!cvox.Cursor} endCursor The position to start searching for the next - * char. On exit, will point to the position past the char. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * initial and final cursor position will be pushed onto this array. - * @param {boolean} skipWhitespace If true, will keep scanning until a - * non-whitespace character is found. - * @return {?string} The next char, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getNextChar = function( - startCursor, endCursor, elementsEntered, elementsLeft, skipWhitespace) { - - // Save the starting position and get the first character. - startCursor.copyFrom(endCursor); - var c = cvox.TraverseUtil.forwardsChar( - endCursor, elementsEntered, elementsLeft); - if (c == null) - return null; - - // Keep track of whether the first character was whitespace. - var initialWhitespace = cvox.TraverseUtil.isWhitespace(c); - - // Keep scanning until we find a non-whitespace or non-skipped character. - while ((cvox.TraverseUtil.isWhitespace(c)) || - (cvox.TraverseUtil.isHidden(endCursor.node))) { - c = cvox.TraverseUtil.forwardsChar( - endCursor, elementsEntered, elementsLeft); - if (c == null) - return null; - } - if (skipWhitespace || !initialWhitespace) { - // If skipWhitepace is true, or if the first character we encountered - // was not whitespace, return that non-whitespace character. - startCursor.copyFrom(endCursor); - startCursor.index--; - return c; - } - else { - for (var i = 0; i < elementsEntered.length; i++) { - if (cvox.TraverseUtil.isHidden(elementsEntered[i])) { - // We need to make sure that startCursor and endCursor aren't - // surrounding a skippable node. - endCursor.index--; - startCursor.copyFrom(endCursor); - startCursor.index--; - return ' '; - } - } - // Otherwise, return all of the whitespace before that last character. - endCursor.index--; - return ' '; - } -}; - -/** - * Finds the previous character, starting from startCursor. Upon exit, - * startCursor and endCursor will surround the previous character. - * If skipWhitespace is true, will skip until a real character is found. - * Otherwise, it will attempt to select all of the whitespace between - * the initial position of endCursor and the next non-whitespace character. - * @param {!cvox.Cursor} startCursor The position to start searching for the - * char. On exit, will point to the position before the char. - * @param {!cvox.Cursor} endCursor The position to start searching for the next - * char. On exit, will point to the position past the char. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * initial and final cursor position will be pushed onto this array. - * @param {boolean} skipWhitespace If true, will keep scanning until a - * non-whitespace character is found. - * @return {?string} The previous char, or null if the top of the - * document has been reached. - */ -cvox.TraverseUtil.getPreviousChar = function( - startCursor, endCursor, elementsEntered, elementsLeft, skipWhitespace) { - - // Save the starting position and get the first character. - endCursor.copyFrom(startCursor); - var c = cvox.TraverseUtil.backwardsChar( - startCursor, elementsEntered, elementsLeft); - if (c == null) - return null; - - // Keep track of whether the first character was whitespace. - var initialWhitespace = cvox.TraverseUtil.isWhitespace(c); - - // Keep scanning until we find a non-whitespace or non-skipped character. - while ((cvox.TraverseUtil.isWhitespace(c)) || - (cvox.TraverseUtil.isHidden(startCursor.node))) { - c = cvox.TraverseUtil.backwardsChar( - startCursor, elementsEntered, elementsLeft); - if (c == null) - return null; - } - if (skipWhitespace || !initialWhitespace) { - // If skipWhitepace is true, or if the first character we encountered - // was not whitespace, return that non-whitespace character. - endCursor.copyFrom(startCursor); - endCursor.index++; - return c; - } else { - for (var i = 0; i < elementsEntered.length; i++) { - if (cvox.TraverseUtil.isHidden(elementsEntered[i])) { - startCursor.index++; - endCursor.copyFrom(startCursor); - endCursor.index++; - return ' '; - } - } - // Otherwise, return all of the whitespace before that last character. - startCursor.index++; - return ' '; - } -}; - -/** - * Finds the next word, starting from endCursor. Upon exit, startCursor - * and endCursor will surround the next word. A word is defined to be - * a string of 1 or more non-whitespace characters in the same DOM node. - * @param {cvox.Cursor} startCursor On exit, will point to the beginning of the - * word returned. - * @param {cvox.Cursor} endCursor The position to start searching for the next - * word. On exit, will point to the end of the word returned. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @return {?string} The next word, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getNextWord = function(startCursor, endCursor, - elementsEntered, elementsLeft) { - - // Find the first non-whitespace or non-skipped character. - var cursor = endCursor.clone(); - var c = cvox.TraverseUtil.forwardsChar(cursor, elementsEntered, elementsLeft); - if (c == null) - return null; - while ((cvox.TraverseUtil.isWhitespace(c)) || - (cvox.TraverseUtil.isHidden(cursor.node))) { - c = cvox.TraverseUtil.forwardsChar(cursor, elementsEntered, elementsLeft); - if (c == null) - return null; - } - - // Set startCursor to the position immediately before the first - // character in our word. It's safe to decrement |index| because - // forwardsChar guarantees that the cursor will be immediately to the - // right of the returned character on exit. - startCursor.copyFrom(cursor); - startCursor.index--; - - // Keep building up our word until we reach a whitespace character or - // would cross a tag. Don't actually return any tags crossed, because this - // word goes up until the tag boundary but not past it. - endCursor.copyFrom(cursor); - var word = c; - var newEntered = []; - var newLeft = []; - c = cvox.TraverseUtil.forwardsChar(cursor, newEntered, newLeft); - if (c == null) { - return word; - } - while (!cvox.TraverseUtil.isWhitespace(c) && - newEntered.length == 0 && - newLeft == 0) { - word += c; - endCursor.copyFrom(cursor); - c = cvox.TraverseUtil.forwardsChar(cursor, newEntered, newLeft); - if (c == null) { - return word; - } - } - - return word; -}; - -/** - * Finds the previous word, starting from startCursor. Upon exit, startCursor - * and endCursor will surround the previous word. A word is defined to be - * a string of 1 or more non-whitespace characters in the same DOM node. - * @param {cvox.Cursor} startCursor The position to start searching for the - * previous word. On exit, will point to the beginning of the - * word returned. - * @param {cvox.Cursor} endCursor On exit, will point to the end of the - * word returned. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @return {?string} The previous word, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getPreviousWord = function(startCursor, endCursor, - elementsEntered, elementsLeft) { - // Find the first non-whitespace or non-skipped character. - var cursor = startCursor.clone(); - var c = cvox.TraverseUtil.backwardsChar( - cursor, elementsEntered, elementsLeft); - if (c == null) - return null; - while ((cvox.TraverseUtil.isWhitespace(c) || - (cvox.TraverseUtil.isHidden(cursor.node)))) { - c = cvox.TraverseUtil.backwardsChar(cursor, elementsEntered, elementsLeft); - if (c == null) - return null; - } - - // Set endCursor to the position immediately after the first - // character we've found (the last character of the word, since we're - // searching backwards). - endCursor.copyFrom(cursor); - endCursor.index++; - - // Keep building up our word until we reach a whitespace character or - // would cross a tag. Don't actually return any tags crossed, because this - // word goes up until the tag boundary but not past it. - startCursor.copyFrom(cursor); - var word = c; - var newEntered = []; - var newLeft = []; - c = cvox.TraverseUtil.backwardsChar(cursor, newEntered, newLeft); - if (c == null) - return word; - while (!cvox.TraverseUtil.isWhitespace(c) && - newEntered.length == 0 && - newLeft.length == 0) { - word = c + word; - startCursor.copyFrom(cursor); - - c = cvox.TraverseUtil.backwardsChar(cursor, newEntered, newLeft); - if (c == null) - return word; - } - - return word; -}; - - -/** - * Given elements entered and left, and break tags, returns true if the - * current word should break. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {Object<boolean>} breakTags Associative array of tags that should - * break. - * @return {boolean} True if elementsEntered or elementsLeft include an - * element with one of these tags. - */ -cvox.TraverseUtil.includesBreakTagOrSkippedNode = function( - elementsEntered, elementsLeft, breakTags) { - for (var i = 0; i < elementsEntered.length; i++) { - if (cvox.TraverseUtil.isHidden(elementsEntered[i])) { - return true; - } - var style = window.getComputedStyle(elementsEntered[i], null); - if ((style && style.display != 'inline') || - breakTags[elementsEntered[i].tagName]) { - return true; - } - } - for (i = 0; i < elementsLeft.length; i++) { - var style = window.getComputedStyle(elementsLeft[i], null); - if ((style && style.display != 'inline') || - breakTags[elementsLeft[i].tagName]) { - return true; - } - } - return false; -}; - - -/** - * Finds the next sentence, starting from endCursor. Upon exit, - * startCursor and endCursor will surround the next sentence. - * - * @param {cvox.Cursor} startCursor On exit, marks the beginning of the - * sentence. - * @param {cvox.Cursor} endCursor The position to start searching for the next - * sentence. On exit, will point to the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {Object<boolean>} breakTags Associative array of tags that should - * break the sentence. - * @return {?string} The next sentence, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getNextSentence = function( - startCursor, endCursor, elementsEntered, elementsLeft, breakTags) { - return cvox.TraverseUtil.getNextString( - startCursor, endCursor, elementsEntered, elementsLeft, - function(str, word, elementsEntered, elementsLeft) { - if (str.substr(-1) == '.') - return true; - return cvox.TraverseUtil.includesBreakTagOrSkippedNode( - elementsEntered, elementsLeft, breakTags); - }); -}; - -/** - * Finds the previous sentence, starting from startCursor. Upon exit, - * startCursor and endCursor will surround the previous sentence. - * - * @param {cvox.Cursor} startCursor The position to start searching for the next - * sentence. On exit, will point to the start of the returned string. - * @param {cvox.Cursor} endCursor On exit, the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {Object<boolean>} breakTags Associative array of tags that should - * break the sentence. - * @return {?string} The previous sentence, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getPreviousSentence = function( - startCursor, endCursor, elementsEntered, elementsLeft, breakTags) { - return cvox.TraverseUtil.getPreviousString( - startCursor, endCursor, elementsEntered, elementsLeft, - function(str, word, elementsEntered, elementsLeft) { - if (word.substr(-1) == '.') - return true; - return cvox.TraverseUtil.includesBreakTagOrSkippedNode( - elementsEntered, elementsLeft, breakTags); - }); -}; - -/** - * Finds the next line, starting from endCursor. Upon exit, - * startCursor and endCursor will surround the next line. - * - * @param {cvox.Cursor} startCursor On exit, marks the beginning of the line. - * @param {cvox.Cursor} endCursor The position to start searching for the next - * line. On exit, will point to the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {Object<boolean>} breakTags Associative array of tags that should - * break the line. - * @return {?string} The next line, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getNextLine = function( - startCursor, endCursor, elementsEntered, elementsLeft, breakTags) { - var range = document.createRange(); - var currentRect = null; - var rightMostRect = null; - var prevCursor = endCursor.clone(); - return cvox.TraverseUtil.getNextString( - startCursor, endCursor, elementsEntered, elementsLeft, - function(str, word, elementsEntered, elementsLeft) { - range.setStart(startCursor.node, startCursor.index); - range.setEnd(endCursor.node, endCursor.index); - var currentRect = range.getBoundingClientRect(); - if (!rightMostRect) { - rightMostRect = currentRect; - } - - // Break at new lines except when within a link. - if (currentRect.bottom != rightMostRect.bottom && - !cvox.DomPredicates.linkPredicate(cvox.DomUtil.getAncestors( - endCursor.node))) { - endCursor.copyFrom(prevCursor); - return true; - } - - rightMostRect = currentRect; - prevCursor.copyFrom(endCursor); - - return cvox.TraverseUtil.includesBreakTagOrSkippedNode( - elementsEntered, elementsLeft, breakTags); - }); -}; - -/** - * Finds the previous line, starting from startCursor. Upon exit, - * startCursor and endCursor will surround the previous line. - * - * @param {cvox.Cursor} startCursor The position to start searching for the next - * line. On exit, will point to the start of the returned string. - * @param {cvox.Cursor} endCursor On exit, the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {Object<boolean>} breakTags Associative array of tags that should - * break the line. - * @return {?string} The previous line, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getPreviousLine = function( - startCursor, endCursor, elementsEntered, elementsLeft, breakTags) { - var range = document.createRange(); - var currentRect = null; - var leftMostRect = null; - var prevCursor = startCursor.clone(); - return cvox.TraverseUtil.getPreviousString( - startCursor, endCursor, elementsEntered, elementsLeft, - function(str, word, elementsEntered, elementsLeft) { - range.setStart(startCursor.node, startCursor.index); - range.setEnd(endCursor.node, endCursor.index); - var currentRect = range.getBoundingClientRect(); - if (!leftMostRect) { - leftMostRect = currentRect; - } - - // Break at new lines except when within a link. - if (currentRect.top != leftMostRect.top && - !cvox.DomPredicates.linkPredicate(cvox.DomUtil.getAncestors( - startCursor.node))) { - startCursor.copyFrom(prevCursor); - return true; - } - - leftMostRect = currentRect; - prevCursor.copyFrom(startCursor); - - return cvox.TraverseUtil.includesBreakTagOrSkippedNode( - elementsEntered, elementsLeft, breakTags); - }); -}; - -/** - * Finds the next paragraph, starting from endCursor. Upon exit, - * startCursor and endCursor will surround the next paragraph. - * - * @param {cvox.Cursor} startCursor On exit, marks the beginning of the - * paragraph. - * @param {cvox.Cursor} endCursor The position to start searching for the next - * paragraph. On exit, will point to the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @return {?string} The next paragraph, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getNextParagraph = function(startCursor, endCursor, - elementsEntered, elementsLeft) { - return cvox.TraverseUtil.getNextString( - startCursor, endCursor, elementsEntered, elementsLeft, - function(str, word, elementsEntered, elementsLeft) { - for (var i = 0; i < elementsEntered.length; i++) { - if (cvox.TraverseUtil.isHidden(elementsEntered[i])) { - return true; - } - var style = window.getComputedStyle(elementsEntered[i], null); - if (style && style.display != 'inline') { - return true; - } - } - for (i = 0; i < elementsLeft.length; i++) { - var style = window.getComputedStyle(elementsLeft[i], null); - if (style && style.display != 'inline') { - return true; - } - } - return false; - }); -}; - -/** - * Finds the previous paragraph, starting from startCursor. Upon exit, - * startCursor and endCursor will surround the previous paragraph. - * - * @param {cvox.Cursor} startCursor The position to start searching for the next - * paragraph. On exit, will point to the start of the returned string. - * @param {cvox.Cursor} endCursor On exit, the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @return {?string} The previous paragraph, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getPreviousParagraph = function( - startCursor, endCursor, elementsEntered, elementsLeft) { - return cvox.TraverseUtil.getPreviousString( - startCursor, endCursor, elementsEntered, elementsLeft, - function(str, word, elementsEntered, elementsLeft) { - for (var i = 0; i < elementsEntered.length; i++) { - if (cvox.TraverseUtil.isHidden(elementsEntered[i])) { - return true; - } - var style = window.getComputedStyle(elementsEntered[i], null); - if (style && style.display != 'inline') { - return true; - } - } - for (i = 0; i < elementsLeft.length; i++) { - var style = window.getComputedStyle(elementsLeft[i], null); - if (style && style.display != 'inline') { - return true; - } - } - return false; - }); -}; - -/** - * Customizable function to return the next string of words in the DOM, based - * on provided functions to decide when to break one string and start - * the next. This can be used to get the next sentence, line, paragraph, - * or potentially other granularities. - * - * Finds the next contiguous string, starting from endCursor. Upon exit, - * startCursor and endCursor will surround the next string. - * - * The breakBefore function takes four parameters, and - * should return true if the string should be broken before the proposed - * next word: - * str The string so far. - * word The next word to be added. - * elementsEntered The elements entered in reaching this next word. - * elementsLeft The elements left in reaching this next word. - * - * @param {cvox.Cursor} startCursor On exit, will point to the beginning of the - * next string. - * @param {cvox.Cursor} endCursor The position to start searching for the next - * string. On exit, will point to the end of the returned string. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {function(string, string, Array<Element>, Array<Element>)} - * breakBefore Function that takes the string so far, next word to be - * added, and elements entered and left, and returns true if the string - * should be ended before adding this word. - * @return {?string} The next string, or null if the bottom of the - * document has been reached. - */ -cvox.TraverseUtil.getNextString = function( - startCursor, endCursor, elementsEntered, elementsLeft, breakBefore) { - // Get the first word and set the start cursor to the start of the - // first word. - var wordStartCursor = endCursor.clone(); - var wordEndCursor = endCursor.clone(); - var newEntered = []; - var newLeft = []; - var str = ''; - var word = cvox.TraverseUtil.getNextWord( - wordStartCursor, wordEndCursor, newEntered, newLeft); - if (word == null) - return null; - startCursor.copyFrom(wordStartCursor); - - // Always add the first word when the string is empty, and then keep - // adding more words as long as breakBefore returns false - while (!str || !breakBefore(str, word, newEntered, newLeft)) { - // Append this word, set the end cursor to the end of this word, and - // update the returned list of nodes crossed to include ones we crossed - // in reaching this word. - if (str) - str += ' '; - str += word; - elementsEntered = elementsEntered.concat(newEntered); - elementsLeft = elementsLeft.concat(newLeft); - endCursor.copyFrom(wordEndCursor); - - // Get the next word and go back to the top of the loop. - newEntered = []; - newLeft = []; - word = cvox.TraverseUtil.getNextWord( - wordStartCursor, wordEndCursor, newEntered, newLeft); - if (word == null) - return str; - } - - return str; -}; - -/** - * Customizable function to return the previous string of words in the DOM, - * based on provided functions to decide when to break one string and start - * the next. See getNextString, above, for more details. - * - * Finds the previous contiguous string, starting from startCursor. Upon exit, - * startCursor and endCursor will surround the next string. - * - * @param {cvox.Cursor} startCursor The position to start searching for the - * previous string. On exit, will point to the beginning of the - * string returned. - * @param {cvox.Cursor} endCursor On exit, will point to the end of the - * string returned. - * @param {Array<Element>} elementsEntered Any HTML elements entered. - * @param {Array<Element>} elementsLeft Any HTML elements left. - * @param {function(string, string, Array<Element>, Array<Element>)} - * breakBefore Function that takes the string so far, the word to be - * added, and nodes crossed, and returns true if the string should be - * ended before adding this word. - * @return {?string} The next string, or null if the top of the - * document has been reached. - */ -cvox.TraverseUtil.getPreviousString = function( - startCursor, endCursor, elementsEntered, elementsLeft, breakBefore) { - // Get the first word and set the end cursor to the end of the - // first word. - var wordStartCursor = startCursor.clone(); - var wordEndCursor = startCursor.clone(); - var newEntered = []; - var newLeft = []; - var str = ''; - var word = cvox.TraverseUtil.getPreviousWord( - wordStartCursor, wordEndCursor, newEntered, newLeft); - if (word == null) - return null; - endCursor.copyFrom(wordEndCursor); - - // Always add the first word when the string is empty, and then keep - // adding more words as long as breakBefore returns false - while (!str || !breakBefore(str, word, newEntered, newLeft)) { - // Prepend this word, set the start cursor to the start of this word, and - // update the returned list of nodes crossed to include ones we crossed - // in reaching this word. - if (str) - str = ' ' + str; - str = word + str; - elementsEntered = elementsEntered.concat(newEntered); - elementsLeft = elementsLeft.concat(newLeft); - startCursor.copyFrom(wordStartCursor); - - // Get the previous word and go back to the top of the loop. - newEntered = []; - newLeft = []; - word = cvox.TraverseUtil.getPreviousWord( - wordStartCursor, wordEndCursor, newEntered, newLeft); - if (word == null) - return str; - } - - return str; -}; diff --git a/chromium/chrome/browser/resources/chromeos/chromevox/common/xpath_util.js b/chromium/chrome/browser/resources/chromeos/chromevox/common/xpath_util.js deleted file mode 100644 index cad6346c016..00000000000 --- a/chromium/chrome/browser/resources/chromeos/chromevox/common/xpath_util.js +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @fileoverview A collection of JavaScript utilities used to simplify working - * with xpaths. - */ - - -goog.provide('cvox.XpathUtil'); - - -/** - * Utilities for simplifying working with xpaths - * @constructor - */ -cvox.XpathUtil = function() { - }; - - -/** - * Mapping for some default namespaces. - * @const - * @private - */ -cvox.XpathUtil.nameSpaces_ = { - 'xhtml' : 'http://www.w3.org/1999/xhtml', - 'mathml': 'http://www.w3.org/1998/Math/MathML' -}; - - -/** - * Resolve some default name spaces. - * @param {string} prefix Namespace prefix. - * @return {string} The corresponding namespace URI. - */ -cvox.XpathUtil.resolveNameSpace = function(prefix) { - return cvox.XpathUtil.nameSpaces_[prefix] || null; -}; - - -/** - * Given an XPath expression and rootNode, it returns an array of children nodes - * that match. The code for this function was taken from Mihai Parparita's GMail - * Macros Greasemonkey Script. - * http://gmail-greasemonkey.googlecode.com/svn/trunk/scripts/gmail-new-macros.user.js - * @param {string} expression The XPath expression to evaluate. - * @param {Node} rootNode The HTML node to start evaluating the XPath from. - * @return {Array} The array of children nodes that match. - */ -cvox.XpathUtil.evalXPath = function(expression, rootNode) { - try { - var xpathIterator = rootNode.ownerDocument.evaluate( - expression, - rootNode, - cvox.XpathUtil.resolveNameSpace, - XPathResult.ORDERED_NODE_ITERATOR_TYPE, - null); // no existing results - } catch (err) { - return []; - } - var results = []; - // Convert result to JS array - for (var xpathNode = xpathIterator.iterateNext(); - xpathNode; - xpathNode = xpathIterator.iterateNext()) { - results.push(xpathNode); - } - return results; -}; - -/** - * Given a rootNode, it returns an array of all its leaf nodes. - * @param {Node} rootNode The node to get the leaf nodes from. - * @return {Array} The array of leaf nodes for the given rootNode. - */ -cvox.XpathUtil.getLeafNodes = function(rootNode) { - try { - var xpathIterator = rootNode.ownerDocument.evaluate( - './/*[count(*)=0]', - rootNode, - null, // no namespace resolver - XPathResult.ORDERED_NODE_ITERATOR_TYPE, - null); // no existing results - } catch (err) { - return []; - } - var results = []; - // Convert result to JS array - for (var xpathNode = xpathIterator.iterateNext(); - xpathNode; - xpathNode = xpathIterator.iterateNext()) { - results.push(xpathNode); - } - return results; -}; - -/** - * Returns whether or not xpath is supported. - * @return {boolean} True if xpath is supported. - */ -cvox.XpathUtil.xpathSupported = function() { - if (typeof(XPathResult) == 'undefined') { - return false; - } - return true; -}; - - -/** - * Given an XPath expression and rootNode, it evaluates the XPath expression as - * a boolean type and returns the result. - * @param {string} expression The XPath expression to evaluate. - * @param {Node} rootNode The HTML node to start evaluating the XPath from. - * @return {boolean} The result of evaluating the xpath expression. - */ -cvox.XpathUtil.evaluateBoolean = function(expression, rootNode) { - try { - var xpathResult = rootNode.ownerDocument.evaluate( - expression, - rootNode, - cvox.XpathUtil.resolveNameSpace, - XPathResult.BOOLEAN_TYPE, - null); // no existing results - } catch (err) { - return false; - } - return xpathResult.booleanValue; -}; - - -/** - * Given an XPath expression and rootNode, it evaluates the XPath expression as - * a string type and returns the result. - * @param {string} expression The XPath expression to evaluate. - * @param {Node} rootNode The HTML node to start evaluating the XPath from. - * @return {string} The result of evaluating the Xpath expression. - */ -cvox.XpathUtil.evaluateString = function(expression, rootNode) { - try { - var xpathResult = rootNode.ownerDocument.evaluate( - expression, - rootNode, - cvox.XpathUtil.resolveNameSpace, - XPathResult.STRING_TYPE, - null); // no existing results - } catch (err) { - return ''; - } - return xpathResult.stringValue; -}; |