| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview A semantic tree for MathML expressions. | 6 * @fileoverview A semantic tree for MathML expressions. |
| 7 * | 7 * |
| 8 * This file contains functionality to compute a semantic interpretation from a | 8 * This file contains functionality to compute a semantic interpretation from a |
| 9 * given MathML expression. This is a very heuristic approach that assumes a | 9 * given MathML expression. This is a very heuristic approach that assumes a |
| 10 * fairly simple default semantic which is suitable for K-12 and simple UG | 10 * fairly simple default semantic which is suitable for K-12 and simple UG |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 for (var i = 0, child; child = this.childNodes[i]; i++) { | 91 for (var i = 0, child; child = this.childNodes[i]; i++) { |
| 92 result = result.concat(child.querySelectorAll(pred)); | 92 result = result.concat(child.querySelectorAll(pred)); |
| 93 } | 93 } |
| 94 if (pred(this)) { | 94 if (pred(this)) { |
| 95 result.unshift(this); | 95 result.unshift(this); |
| 96 } | 96 } |
| 97 return result; | 97 return result; |
| 98 }; | 98 }; |
| 99 | 99 |
| 100 | 100 |
| 101 /** | 101 /** |
| 102 * Returns an XML representation of the tree. | 102 * Returns an XML representation of the tree. |
| 103 * @param {boolean=} brief If set attributes are omitted. | 103 * @param {boolean=} brief If set attributes are omitted. |
| 104 * @return {Node} The XML representation of the tree. | 104 * @return {Node} The XML representation of the tree. |
| 105 */ | 105 */ |
| 106 cvox.SemanticTree.prototype.xml = function(brief) { | 106 cvox.SemanticTree.prototype.xml = function(brief) { |
| 107 var dp = new DOMParser(); | 107 var dp = new DOMParser(); |
| 108 var xml = dp.parseFromString('<stree></stree>', 'text/xml'); | 108 var xml = dp.parseFromString('<stree></stree>', 'text/xml'); |
| 109 | 109 |
| 110 var xmlRoot = this.root.xml(xml, brief); | 110 var xmlRoot = this.root.xml(xml, brief); |
| 111 xml.childNodes[0].appendChild(xmlRoot); | 111 xml.childNodes[0].appendChild(xmlRoot); |
| 112 | 112 |
| 113 return xml.childNodes[0]; | 113 return xml.childNodes[0]; |
| 114 }; | 114 }; |
| 115 | |
| 116 | |
| 117 /** | |
| 118 * An XML tree representation of the current node. | |
| 119 * @param {Document} xml The XML document. | |
| 120 * @param {boolean=} brief If set attributes are omitted. | |
| 121 * @return {Node} The XML representation of the node. | |
| 122 */ | |
| 123 cvox.SemanticTree.Node.prototype.xml = function(xml, brief) { | |
| 124 /** | |
| 125 * Translates a list of nodes into XML representation. | |
| 126 * @param {string} tag Name of the enclosing tag. | |
| 127 * @param {!Array<!cvox.SemanticTree.Node>} nodes A list of nodes. | |
| 128 * @return {Node} An XML representation of the node list. | |
| 129 */ | |
| 130 var xmlNodeList = function(tag, nodes) { | |
| 131 var xmlNodes = nodes.map(function(x) {return x.xml(xml, brief);}); | |
| 132 var tagNode = xml.createElement(tag); | |
| 133 for (var i = 0, child; child = xmlNodes[i]; i++) { | |
| 134 tagNode.appendChild(child); | |
| 135 } | |
| 136 return tagNode; | |
| 137 }; | |
| 138 var node = xml.createElement(this.type); | |
| 139 if (!brief) { | |
| 140 this.xmlAttributes_(node); | |
| 141 } | |
| 142 node.textContent = this.textContent; | |
| 143 if (this.contentNodes.length > 0) { | |
| 144 node.appendChild(xmlNodeList('content', this.contentNodes)); | |
| 145 } | |
| 146 if (this.childNodes.length > 0) { | |
| 147 node.appendChild(xmlNodeList('children', this.childNodes)); | |
| 148 } | |
| 149 return node; | |
| 150 }; | |
| 151 | 115 |
| 152 | 116 |
| 153 /** | 117 /** |
| 154 * Serializes the XML representation of the tree. | 118 * An XML tree representation of the current node. |
| 155 * @param {boolean=} brief If set attributes are omitted. | 119 * @param {Document} xml The XML document. |
| 120 * @param {boolean=} brief If set attributes are omitted. |
| 121 * @return {Node} The XML representation of the node. |
| 122 */ |
| 123 cvox.SemanticTree.Node.prototype.xml = function(xml, brief) { |
| 124 /** |
| 125 * Translates a list of nodes into XML representation. |
| 126 * @param {string} tag Name of the enclosing tag. |
| 127 * @param {!Array<!cvox.SemanticTree.Node>} nodes A list of nodes. |
| 128 * @return {Node} An XML representation of the node list. |
| 129 */ |
| 130 var xmlNodeList = function(tag, nodes) { |
| 131 var xmlNodes = nodes.map(function(x) { |
| 132 return x.xml(xml, brief); |
| 133 }); |
| 134 var tagNode = xml.createElement(tag); |
| 135 for (var i = 0, child; child = xmlNodes[i]; i++) { |
| 136 tagNode.appendChild(child); |
| 137 } |
| 138 return tagNode; |
| 139 }; |
| 140 var node = xml.createElement(this.type); |
| 141 if (!brief) { |
| 142 this.xmlAttributes_(node); |
| 143 } |
| 144 node.textContent = this.textContent; |
| 145 if (this.contentNodes.length > 0) { |
| 146 node.appendChild(xmlNodeList('content', this.contentNodes)); |
| 147 } |
| 148 if (this.childNodes.length > 0) { |
| 149 node.appendChild(xmlNodeList('children', this.childNodes)); |
| 150 } |
| 151 return node; |
| 152 }; |
| 153 |
| 154 |
| 155 /** |
| 156 * Serializes the XML representation of the tree. |
| 157 * @param {boolean=} brief If set attributes are omitted. |
| 156 * @return {string} Serialized string. | 158 * @return {string} Serialized string. |
| 157 */ | 159 */ |
| 158 cvox.SemanticTree.prototype.toString = function(brief) { | 160 cvox.SemanticTree.prototype.toString = function(brief) { |
| 159 var xmls = new XMLSerializer(); | 161 var xmls = new XMLSerializer(); |
| 160 return xmls.serializeToString(this.xml(brief)); | 162 return xmls.serializeToString(this.xml(brief)); |
| 161 }; | 163 }; |
| 162 | 164 |
| 163 | 165 |
| 164 /** | 166 /** |
| 165 * Pretty print the XML representation of the tree. | 167 * Pretty print the XML representation of the tree. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 177 * @param {string} xml The serialised XML string. | 179 * @param {string} xml The serialised XML string. |
| 178 * @return {string} The formatted string. | 180 * @return {string} The formatted string. |
| 179 */ | 181 */ |
| 180 cvox.SemanticTree.formatXml = function(xml) { | 182 cvox.SemanticTree.formatXml = function(xml) { |
| 181 var reg = /(>)(<)(\/*)/g; | 183 var reg = /(>)(<)(\/*)/g; |
| 182 xml = xml.replace(reg, '$1\r\n$2$3'); | 184 xml = xml.replace(reg, '$1\r\n$2$3'); |
| 183 reg = /(>)(.+)(<c)/g; | 185 reg = /(>)(.+)(<c)/g; |
| 184 xml = xml.replace(reg, '$1\r\n$2\r\n$3'); | 186 xml = xml.replace(reg, '$1\r\n$2\r\n$3'); |
| 185 var formatted = ''; | 187 var formatted = ''; |
| 186 var padding = ''; | 188 var padding = ''; |
| 187 xml.split('\r\n') | 189 xml.split('\r\n').forEach(function(node) { |
| 188 .forEach(function(node) { | 190 if (node.match(/.+<\/\w[^>]*>$/)) { |
| 189 if (node.match(/.+<\/\w[^>]*>$/)) { | 191 // Node with content. |
| 190 // Node with content. | 192 formatted += padding + node + '\r\n'; |
| 191 formatted += padding + node + '\r\n'; | 193 } else if (node.match(/^<\/\w/)) { |
| 192 } else if (node.match(/^<\/\w/)) { | 194 if (padding) { |
| 193 if (padding) { | 195 // Closing tag |
| 194 // Closing tag | 196 padding = padding.slice(2); |
| 195 padding = padding.slice(2); | 197 formatted += padding + node + '\r\n'; |
| 196 formatted += padding + node + '\r\n'; | 198 } |
| 197 } | 199 } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { |
| 198 } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { | 200 // Opening tag |
| 199 // Opening tag | 201 formatted += padding + node + '\r\n'; |
| 200 formatted += padding + node + '\r\n'; | 202 padding += ' '; |
| 201 padding += ' '; | 203 } else { |
| 202 } else { | 204 // Empty tag |
| 203 // Empty tag | 205 formatted += padding + node + '\r\n'; |
| 204 formatted += padding + node + '\r\n'; | 206 } |
| 205 } | 207 }); |
| 206 }); | |
| 207 return formatted; | 208 return formatted; |
| 208 }; | 209 }; |
| 209 | 210 |
| 210 | 211 |
| 211 /** | 212 /** |
| 212 * Serializes the XML representation of a node. | 213 * Serializes the XML representation of a node. |
| 213 * @param {boolean=} brief If attributes are to be omitted. | 214 * @param {boolean=} brief If attributes are to be omitted. |
| 214 * @return {string} Serialized string. | 215 * @return {string} Serialized string. |
| 215 */ | 216 */ |
| 216 cvox.SemanticTree.Node.prototype.toString = function(brief) { | 217 cvox.SemanticTree.Node.prototype.toString = function(brief) { |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 var index = this.childNodes.indexOf(oldNode); | 335 var index = this.childNodes.indexOf(oldNode); |
| 335 if (index == -1) { | 336 if (index == -1) { |
| 336 return; | 337 return; |
| 337 } | 338 } |
| 338 newNode.parent = this; | 339 newNode.parent = this; |
| 339 oldNode.parent = null; | 340 oldNode.parent = null; |
| 340 this.childNodes[index] = newNode; | 341 this.childNodes[index] = newNode; |
| 341 // To not mess up the order of MathML elements more than necessary, we only | 342 // To not mess up the order of MathML elements more than necessary, we only |
| 342 // remove and add difference lists. The hope is that we might end up with | 343 // remove and add difference lists. The hope is that we might end up with |
| 343 // little change. | 344 // little change. |
| 344 var removeMathml = oldNode.mathml.filter( | 345 var removeMathml = oldNode.mathml.filter(function(x) { |
| 345 function(x) {return newNode.mathml.indexOf(x) == -1;}); | 346 return newNode.mathml.indexOf(x) == -1; |
| 346 var addMathml = newNode.mathml.filter( | 347 }); |
| 347 function(x) {return oldNode.mathml.indexOf(x) == -1;}); | 348 var addMathml = newNode.mathml.filter(function(x) { |
| 349 return oldNode.mathml.indexOf(x) == -1; |
| 350 }); |
| 348 this.removeMathmlNodes_(removeMathml); | 351 this.removeMathmlNodes_(removeMathml); |
| 349 this.addMathmlNodes_(addMathml); | 352 this.addMathmlNodes_(addMathml); |
| 350 }; | 353 }; |
| 351 | 354 |
| 352 | 355 |
| 353 /** | 356 /** |
| 354 * Appends a content node to the node. | 357 * Appends a content node to the node. |
| 355 * @param {cvox.SemanticTree.Node} node The new content node. | 358 * @param {cvox.SemanticTree.Node} node The new content node. |
| 356 * @private | 359 * @private |
| 357 */ | 360 */ |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 389 cvox.SemanticTree.prototype.parseMathml_ = function(mml) { | 392 cvox.SemanticTree.prototype.parseMathml_ = function(mml) { |
| 390 var children = cvox.DomUtil.toArray(mml.children); | 393 var children = cvox.DomUtil.toArray(mml.children); |
| 391 switch (cvox.SemanticUtil.tagName(mml)) { | 394 switch (cvox.SemanticUtil.tagName(mml)) { |
| 392 case 'MATH': | 395 case 'MATH': |
| 393 case 'MROW': | 396 case 'MROW': |
| 394 case 'MPADDED': | 397 case 'MPADDED': |
| 395 case 'MSTYLE': | 398 case 'MSTYLE': |
| 396 children = cvox.SemanticUtil.purgeNodes(children); | 399 children = cvox.SemanticUtil.purgeNodes(children); |
| 397 // Single child node, i.e. the row is meaningless. | 400 // Single child node, i.e. the row is meaningless. |
| 398 if (children.length == 1) { | 401 if (children.length == 1) { |
| 399 return this.parseMathml_(/** @type {!Element} */(children[0])); | 402 return this.parseMathml_(/** @type {!Element} */ (children[0])); |
| 400 } | 403 } |
| 401 // Case of a 'meaningful' row, even if they are empty. | 404 // Case of a 'meaningful' row, even if they are empty. |
| 402 return this.processRow_(this.parseMathmlChildren_(children)); | 405 return this.processRow_(this.parseMathmlChildren_(children)); |
| 403 break; | 406 break; |
| 404 case 'MFRAC': | 407 case 'MFRAC': |
| 405 var newNode = this.makeBranchNode_( | 408 var newNode = this.makeBranchNode_( |
| 406 cvox.SemanticAttr.Type.FRACTION, | 409 cvox.SemanticAttr.Type.FRACTION, |
| 407 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], | 410 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], []); |
| 408 []); | |
| 409 newNode.role = cvox.SemanticAttr.Role.DIVISION; | 411 newNode.role = cvox.SemanticAttr.Role.DIVISION; |
| 410 return newNode; | 412 return newNode; |
| 411 break; | 413 break; |
| 412 case 'MSUB': | 414 case 'MSUB': |
| 413 case 'MSUP': | 415 case 'MSUP': |
| 414 case 'MSUBSUP': | 416 case 'MSUBSUP': |
| 415 case 'MOVER': | 417 case 'MOVER': |
| 416 case 'MUNDER': | 418 case 'MUNDER': |
| 417 case 'MUNDEROVER': | 419 case 'MUNDEROVER': |
| 418 return this.makeLimitNode_(cvox.SemanticUtil.tagName(mml), | 420 return this.makeLimitNode_( |
| 419 this.parseMathmlChildren_(children)); | 421 cvox.SemanticUtil.tagName(mml), this.parseMathmlChildren_(children)); |
| 420 break; | 422 break; |
| 421 case 'MROOT': | 423 case 'MROOT': |
| 422 return this.makeBranchNode_( | 424 return this.makeBranchNode_( |
| 423 cvox.SemanticAttr.Type.ROOT, | 425 cvox.SemanticAttr.Type.ROOT, |
| 424 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], | 426 [this.parseMathml_(children[0]), this.parseMathml_(children[1])], []); |
| 425 []); | |
| 426 break; | 427 break; |
| 427 case 'MSQRT': | 428 case 'MSQRT': |
| 428 children = this.parseMathmlChildren_( | 429 children = |
| 429 cvox.SemanticUtil.purgeNodes(children)); | 430 this.parseMathmlChildren_(cvox.SemanticUtil.purgeNodes(children)); |
| 430 return this.makeBranchNode_( | 431 return this.makeBranchNode_( |
| 431 cvox.SemanticAttr.Type.SQRT, [this.processRow_(children)], []); | 432 cvox.SemanticAttr.Type.SQRT, [this.processRow_(children)], []); |
| 432 break; | 433 break; |
| 433 case 'MTABLE': | 434 case 'MTABLE': |
| 434 newNode = this.makeBranchNode_( | 435 newNode = this.makeBranchNode_( |
| 435 cvox.SemanticAttr.Type.TABLE, | 436 cvox.SemanticAttr.Type.TABLE, this.parseMathmlChildren_(children), |
| 436 this.parseMathmlChildren_(children), []); | 437 []); |
| 437 if (cvox.SemanticTree.tableIsMultiline_(newNode)) { | 438 if (cvox.SemanticTree.tableIsMultiline_(newNode)) { |
| 438 this.tableToMultiline_(newNode); | 439 this.tableToMultiline_(newNode); |
| 439 } | 440 } |
| 440 return newNode; | 441 return newNode; |
| 441 break; | 442 break; |
| 442 case 'MTR': | 443 case 'MTR': |
| 443 newNode = this.makeBranchNode_( | 444 newNode = this.makeBranchNode_( |
| 444 cvox.SemanticAttr.Type.ROW, | 445 cvox.SemanticAttr.Type.ROW, this.parseMathmlChildren_(children), []); |
| 445 this.parseMathmlChildren_(children), []); | |
| 446 newNode.role = cvox.SemanticAttr.Role.TABLE; | 446 newNode.role = cvox.SemanticAttr.Role.TABLE; |
| 447 return newNode; | 447 return newNode; |
| 448 break; | 448 break; |
| 449 case 'MTD': | 449 case 'MTD': |
| 450 children = this.parseMathmlChildren_( | 450 children = |
| 451 cvox.SemanticUtil.purgeNodes(children)); | 451 this.parseMathmlChildren_(cvox.SemanticUtil.purgeNodes(children)); |
| 452 newNode = this.makeBranchNode_( | 452 newNode = this.makeBranchNode_( |
| 453 cvox.SemanticAttr.Type.CELL, [this.processRow_(children)], []); | 453 cvox.SemanticAttr.Type.CELL, [this.processRow_(children)], []); |
| 454 newNode.role = cvox.SemanticAttr.Role.TABLE; | 454 newNode.role = cvox.SemanticAttr.Role.TABLE; |
| 455 return newNode; | 455 return newNode; |
| 456 break; | 456 break; |
| 457 case 'MTEXT': | 457 case 'MTEXT': |
| 458 var leaf = this.makeLeafNode_(mml); | 458 var leaf = this.makeLeafNode_(mml); |
| 459 leaf.type = cvox.SemanticAttr.Type.TEXT; | 459 leaf.type = cvox.SemanticAttr.Type.TEXT; |
| 460 return leaf; | 460 return leaf; |
| 461 break; | 461 break; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 478 break; | 478 break; |
| 479 case 'MO': | 479 case 'MO': |
| 480 leaf = this.makeLeafNode_(mml); | 480 leaf = this.makeLeafNode_(mml); |
| 481 if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) { | 481 if (leaf.type == cvox.SemanticAttr.Type.UNKNOWN) { |
| 482 leaf.type = cvox.SemanticAttr.Type.OPERATOR; | 482 leaf.type = cvox.SemanticAttr.Type.OPERATOR; |
| 483 } | 483 } |
| 484 return leaf; | 484 return leaf; |
| 485 break; | 485 break; |
| 486 // TODO (sorge) Do something useful with error and phantom symbols. | 486 // TODO (sorge) Do something useful with error and phantom symbols. |
| 487 default: | 487 default: |
| 488 // Ordinarilly at this point we should not get any other tag. | 488 // Ordinarilly at this point we should not get any other tag. |
| 489 return this.makeUnprocessed_(mml); | 489 return this.makeUnprocessed_(mml); |
| 490 } | 490 } |
| 491 }; | 491 }; |
| 492 | 492 |
| 493 | 493 |
| 494 /** | 494 /** |
| 495 * Parse a list of MathML nodes into the semantic tree. | 495 * Parse a list of MathML nodes into the semantic tree. |
| 496 * @param {Array<Element>} mmls A list of MathML nodes. | 496 * @param {Array<Element>} mmls A list of MathML nodes. |
| 497 * @return {!Array<cvox.SemanticTree.Node>} The list of resulting semantic | 497 * @return {!Array<cvox.SemanticTree.Node>} The list of resulting semantic |
| 498 * node. | 498 * node. |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 557 */ | 557 */ |
| 558 cvox.SemanticTree.prototype.makeBranchNode_ = function( | 558 cvox.SemanticTree.prototype.makeBranchNode_ = function( |
| 559 type, children, contentNodes, content) { | 559 type, children, contentNodes, content) { |
| 560 var node = this.createNode_(); | 560 var node = this.createNode_(); |
| 561 if (content) { | 561 if (content) { |
| 562 node.updateContent_(content); | 562 node.updateContent_(content); |
| 563 } | 563 } |
| 564 node.type = type; | 564 node.type = type; |
| 565 node.childNodes = children; | 565 node.childNodes = children; |
| 566 node.contentNodes = contentNodes; | 566 node.contentNodes = contentNodes; |
| 567 children.concat(contentNodes) | 567 children.concat(contentNodes).forEach(function(x) { |
| 568 .forEach( | 568 x.parent = node; |
| 569 function(x) { | 569 node.addMathmlNodes_(x.mathml); |
| 570 x.parent = node; | 570 }); |
| 571 node.addMathmlNodes_(x.mathml); | |
| 572 }); | |
| 573 return node; | 571 return node; |
| 574 }; | 572 }; |
| 575 | 573 |
| 576 | 574 |
| 577 /** | 575 /** |
| 578 * Create a branching node for an implicit operation, currently assumed to | 576 * Create a branching node for an implicit operation, currently assumed to |
| 579 * be of multiplicative type. | 577 * be of multiplicative type. |
| 580 * @param {!Array<!cvox.SemanticTree.Node>} nodes The operands. | 578 * @param {!Array<!cvox.SemanticTree.Node>} nodes The operands. |
| 581 * @return {!cvox.SemanticTree.Node} The new branch node. | 579 * @return {!cvox.SemanticTree.Node} The new branch node. |
| 582 * @private | 580 * @private |
| 583 */ | 581 */ |
| 584 cvox.SemanticTree.prototype.makeImplicitNode_ = function(nodes) { | 582 cvox.SemanticTree.prototype.makeImplicitNode_ = function(nodes) { |
| 585 if (nodes.length == 1) { | 583 if (nodes.length == 1) { |
| 586 return nodes[0]; | 584 return nodes[0]; |
| 587 } | 585 } |
| 588 var operator = this.createNode_(); | 586 var operator = this.createNode_(); |
| 589 // For now we assume this is a multiplication using invisible times. | 587 // For now we assume this is a multiplication using invisible times. |
| 590 operator.updateContent_(cvox.SemanticAttr.invisibleTimes()); | 588 operator.updateContent_(cvox.SemanticAttr.invisibleTimes()); |
| 591 var newNode = this.makeInfixNode_(nodes, operator); | 589 var newNode = this.makeInfixNode_(nodes, operator); |
| 592 newNode.role = cvox.SemanticAttr.Role.IMPLICIT; | 590 newNode.role = cvox.SemanticAttr.Role.IMPLICIT; |
| 593 return newNode; | 591 return newNode; |
| 594 }; | 592 }; |
| 595 | 593 |
| 596 | 594 |
| 597 /** | 595 /** |
| (...skipping 16 matching lines...) Expand all Loading... |
| 614 * @param {!cvox.SemanticTree.Node} inner The inner node. | 612 * @param {!cvox.SemanticTree.Node} inner The inner node. |
| 615 * @param {!Array<cvox.SemanticTree.Node>} nodeList List of nodes. | 613 * @param {!Array<cvox.SemanticTree.Node>} nodeList List of nodes. |
| 616 * @param {!cvox.SemanticAttr.Type} type The new type of the node. | 614 * @param {!cvox.SemanticAttr.Type} type The new type of the node. |
| 617 * @return {!cvox.SemanticTree.Node} The new branch node. | 615 * @return {!cvox.SemanticTree.Node} The new branch node. |
| 618 * @private | 616 * @private |
| 619 */ | 617 */ |
| 620 cvox.SemanticTree.prototype.makeConcatNode_ = function(inner, nodeList, type) { | 618 cvox.SemanticTree.prototype.makeConcatNode_ = function(inner, nodeList, type) { |
| 621 if (nodeList.length == 0) { | 619 if (nodeList.length == 0) { |
| 622 return inner; | 620 return inner; |
| 623 } | 621 } |
| 624 var content = nodeList.map(function(x) {return x.textContent;}).join(' '); | 622 var content = nodeList |
| 623 .map(function(x) { |
| 624 return x.textContent; |
| 625 }) |
| 626 .join(' '); |
| 625 var newNode = this.makeBranchNode_(type, [inner], nodeList, content); | 627 var newNode = this.makeBranchNode_(type, [inner], nodeList, content); |
| 626 if (nodeList.length > 0) { | 628 if (nodeList.length > 0) { |
| 627 newNode.role = cvox.SemanticAttr.Role.MULTIOP; | 629 newNode.role = cvox.SemanticAttr.Role.MULTIOP; |
| 628 } | 630 } |
| 629 return newNode; | 631 return newNode; |
| 630 }; | 632 }; |
| 631 | 633 |
| 632 | 634 |
| 633 /** | 635 /** |
| 634 * Wraps a node into prefix operators. | 636 * Wraps a node into prefix operators. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 var partition = cvox.SemanticTree.partitionNodes_( | 712 var partition = cvox.SemanticTree.partitionNodes_( |
| 711 nodes, cvox.SemanticTree.attrPred_('type', 'RELATION')); | 713 nodes, cvox.SemanticTree.attrPred_('type', 'RELATION')); |
| 712 var firstRel = partition.rel[0]; | 714 var firstRel = partition.rel[0]; |
| 713 | 715 |
| 714 if (!firstRel) { | 716 if (!firstRel) { |
| 715 return this.processOperationsInRow_(nodes); | 717 return this.processOperationsInRow_(nodes); |
| 716 } | 718 } |
| 717 if (nodes.length == 1) { | 719 if (nodes.length == 1) { |
| 718 return nodes[0]; | 720 return nodes[0]; |
| 719 } | 721 } |
| 720 var children = partition.comp.map( | 722 var children = |
| 721 goog.bind(this.processOperationsInRow_, this)); | 723 partition.comp.map(goog.bind(this.processOperationsInRow_, this)); |
| 722 if (partition.rel.every( | 724 if (partition.rel.every(function(x) { |
| 723 function(x) {return x.textContent == firstRel.textContent;})) { | 725 return x.textContent == firstRel.textContent; |
| 726 })) { |
| 724 return this.makeBranchNode_( | 727 return this.makeBranchNode_( |
| 725 cvox.SemanticAttr.Type.RELSEQ, children, partition.rel, | 728 cvox.SemanticAttr.Type.RELSEQ, children, partition.rel, |
| 726 firstRel.textContent); | 729 firstRel.textContent); |
| 727 } | 730 } |
| 728 return this.makeBranchNode_( | 731 return this.makeBranchNode_( |
| 729 cvox.SemanticAttr.Type.MULTIREL, children, partition.rel); | 732 cvox.SemanticAttr.Type.MULTIREL, children, partition.rel); |
| 730 }; | 733 }; |
| 731 | 734 |
| 732 | 735 |
| 733 /** | 736 /** |
| 734 * Constructs a syntax tree with operator precedence from a list nodes. | 737 * Constructs a syntax tree with operator precedence from a list nodes. |
| 735 * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes. | 738 * @param {!Array<!cvox.SemanticTree.Node>} nodes The list of nodes. |
| 736 * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. | 739 * @return {!cvox.SemanticTree.Node} The root node of the syntax tree. |
| 737 * @private | 740 * @private |
| 738 */ | 741 */ |
| 739 cvox.SemanticTree.prototype.processOperationsInRow_ = function(nodes) { | 742 cvox.SemanticTree.prototype.processOperationsInRow_ = function(nodes) { |
| 740 if (nodes.length == 0) { | 743 if (nodes.length == 0) { |
| 741 return this.makeEmptyNode_(); | 744 return this.makeEmptyNode_(); |
| 742 } | 745 } |
| 743 if (nodes.length == 1) { | 746 if (nodes.length == 1) { |
| 744 return nodes[0]; | 747 return nodes[0]; |
| 745 } | 748 } |
| 746 | 749 |
| 747 var prefix = []; | 750 var prefix = []; |
| 748 while (nodes.length > 0 && | 751 while (nodes.length > 0 && nodes[0].type == cvox.SemanticAttr.Type.OPERATOR) { |
| 749 nodes[0].type == cvox.SemanticAttr.Type.OPERATOR) { | |
| 750 prefix.push(nodes.shift()); | 752 prefix.push(nodes.shift()); |
| 751 } | 753 } |
| 752 // Pathological case: only operators in row. | 754 // Pathological case: only operators in row. |
| 753 if (nodes.length == 0) { | 755 if (nodes.length == 0) { |
| 754 return this.makePrefixNode_(prefix.pop(), prefix); | 756 return this.makePrefixNode_(prefix.pop(), prefix); |
| 755 } | 757 } |
| 756 if (nodes.length == 1) { | 758 if (nodes.length == 1) { |
| 757 return this.makePrefixNode_(nodes[0], prefix); | 759 return this.makePrefixNode_(nodes[0], prefix); |
| 758 } | 760 } |
| 759 | 761 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 803 } | 805 } |
| 804 | 806 |
| 805 var split = cvox.SemanticTree.sliceNodes_( | 807 var split = cvox.SemanticTree.sliceNodes_( |
| 806 nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR')); | 808 nodes, cvox.SemanticTree.attrPred_('type', 'OPERATOR')); |
| 807 | 809 |
| 808 if (split.head.length == 0) { | 810 if (split.head.length == 0) { |
| 809 prefixes.push(split.div); | 811 prefixes.push(split.div); |
| 810 return this.makeOperationsTree_(split.tail, root, lastop, prefixes); | 812 return this.makeOperationsTree_(split.tail, root, lastop, prefixes); |
| 811 } | 813 } |
| 812 | 814 |
| 813 var node = this.makePrefixNode_( | 815 var node = this.makePrefixNode_(this.makeImplicitNode_(split.head), prefixes); |
| 814 this.makeImplicitNode_(split.head), prefixes); | |
| 815 var newNode = this.appendOperand_(root, lastop, node); | 816 var newNode = this.appendOperand_(root, lastop, node); |
| 816 if (!split.div) { | 817 if (!split.div) { |
| 817 return newNode; | 818 return newNode; |
| 818 } | 819 } |
| 819 | 820 |
| 820 return this.makeOperationsTree_(split.tail, newNode, split.div, []); | 821 return this.makeOperationsTree_(split.tail, newNode, split.div, []); |
| 821 }; | 822 }; |
| 822 | 823 |
| 823 // TODO (sorge) The following four functions could be combined into | 824 // TODO (sorge) The following four functions could be combined into |
| 824 // a single one. Currently it is clearer the way it is, though. | 825 // a single one. Currently it is clearer the way it is, though. |
| 825 /** | 826 /** |
| 826 * Appends an operand at the right place in an operator tree. | 827 * Appends an operand at the right place in an operator tree. |
| 827 * @param {!cvox.SemanticTree.Node} root The operator tree. | 828 * @param {!cvox.SemanticTree.Node} root The operator tree. |
| 828 * @param {!cvox.SemanticTree.Node} op The operator node. | 829 * @param {!cvox.SemanticTree.Node} op The operator node. |
| 829 * @param {!cvox.SemanticTree.Node} node The node to be added. | 830 * @param {!cvox.SemanticTree.Node} node The node to be added. |
| 830 * @return {!cvox.SemanticTree.Node} The modified root node. | 831 * @return {!cvox.SemanticTree.Node} The modified root node. |
| 831 * @private | 832 * @private |
| 832 */ | 833 */ |
| 833 cvox.SemanticTree.prototype.appendOperand_ = function(root, op, node) { | 834 cvox.SemanticTree.prototype.appendOperand_ = function(root, op, node) { |
| 834 // In general our operator tree will have the form that additions and | 835 // In general our operator tree will have the form that additions and |
| 835 // subtractions are stacked, while multiplications are subordinate. | 836 // subtractions are stacked, while multiplications are subordinate. |
| 836 if (root.type != cvox.SemanticAttr.Type.INFIXOP) { | 837 if (root.type != cvox.SemanticAttr.Type.INFIXOP) { |
| 837 return this.makeInfixNode_([root, node], op); | 838 return this.makeInfixNode_([root, node], op); |
| 838 } | 839 } |
| 839 if (this.appendExistingOperator_(root, op, node)) { | 840 if (this.appendExistingOperator_(root, op, node)) { |
| 840 return root; | 841 return root; |
| 841 } | 842 } |
| 842 return op.role == cvox.SemanticAttr.Role.MULTIPLICATION ? | 843 return op.role == cvox.SemanticAttr.Role.MULTIPLICATION ? |
| 843 this.appendMultiplicativeOp_(root, op, node) : | 844 this.appendMultiplicativeOp_(root, op, node) : |
| 844 this.appendAdditiveOp_(root, op, node); | 845 this.appendAdditiveOp_(root, op, node); |
| 845 }; | 846 }; |
| 846 | 847 |
| 847 | 848 |
| 848 /** | 849 /** |
| 849 * Appends a multiplicative operator and operand. | 850 * Appends a multiplicative operator and operand. |
| 850 * @param {!cvox.SemanticTree.Node} root The root node. | 851 * @param {!cvox.SemanticTree.Node} root The root node. |
| 851 * @param {!cvox.SemanticTree.Node} op The operator node. | 852 * @param {!cvox.SemanticTree.Node} op The operator node. |
| 852 * @param {!cvox.SemanticTree.Node} node The operand node to be added. | 853 * @param {!cvox.SemanticTree.Node} node The operand node to be added. |
| 853 * @return {!cvox.SemanticTree.Node} The modified root node. | 854 * @return {!cvox.SemanticTree.Node} The modified root node. |
| 854 * @private | 855 * @private |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 893 return false; | 894 return false; |
| 894 } | 895 } |
| 895 if (root.textContent == op.textContent) { | 896 if (root.textContent == op.textContent) { |
| 896 root.appendContentNode_(op); | 897 root.appendContentNode_(op); |
| 897 root.appendChild_(node); | 898 root.appendChild_(node); |
| 898 return true; | 899 return true; |
| 899 } | 900 } |
| 900 this.appendExistingOperator_( | 901 this.appendExistingOperator_( |
| 901 // Again, if this is an INFIXOP node, we know it has a child! | 902 // Again, if this is an INFIXOP node, we know it has a child! |
| 902 /** @type {!cvox.SemanticTree.Node} */ | 903 /** @type {!cvox.SemanticTree.Node} */ |
| 903 (root.childNodes[root.childNodes.length - 1]), | 904 (root.childNodes[root.childNodes.length - 1]), op, node); |
| 904 op, node); | |
| 905 return false; | 905 return false; |
| 906 }; | 906 }; |
| 907 | 907 |
| 908 | 908 |
| 909 // TODO (sorge) The following procedure needs a rational reconstruction. It | 909 // TODO (sorge) The following procedure needs a rational reconstruction. It |
| 910 // contains a number of similar cases which should be combined. | 910 // contains a number of similar cases which should be combined. |
| 911 /** | 911 /** |
| 912 * Combines delimited expressions in a list of nodes. | 912 * Combines delimited expressions in a list of nodes. |
| 913 * | 913 * |
| 914 * The basic idea of the heuristic is as follows: | 914 * The basic idea of the heuristic is as follows: |
| (...skipping 24 matching lines...) Expand all Loading... |
| 939 * @param {!Array<!Array<cvox.SemanticTree.Node>>} content FIFO queue content | 939 * @param {!Array<!Array<cvox.SemanticTree.Node>>} content FIFO queue content |
| 940 * between fences. | 940 * between fences. |
| 941 * @param {!Array<cvox.SemanticTree.Node>} openStack LIFO stack of open fences. | 941 * @param {!Array<cvox.SemanticTree.Node>} openStack LIFO stack of open fences. |
| 942 * @param {!Array<!Array<cvox.SemanticTree.Node>>} contentStack LIFO stack of | 942 * @param {!Array<!Array<cvox.SemanticTree.Node>>} contentStack LIFO stack of |
| 943 * content between fences yet to be processed. | 943 * content between fences yet to be processed. |
| 944 * @return {!Array<cvox.SemanticTree.Node>} A list of nodes with all fenced | 944 * @return {!Array<cvox.SemanticTree.Node>} A list of nodes with all fenced |
| 945 * expressions processed. | 945 * expressions processed. |
| 946 * @private | 946 * @private |
| 947 */ | 947 */ |
| 948 cvox.SemanticTree.prototype.processFences_ = function( | 948 cvox.SemanticTree.prototype.processFences_ = function( |
| 949 fences, content, openStack, contentStack) { | 949 fences, content, openStack, contentStack) { |
| 950 // Base case 1: Everything is used up. | 950 // Base case 1: Everything is used up. |
| 951 if (fences.length == 0 && openStack.length == 0) { | 951 if (fences.length == 0 && openStack.length == 0) { |
| 952 return contentStack[0]; | 952 return contentStack[0]; |
| 953 } | 953 } |
| 954 var openPred = cvox.SemanticTree.attrPred_('role', 'OPEN'); | 954 var openPred = cvox.SemanticTree.attrPred_('role', 'OPEN'); |
| 955 // Base case 2: Only open and neutral fences are left on the stack. | 955 // Base case 2: Only open and neutral fences are left on the stack. |
| 956 if (fences.length == 0) { | 956 if (fences.length == 0) { |
| 957 // Basic idea: | 957 // Basic idea: |
| 958 // - make punctuation nodes from open fences | 958 // - make punctuation nodes from open fences |
| 959 // - combine as many neutral fences as possible, if the are not separated by | 959 // - combine as many neutral fences as possible, if the are not separated by |
| 960 // open fences. | 960 // open fences. |
| 961 // The idea is to allow for things like case statements etc. and not bury | 961 // The idea is to allow for things like case statements etc. and not bury |
| 962 // them inside a neutral fenced expression. | 962 // them inside a neutral fenced expression. |
| 963 // | 963 // |
| 964 // 0. We process the list from left to right. Hence the first element on the | 964 // 0. We process the list from left to right. Hence the first element on the |
| 965 // content stack are actually left most elements in the expression. | 965 // content stack are actually left most elements in the expression. |
| 966 // 1. Slice at open fence. | 966 // 1. Slice at open fence. |
| 967 // 2. On tail optimize for neutral fences. | 967 // 2. On tail optimize for neutral fences. |
| 968 // 3. Repeat until fence stack is exhausted. | 968 // 3. Repeat until fence stack is exhausted. |
| 969 // Push rightmost elements onto the result. | 969 // Push rightmost elements onto the result. |
| 970 var result = contentStack.shift(); | 970 var result = contentStack.shift(); |
| 971 while (openStack.length > 0) { | 971 while (openStack.length > 0) { |
| 972 if (openPred(openStack[0])) { | 972 if (openPred(openStack[0])) { |
| 973 var firstOpen = openStack.shift(); | 973 var firstOpen = openStack.shift(); |
| 974 cvox.SemanticTree.fenceToPunct_(firstOpen); | 974 cvox.SemanticTree.fenceToPunct_(firstOpen); |
| 975 result.push(firstOpen); | 975 result.push(firstOpen); |
| 976 } else { | 976 } else { |
| 977 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred); | 977 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred); |
| 978 var cutLength = split.head.length - 1; | 978 var cutLength = split.head.length - 1; |
| 979 var innerNodes = this.processNeutralFences_( | 979 var innerNodes = this.processNeutralFences_( |
| 980 split.head, contentStack.slice(0, cutLength)); | 980 split.head, contentStack.slice(0, cutLength)); |
| 981 contentStack = contentStack.slice(cutLength); | 981 contentStack = contentStack.slice(cutLength); |
| 982 //var rightContent = contentStack.shift(); | 982 // var rightContent = contentStack.shift(); |
| 983 result.push.apply(result, innerNodes); | 983 result.push.apply(result, innerNodes); |
| 984 //result.push.apply(result, rightContent); | 984 // result.push.apply(result, rightContent); |
| 985 if (split.div) { | 985 if (split.div) { |
| 986 split.tail.unshift(split.div); | 986 split.tail.unshift(split.div); |
| 987 } | 987 } |
| 988 openStack = split.tail; | 988 openStack = split.tail; |
| 989 } | 989 } |
| 990 result.push.apply(result, contentStack.shift()); | 990 result.push.apply(result, contentStack.shift()); |
| 991 } | 991 } |
| 992 return result; | 992 return result; |
| 993 } | 993 } |
| 994 var lastOpen = openStack[openStack.length - 1]; | 994 var lastOpen = openStack[openStack.length - 1]; |
| 995 var firstRole = fences[0].role; | 995 var firstRole = fences[0].role; |
| 996 // General opening case. | 996 // General opening case. |
| 997 // Either we have an open fence. | 997 // Either we have an open fence. |
| 998 if (firstRole == cvox.SemanticAttr.Role.OPEN || | 998 if (firstRole == cvox.SemanticAttr.Role.OPEN || |
| 999 // Or we have a neutral fence that does not have a counter part. | 999 // Or we have a neutral fence that does not have a counter part. |
| 1000 (firstRole == cvox.SemanticAttr.Role.NEUTRAL && | 1000 (firstRole == cvox.SemanticAttr.Role.NEUTRAL && |
| 1001 (!lastOpen || | 1001 (!lastOpen || fences[0].textContent != lastOpen.textContent))) { |
| 1002 fences[0].textContent != lastOpen.textContent))) { | |
| 1003 openStack.push(fences.shift()); | 1002 openStack.push(fences.shift()); |
| 1004 contentStack.push(content.shift()); | 1003 contentStack.push(content.shift()); |
| 1005 return this.processFences_(fences, content, openStack, contentStack); | 1004 return this.processFences_(fences, content, openStack, contentStack); |
| 1006 } | 1005 } |
| 1007 // General closing case. | 1006 // General closing case. |
| 1008 if (lastOpen && ( | 1007 if (lastOpen && |
| 1009 // Closing fence for some opening fence. | 1008 ( |
| 1010 (firstRole == cvox.SemanticAttr.Role.CLOSE && | 1009 // Closing fence for some opening fence. |
| 1011 lastOpen.role == cvox.SemanticAttr.Role.OPEN) || | 1010 (firstRole == cvox.SemanticAttr.Role.CLOSE && |
| 1012 // Netural fence with exact counter part. | 1011 lastOpen.role == cvox.SemanticAttr.Role.OPEN) || |
| 1013 (firstRole == cvox.SemanticAttr.Role.NEUTRAL && | 1012 // Netural fence with exact counter part. |
| 1014 fences[0].textContent == lastOpen.textContent))) { | 1013 (firstRole == cvox.SemanticAttr.Role.NEUTRAL && |
| 1014 fences[0].textContent == lastOpen.textContent))) { |
| 1015 var fenced = this.makeHorizontalFencedNode_( | 1015 var fenced = this.makeHorizontalFencedNode_( |
| 1016 openStack.pop(), fences.shift(), contentStack.pop()); | 1016 openStack.pop(), fences.shift(), contentStack.pop()); |
| 1017 contentStack.push(contentStack.pop().concat([fenced], content.shift())); | 1017 contentStack.push(contentStack.pop().concat([fenced], content.shift())); |
| 1018 return this.processFences_(fences, content, openStack, contentStack); | 1018 return this.processFences_(fences, content, openStack, contentStack); |
| 1019 } | 1019 } |
| 1020 // Closing with a neutral fence on the stack. | 1020 // Closing with a neutral fence on the stack. |
| 1021 if (lastOpen && firstRole == cvox.SemanticAttr.Role.CLOSE && | 1021 if (lastOpen && firstRole == cvox.SemanticAttr.Role.CLOSE && |
| 1022 lastOpen.role == cvox.SemanticAttr.Role.NEUTRAL && | 1022 lastOpen.role == cvox.SemanticAttr.Role.NEUTRAL && |
| 1023 openStack.some(openPred)) { | 1023 openStack.some(openPred)) { |
| 1024 // Steps of the algorithm: | 1024 // Steps of the algorithm: |
| 1025 // 1. Split list at right most opening bracket. | 1025 // 1. Split list at right most opening bracket. |
| 1026 // 2. Cut content list at corresponding length. | 1026 // 2. Cut content list at corresponding length. |
| 1027 // 3. Optimise the neutral fences. | 1027 // 3. Optimise the neutral fences. |
| 1028 // 4. Make fenced node. | 1028 // 4. Make fenced node. |
| 1029 // | 1029 // |
| 1030 // Careful, this reverses openStack! | 1030 // Careful, this reverses openStack! |
| 1031 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred, true); | 1031 var split = cvox.SemanticTree.sliceNodes_(openStack, openPred, true); |
| 1032 // We know that | 1032 // We know that |
| 1033 // (a) div & tail exist, | 1033 // (a) div & tail exist, |
| 1034 // (b) all are combined in this step into a single fenced node, | 1034 // (b) all are combined in this step into a single fenced node, |
| 1035 // (c) head is the new openStack, | 1035 // (c) head is the new openStack, |
| 1036 // (d) the new contentStack is remainder of contentStack + new fenced node + | 1036 // (d) the new contentStack is remainder of contentStack + new fenced node + |
| 1037 // shift of content. | 1037 // shift of content. |
| 1038 var rightContent = contentStack.pop(); | 1038 var rightContent = contentStack.pop(); |
| 1039 var cutLength = contentStack.length - split.tail.length + 1; | 1039 var cutLength = contentStack.length - split.tail.length + 1; |
| 1040 var innerNodes = this.processNeutralFences_( | 1040 var innerNodes = |
| 1041 split.tail, contentStack.slice(cutLength)); | 1041 this.processNeutralFences_(split.tail, contentStack.slice(cutLength)); |
| 1042 contentStack = contentStack.slice(0, cutLength); | 1042 contentStack = contentStack.slice(0, cutLength); |
| 1043 var fenced = this.makeHorizontalFencedNode_( | 1043 var fenced = this.makeHorizontalFencedNode_( |
| 1044 split.div, fences.shift(), | 1044 split.div, fences.shift(), |
| 1045 contentStack.pop().concat(innerNodes, rightContent)); | 1045 contentStack.pop().concat(innerNodes, rightContent)); |
| 1046 contentStack.push(contentStack.pop().concat([fenced], content.shift())); | 1046 contentStack.push(contentStack.pop().concat([fenced], content.shift())); |
| 1047 return this.processFences_(fences, content, split.head, contentStack); | 1047 return this.processFences_(fences, content, split.head, contentStack); |
| 1048 } | 1048 } |
| 1049 // Final Case: A singular closing fence. | 1049 // Final Case: A singular closing fence. |
| 1050 // We turn the fence into a punctuation. | 1050 // We turn the fence into a punctuation. |
| 1051 var fenced = fences.shift(); | 1051 var fenced = fences.shift(); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1065 * nodes. | 1065 * nodes. |
| 1066 * @private | 1066 * @private |
| 1067 */ | 1067 */ |
| 1068 cvox.SemanticTree.prototype.processNeutralFences_ = function(fences, content) { | 1068 cvox.SemanticTree.prototype.processNeutralFences_ = function(fences, content) { |
| 1069 if (fences.length == 0) { | 1069 if (fences.length == 0) { |
| 1070 return fences; | 1070 return fences; |
| 1071 } | 1071 } |
| 1072 if (fences.length == 1) { | 1072 if (fences.length == 1) { |
| 1073 cvox.SemanticTree.fenceToPunct_(fences[0]); | 1073 cvox.SemanticTree.fenceToPunct_(fences[0]); |
| 1074 return fences; | 1074 return fences; |
| 1075 } | 1075 } |
| 1076 var firstFence = fences.shift(); | 1076 var firstFence = fences.shift(); |
| 1077 var split = cvox.SemanticTree.sliceNodes_( | 1077 var split = cvox.SemanticTree.sliceNodes_(fences, function(x) { |
| 1078 fences, function(x) {return x.textContent == firstFence.textContent;}); | 1078 return x.textContent == firstFence.textContent; |
| 1079 }); |
| 1079 if (!split.div) { | 1080 if (!split.div) { |
| 1080 cvox.SemanticTree.fenceToPunct_(firstFence); | 1081 cvox.SemanticTree.fenceToPunct_(firstFence); |
| 1081 var restContent = content.shift(); | 1082 var restContent = content.shift(); |
| 1082 restContent.unshift(firstFence); | 1083 restContent.unshift(firstFence); |
| 1083 return restContent.concat(this.processNeutralFences_(fences, content)); | 1084 return restContent.concat(this.processNeutralFences_(fences, content)); |
| 1084 } | 1085 } |
| 1085 var newContent = this.combineFencedContent_( | 1086 var newContent = |
| 1086 firstFence, split.div, split.head, content); | 1087 this.combineFencedContent_(firstFence, split.div, split.head, content); |
| 1087 if (split.tail.length > 0) { | 1088 if (split.tail.length > 0) { |
| 1088 var leftContent = newContent.shift(); | 1089 var leftContent = newContent.shift(); |
| 1089 var result = this.processNeutralFences_(split.tail, newContent); | 1090 var result = this.processNeutralFences_(split.tail, newContent); |
| 1090 return leftContent.concat(result); | 1091 return leftContent.concat(result); |
| 1091 } | 1092 } |
| 1092 return newContent[0]; | 1093 return newContent[0]; |
| 1093 }; | 1094 }; |
| 1094 | 1095 |
| 1095 | 1096 |
| 1096 /** | 1097 /** |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1107 * remainder. | 1108 * remainder. |
| 1108 * @return {!Array<!Array<cvox.SemanticTree.Node>>} List of content nodes | 1109 * @return {!Array<!Array<cvox.SemanticTree.Node>>} List of content nodes |
| 1109 * where the first is the fully fenced node wrt. the given left and right | 1110 * where the first is the fully fenced node wrt. the given left and right |
| 1110 * fence. | 1111 * fence. |
| 1111 * @private | 1112 * @private |
| 1112 */ | 1113 */ |
| 1113 cvox.SemanticTree.prototype.combineFencedContent_ = function( | 1114 cvox.SemanticTree.prototype.combineFencedContent_ = function( |
| 1114 leftFence, rightFence, midFences, content) { | 1115 leftFence, rightFence, midFences, content) { |
| 1115 | 1116 |
| 1116 if (midFences.length == 0) { | 1117 if (midFences.length == 0) { |
| 1117 var fenced = this.makeHorizontalFencedNode_( | 1118 var fenced = |
| 1118 leftFence, rightFence, content.shift()); | 1119 this.makeHorizontalFencedNode_(leftFence, rightFence, content.shift()); |
| 1119 content.unshift(fenced); | 1120 content.unshift(fenced); |
| 1120 return content; | 1121 return content; |
| 1121 } | 1122 } |
| 1122 | 1123 |
| 1123 var leftContent = content.shift(); | 1124 var leftContent = content.shift(); |
| 1124 var cutLength = midFences.length - 1; | 1125 var cutLength = midFences.length - 1; |
| 1125 var midContent = content.slice(0, cutLength); | 1126 var midContent = content.slice(0, cutLength); |
| 1126 content = content.slice(cutLength); | 1127 content = content.slice(cutLength); |
| 1127 var rightContent = content.shift(); | 1128 var rightContent = content.shift(); |
| 1128 var innerNodes = this.processNeutralFences_(midFences, midContent); | 1129 var innerNodes = this.processNeutralFences_(midFences, midContent); |
| 1129 leftContent.push.apply(leftContent, innerNodes); | 1130 leftContent.push.apply(leftContent, innerNodes); |
| 1130 leftContent.push.apply(leftContent, rightContent); | 1131 leftContent.push.apply(leftContent, rightContent); |
| 1131 var fenced = this.makeHorizontalFencedNode_( | 1132 var fenced = |
| 1132 leftFence, rightFence, leftContent); | 1133 this.makeHorizontalFencedNode_(leftFence, rightFence, leftContent); |
| 1133 if (content.length > 0) { | 1134 if (content.length > 0) { |
| 1134 content[0].unshift(fenced); | 1135 content[0].unshift(fenced); |
| 1135 } else { | 1136 } else { |
| 1136 content = [[fenced]]; | 1137 content = [[fenced]]; |
| 1137 } | 1138 } |
| 1138 return content; | 1139 return content; |
| 1139 }; | 1140 }; |
| 1140 | 1141 |
| 1141 | 1142 |
| 1142 /** | 1143 /** |
| 1143 * Rewrite fences into punctuation. This is done with any "leftover" fence. | 1144 * Rewrite fences into punctuation. This is done with any "leftover" fence. |
| 1144 * @param {cvox.SemanticTree.Node} fence Fence. | 1145 * @param {cvox.SemanticTree.Node} fence Fence. |
| 1145 * @private | 1146 * @private |
| 1146 */ | 1147 */ |
| 1147 cvox.SemanticTree.fenceToPunct_ = function(fence) { | 1148 cvox.SemanticTree.fenceToPunct_ = function(fence) { |
| 1148 fence.type = cvox.SemanticAttr.Type.PUNCTUATION; | 1149 fence.type = cvox.SemanticAttr.Type.PUNCTUATION; |
| 1149 switch (fence.role) { | 1150 switch (fence.role) { |
| 1150 case cvox.SemanticAttr.Role.NEUTRAL: | 1151 case cvox.SemanticAttr.Role.NEUTRAL: |
| 1151 fence.role = cvox.SemanticAttr.Role.VBAR; | 1152 fence.role = cvox.SemanticAttr.Role.VBAR; |
| 1152 break; | 1153 break; |
| 1153 case cvox.SemanticAttr.Role.OPEN: | 1154 case cvox.SemanticAttr.Role.OPEN: |
| 1154 fence.role = cvox.SemanticAttr.Role.OPENFENCE; | 1155 fence.role = cvox.SemanticAttr.Role.OPENFENCE; |
| 1155 break; | 1156 break; |
| 1156 case cvox.SemanticAttr.Role.CLOSE: | 1157 case cvox.SemanticAttr.Role.CLOSE: |
| 1157 fence.role = cvox.SemanticAttr.Role.CLOSEFENCE; | 1158 fence.role = cvox.SemanticAttr.Role.CLOSEFENCE; |
| 1158 break; | 1159 break; |
| 1159 } | 1160 } |
| 1160 }; | 1161 }; |
| 1161 | 1162 |
| 1162 | 1163 |
| 1163 /** | 1164 /** |
| 1164 * Create a fenced node. | 1165 * Create a fenced node. |
| 1165 * @param {cvox.SemanticTree.Node} ofence Opening fence. | 1166 * @param {cvox.SemanticTree.Node} ofence Opening fence. |
| 1166 * @param {cvox.SemanticTree.Node} cfence Closing fence. | 1167 * @param {cvox.SemanticTree.Node} cfence Closing fence. |
| 1167 * @param {!Array<cvox.SemanticTree.Node>} content The content | 1168 * @param {!Array<cvox.SemanticTree.Node>} content The content |
| 1168 * between the fences. | 1169 * between the fences. |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1187 * Combines sequences of punctuated expressions in a list of nodes. | 1188 * Combines sequences of punctuated expressions in a list of nodes. |
| 1188 * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes. | 1189 * @param {!Array<cvox.SemanticTree.Node>} nodes The list of nodes. |
| 1189 * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes. | 1190 * @return {!Array<cvox.SemanticTree.Node>} The new list of nodes. |
| 1190 * @private | 1191 * @private |
| 1191 */ | 1192 */ |
| 1192 cvox.SemanticTree.prototype.getPunctuationInRow_ = function(nodes) { | 1193 cvox.SemanticTree.prototype.getPunctuationInRow_ = function(nodes) { |
| 1193 // For now we just make a punctuation node with a particular role. This is | 1194 // For now we just make a punctuation node with a particular role. This is |
| 1194 // similar to an mrow. The only exception are ellipses, which we assume to be | 1195 // similar to an mrow. The only exception are ellipses, which we assume to be |
| 1195 // in lieu of identifiers. | 1196 // in lieu of identifiers. |
| 1196 // In addition we keep the single punctuation nodes as content. | 1197 // In addition we keep the single punctuation nodes as content. |
| 1197 var partition = cvox.SemanticTree.partitionNodes_( | 1198 var partition = cvox.SemanticTree.partitionNodes_(nodes, function(x) { |
| 1198 nodes, function(x) { | 1199 return cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(x) && |
| 1199 return cvox.SemanticTree.attrPred_('type', 'PUNCTUATION')(x) && | 1200 !cvox.SemanticTree.attrPred_('role', 'ELLIPSIS')(x); |
| 1200 !cvox.SemanticTree.attrPred_('role', 'ELLIPSIS')(x);}); | 1201 }); |
| 1201 if (partition.rel.length == 0) { | 1202 if (partition.rel.length == 0) { |
| 1202 return nodes; | 1203 return nodes; |
| 1203 } | 1204 } |
| 1204 var newNodes = []; | 1205 var newNodes = []; |
| 1205 var firstComp = partition.comp.shift(); | 1206 var firstComp = partition.comp.shift(); |
| 1206 if (firstComp.length > 0) { | 1207 if (firstComp.length > 0) { |
| 1207 newNodes.push(this.processRow_(firstComp)); | 1208 newNodes.push(this.processRow_(firstComp)); |
| 1208 } | 1209 } |
| 1209 var relCounter = 0; | 1210 var relCounter = 0; |
| 1210 while (partition.comp.length > 0) { | 1211 while (partition.comp.length > 0) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1228 * @private | 1229 * @private |
| 1229 */ | 1230 */ |
| 1230 cvox.SemanticTree.prototype.makePunctuatedNode_ = function( | 1231 cvox.SemanticTree.prototype.makePunctuatedNode_ = function( |
| 1231 nodes, punctuations) { | 1232 nodes, punctuations) { |
| 1232 var newNode = this.makeBranchNode_( | 1233 var newNode = this.makeBranchNode_( |
| 1233 cvox.SemanticAttr.Type.PUNCTUATED, nodes, punctuations); | 1234 cvox.SemanticAttr.Type.PUNCTUATED, nodes, punctuations); |
| 1234 | 1235 |
| 1235 if (punctuations.length == 1 && | 1236 if (punctuations.length == 1 && |
| 1236 nodes[0].type == cvox.SemanticAttr.Type.PUNCTUATION) { | 1237 nodes[0].type == cvox.SemanticAttr.Type.PUNCTUATION) { |
| 1237 newNode.role = cvox.SemanticAttr.Role.STARTPUNCT; | 1238 newNode.role = cvox.SemanticAttr.Role.STARTPUNCT; |
| 1238 } else if (punctuations.length == 1 && | 1239 } else if ( |
| 1240 punctuations.length == 1 && |
| 1239 nodes[nodes.length - 1].type == cvox.SemanticAttr.Type.PUNCTUATION) { | 1241 nodes[nodes.length - 1].type == cvox.SemanticAttr.Type.PUNCTUATION) { |
| 1240 newNode.role = cvox.SemanticAttr.Role.ENDPUNCT; | 1242 newNode.role = cvox.SemanticAttr.Role.ENDPUNCT; |
| 1241 } else { | 1243 } else { |
| 1242 newNode.role = cvox.SemanticAttr.Role.SEQUENCE; | 1244 newNode.role = cvox.SemanticAttr.Role.SEQUENCE; |
| 1243 } | 1245 } |
| 1244 return newNode; | 1246 return newNode; |
| 1245 }; | 1247 }; |
| 1246 | 1248 |
| 1247 | 1249 |
| 1248 /** | 1250 /** |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1262 cvox.SemanticTree.attrPred_('type', 'LIMBOTH')(center) || | 1264 cvox.SemanticTree.attrPred_('type', 'LIMBOTH')(center) || |
| 1263 cvox.SemanticTree.attrPred_('type', 'LIMLOWER')(center) || | 1265 cvox.SemanticTree.attrPred_('type', 'LIMLOWER')(center) || |
| 1264 cvox.SemanticTree.attrPred_('type', 'LIMUPPER')(center) || | 1266 cvox.SemanticTree.attrPred_('type', 'LIMUPPER')(center) || |
| 1265 (isFunction && cvox.SemanticTree.attrPred_('role', 'LIMFUNC')(center)); | 1267 (isFunction && cvox.SemanticTree.attrPred_('role', 'LIMFUNC')(center)); |
| 1266 var type = cvox.SemanticAttr.Type.UNKNOWN; | 1268 var type = cvox.SemanticAttr.Type.UNKNOWN; |
| 1267 // TODO (sorge) Make use of the difference in information on sub vs under etc. | 1269 // TODO (sorge) Make use of the difference in information on sub vs under etc. |
| 1268 if (isLimit) { | 1270 if (isLimit) { |
| 1269 switch (mmlTag) { | 1271 switch (mmlTag) { |
| 1270 case 'MSUB': | 1272 case 'MSUB': |
| 1271 case 'MUNDER': | 1273 case 'MUNDER': |
| 1272 type = cvox.SemanticAttr.Type.LIMLOWER; | 1274 type = cvox.SemanticAttr.Type.LIMLOWER; |
| 1273 break; | 1275 break; |
| 1274 case 'MSUP': | 1276 case 'MSUP': |
| 1275 case 'MOVER': | 1277 case 'MOVER': |
| 1276 type = cvox.SemanticAttr.Type.LIMUPPER; | 1278 type = cvox.SemanticAttr.Type.LIMUPPER; |
| 1277 break; | 1279 break; |
| 1278 case 'MSUBSUP': | 1280 case 'MSUBSUP': |
| 1279 case 'MUNDEROVER': | 1281 case 'MUNDEROVER': |
| 1280 type = cvox.SemanticAttr.Type.LIMBOTH; | 1282 type = cvox.SemanticAttr.Type.LIMBOTH; |
| 1281 break; | 1283 break; |
| 1282 } | 1284 } |
| 1283 } else { | 1285 } else { |
| 1284 switch (mmlTag) { | 1286 switch (mmlTag) { |
| 1285 case 'MSUB': | 1287 case 'MSUB': |
| 1286 type = cvox.SemanticAttr.Type.SUBSCRIPT; | 1288 type = cvox.SemanticAttr.Type.SUBSCRIPT; |
| 1287 break; | 1289 break; |
| 1288 case 'MSUP': | 1290 case 'MSUP': |
| 1289 type = cvox.SemanticAttr.Type.SUPERSCRIPT; | 1291 type = cvox.SemanticAttr.Type.SUPERSCRIPT; |
| 1290 break; | 1292 break; |
| 1291 case 'MSUBSUP': | 1293 case 'MSUBSUP': |
| 1292 var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.SUBSCRIPT, | 1294 var innerNode = this.makeBranchNode_( |
| 1293 [center, children[1]], []); | 1295 cvox.SemanticAttr.Type.SUBSCRIPT, [center, children[1]], []); |
| 1294 innerNode.role = center.role; | 1296 innerNode.role = center.role; |
| 1295 children = [innerNode, children[2]]; | 1297 children = [innerNode, children[2]]; |
| 1296 type = cvox.SemanticAttr.Type.SUPERSCRIPT; | 1298 type = cvox.SemanticAttr.Type.SUPERSCRIPT; |
| 1297 break; | 1299 break; |
| 1298 case 'MOVER': | 1300 case 'MOVER': |
| 1299 type = cvox.SemanticAttr.Type.OVERSCORE; | 1301 type = cvox.SemanticAttr.Type.OVERSCORE; |
| 1300 break; | 1302 break; |
| 1301 case 'MUNDER': | 1303 case 'MUNDER': |
| 1302 type = cvox.SemanticAttr.Type.UNDERSCORE; | 1304 type = cvox.SemanticAttr.Type.UNDERSCORE; |
| 1303 break; | 1305 break; |
| 1304 case 'MUNDEROVER': | 1306 case 'MUNDEROVER': |
| 1305 default: | 1307 default: |
| 1306 var innerNode = this.makeBranchNode_(cvox.SemanticAttr.Type.UNDERSCORE, | 1308 var innerNode = this.makeBranchNode_( |
| 1307 [center, children[1]], []); | 1309 cvox.SemanticAttr.Type.UNDERSCORE, [center, children[1]], []); |
| 1308 innerNode.role = center.role; | 1310 innerNode.role = center.role; |
| 1309 children = [innerNode, children[2]]; | 1311 children = [innerNode, children[2]]; |
| 1310 type = cvox.SemanticAttr.Type.OVERSCORE; | 1312 type = cvox.SemanticAttr.Type.OVERSCORE; |
| 1311 break; | 1313 break; |
| 1312 } | 1314 } |
| 1313 } | 1315 } |
| 1314 var newNode = this.makeBranchNode_(type, children, []); | 1316 var newNode = this.makeBranchNode_(type, children, []); |
| 1315 newNode.role = center.role; | 1317 newNode.role = center.role; |
| 1316 return newNode; | 1318 return newNode; |
| 1317 }; | 1319 }; |
| 1318 | 1320 |
| 1319 | 1321 |
| 1320 /** | 1322 /** |
| 1321 * Recursive method to accumulate function expressions. | 1323 * Recursive method to accumulate function expressions. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1364 * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of | 1366 * @param {!Array<cvox.SemanticTree.Node>} restNodes The remainder list of |
| 1365 * nodes. They can useful to look ahead if there is an explicit function | 1367 * nodes. They can useful to look ahead if there is an explicit function |
| 1366 * application. If there is one, it will be destructively removed! | 1368 * application. If there is one, it will be destructively removed! |
| 1367 * @return {!string} The string specifying the heuristic. | 1369 * @return {!string} The string specifying the heuristic. |
| 1368 * @private | 1370 * @private |
| 1369 */ | 1371 */ |
| 1370 cvox.SemanticTree.classifyFunction_ = function(funcNode, restNodes) { | 1372 cvox.SemanticTree.classifyFunction_ = function(funcNode, restNodes) { |
| 1371 // We do not allow double function application. This is not lambda calculus! | 1373 // We do not allow double function application. This is not lambda calculus! |
| 1372 if (funcNode.type == cvox.SemanticAttr.Type.APPL || | 1374 if (funcNode.type == cvox.SemanticAttr.Type.APPL || |
| 1373 funcNode.type == cvox.SemanticAttr.Type.BIGOP || | 1375 funcNode.type == cvox.SemanticAttr.Type.BIGOP || |
| 1374 funcNode.type == cvox.SemanticAttr.Type.INTEGRAL) { | 1376 funcNode.type == cvox.SemanticAttr.Type.INTEGRAL) { |
| 1375 return ''; | 1377 return ''; |
| 1376 } | 1378 } |
| 1377 // Find and remove explicit function applications. | 1379 // Find and remove explicit function applications. |
| 1378 // We now treat funcNode as a prefix function, regardless of what its actual | 1380 // We now treat funcNode as a prefix function, regardless of what its actual |
| 1379 // content is. | 1381 // content is. |
| 1380 if (restNodes[0] && | 1382 if (restNodes[0] && |
| 1381 restNodes[0].textContent == cvox.SemanticAttr.functionApplication()) { | 1383 restNodes[0].textContent == cvox.SemanticAttr.functionApplication()) { |
| 1382 // Remove explicit function application. This is destructive on the | 1384 // Remove explicit function application. This is destructive on the |
| 1383 // underlying list. | 1385 // underlying list. |
| 1384 restNodes.shift(); | 1386 restNodes.shift(); |
| 1385 cvox.SemanticTree.propagatePrefixFunc_(funcNode); | 1387 cvox.SemanticTree.propagatePrefixFunc_(funcNode); |
| 1386 return 'prefix'; | 1388 return 'prefix'; |
| 1387 } | 1389 } |
| 1388 switch (funcNode.role) { | 1390 switch (funcNode.role) { |
| 1389 case cvox.SemanticAttr.Role.INTEGRAL: | 1391 case cvox.SemanticAttr.Role.INTEGRAL: |
| 1390 return 'integral'; | 1392 return 'integral'; |
| 1391 break; | 1393 break; |
| 1392 case cvox.SemanticAttr.Role.SUM: | 1394 case cvox.SemanticAttr.Role.SUM: |
| 1393 return 'bigop'; | 1395 return 'bigop'; |
| 1394 break; | 1396 break; |
| 1395 case cvox.SemanticAttr.Role.PREFIXFUNC: | 1397 case cvox.SemanticAttr.Role.PREFIXFUNC: |
| 1396 case cvox.SemanticAttr.Role.LIMFUNC: | 1398 case cvox.SemanticAttr.Role.LIMFUNC: |
| 1397 return 'prefix'; | 1399 return 'prefix'; |
| 1398 break; | 1400 break; |
| 1399 default: | 1401 default: |
| 1400 if (funcNode.type == cvox.SemanticAttr.Type.IDENTIFIER) { | 1402 if (funcNode.type == cvox.SemanticAttr.Type.IDENTIFIER) { |
| 1401 return 'simple'; | 1403 return 'simple'; |
| 1402 } | 1404 } |
| 1403 } | 1405 } |
| 1404 return ''; | 1406 return ''; |
| 1405 }; | 1407 }; |
| 1406 | 1408 |
| 1407 | 1409 |
| 1408 /** | 1410 /** |
| 1409 * Propagates a prefix function role in a node. | 1411 * Propagates a prefix function role in a node. |
| 1410 * @param {cvox.SemanticTree.Node} funcNode The node whose role is to be | 1412 * @param {cvox.SemanticTree.Node} funcNode The node whose role is to be |
| 1411 * rewritten. | 1413 * rewritten. |
| 1412 * @private | 1414 * @private |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1426 * @param {!Array<cvox.SemanticTree.Node>} rest List of nodes to choose | 1428 * @param {!Array<cvox.SemanticTree.Node>} rest List of nodes to choose |
| 1427 * arguments from. | 1429 * arguments from. |
| 1428 * @param {string} heuristic The heuristic to follow. | 1430 * @param {string} heuristic The heuristic to follow. |
| 1429 * @return {!Array<!cvox.SemanticTree.Node>} The function and the remainder of | 1431 * @return {!Array<!cvox.SemanticTree.Node>} The function and the remainder of |
| 1430 * the rest list. | 1432 * the rest list. |
| 1431 * @private | 1433 * @private |
| 1432 */ | 1434 */ |
| 1433 cvox.SemanticTree.prototype.getFunctionArgs_ = function(func, rest, heuristic) { | 1435 cvox.SemanticTree.prototype.getFunctionArgs_ = function(func, rest, heuristic) { |
| 1434 switch (heuristic) { | 1436 switch (heuristic) { |
| 1435 case 'integral': | 1437 case 'integral': |
| 1436 var components = this.getIntegralArgs_(rest); | 1438 var components = this.getIntegralArgs_(rest); |
| 1437 var integrand = this.processRow_(components.integrand); | 1439 var integrand = this.processRow_(components.integrand); |
| 1438 var funcNode = this.makeIntegralNode_(func, integrand, components.intvar); | 1440 var funcNode = this.makeIntegralNode_(func, integrand, components.intvar); |
| 1439 components.rest.unshift(funcNode); | 1441 components.rest.unshift(funcNode); |
| 1440 return components.rest; | 1442 return components.rest; |
| 1441 break; | 1443 break; |
| 1442 case 'prefix': | 1444 case 'prefix': |
| 1443 if (rest[0] && rest[0].type == cvox.SemanticAttr.Type.FENCED) { | 1445 if (rest[0] && rest[0].type == cvox.SemanticAttr.Type.FENCED) { |
| 1444 funcNode = this.makeFunctionNode_( | 1446 funcNode = this.makeFunctionNode_( |
| 1445 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); | 1447 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); |
| 1446 rest.unshift(funcNode); | 1448 rest.unshift(funcNode); |
| 1449 return rest; |
| 1450 } |
| 1451 case 'bigop': |
| 1452 var partition = cvox.SemanticTree.sliceNodes_( |
| 1453 rest, cvox.SemanticTree.prefixFunctionBoundary_); |
| 1454 var arg = this.processRow_(partition.head); |
| 1455 if (heuristic == 'prefix') { |
| 1456 funcNode = this.makeFunctionNode_(func, arg); |
| 1457 } else { |
| 1458 funcNode = this.makeBigOpNode_(func, arg); |
| 1459 } |
| 1460 if (partition.div) { |
| 1461 partition.tail.unshift(partition.div); |
| 1462 } |
| 1463 partition.tail.unshift(funcNode); |
| 1464 return partition.tail; |
| 1465 break; |
| 1466 case 'simple': |
| 1467 if (rest.length == 0) { |
| 1468 return [func]; |
| 1469 } |
| 1470 var firstArg = rest[0]; |
| 1471 if (firstArg.type == cvox.SemanticAttr.Type.FENCED && |
| 1472 firstArg.role != cvox.SemanticAttr.Role.NEUTRAL && |
| 1473 this.simpleFunctionHeuristic_(firstArg)) { |
| 1474 funcNode = this.makeFunctionNode_( |
| 1475 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); |
| 1476 rest.unshift(funcNode); |
| 1477 return rest; |
| 1478 } |
| 1479 rest.unshift(func); |
| 1447 return rest; | 1480 return rest; |
| 1448 } | 1481 break; |
| 1449 case 'bigop': | |
| 1450 var partition = cvox.SemanticTree.sliceNodes_( | |
| 1451 rest, cvox.SemanticTree.prefixFunctionBoundary_); | |
| 1452 var arg = this.processRow_(partition.head); | |
| 1453 if (heuristic == 'prefix') { | |
| 1454 funcNode = this.makeFunctionNode_(func, arg); | |
| 1455 } else { | |
| 1456 funcNode = this.makeBigOpNode_(func, arg); | |
| 1457 } | |
| 1458 if (partition.div) { | |
| 1459 partition.tail.unshift(partition.div); | |
| 1460 } | |
| 1461 partition.tail.unshift(funcNode); | |
| 1462 return partition.tail; | |
| 1463 break; | |
| 1464 case 'simple': | |
| 1465 if (rest.length == 0) { | |
| 1466 return [func]; | |
| 1467 } | |
| 1468 var firstArg = rest[0]; | |
| 1469 if (firstArg.type == cvox.SemanticAttr.Type.FENCED && | |
| 1470 firstArg.role != cvox.SemanticAttr.Role.NEUTRAL && | |
| 1471 this.simpleFunctionHeuristic_(firstArg)) { | |
| 1472 funcNode = this.makeFunctionNode_( | |
| 1473 func, /** @type {!cvox.SemanticTree.Node} */ (rest.shift())); | |
| 1474 rest.unshift(funcNode); | |
| 1475 return rest; | |
| 1476 } | |
| 1477 rest.unshift(func); | |
| 1478 return rest; | |
| 1479 break; | |
| 1480 } | 1482 } |
| 1481 return []; | 1483 return []; |
| 1482 }; | 1484 }; |
| 1483 | 1485 |
| 1484 | 1486 |
| 1485 /** | 1487 /** |
| 1486 * Tail recursive function to obtain integral arguments. | 1488 * Tail recursive function to obtain integral arguments. |
| 1487 * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to take | 1489 * @param {!Array<cvox.SemanticTree.Node>} nodes List of nodes to take |
| 1488 * arguments from. | 1490 * arguments from. |
| 1489 * @param {Array<cvox.SemanticTree.Node>=} args List of integral arguments. | 1491 * @param {Array<cvox.SemanticTree.Node>=} args List of integral arguments. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1502 var firstNode = nodes[0]; | 1504 var firstNode = nodes[0]; |
| 1503 if (cvox.SemanticTree.generalFunctionBoundary_(firstNode)) { | 1505 if (cvox.SemanticTree.generalFunctionBoundary_(firstNode)) { |
| 1504 return {integrand: args, intvar: null, rest: nodes}; | 1506 return {integrand: args, intvar: null, rest: nodes}; |
| 1505 } | 1507 } |
| 1506 if (cvox.SemanticTree.integralDxBoundarySingle_(firstNode)) { | 1508 if (cvox.SemanticTree.integralDxBoundarySingle_(firstNode)) { |
| 1507 return {integrand: args, intvar: firstNode, rest: nodes.slice(1)}; | 1509 return {integrand: args, intvar: firstNode, rest: nodes.slice(1)}; |
| 1508 } | 1510 } |
| 1509 if (nodes[1] && cvox.SemanticTree.integralDxBoundary_(firstNode, nodes[1])) { | 1511 if (nodes[1] && cvox.SemanticTree.integralDxBoundary_(firstNode, nodes[1])) { |
| 1510 var comma = this.createNode_(); | 1512 var comma = this.createNode_(); |
| 1511 comma.updateContent_(cvox.SemanticAttr.invisibleComma()); | 1513 comma.updateContent_(cvox.SemanticAttr.invisibleComma()); |
| 1512 var intvar = this.makePunctuatedNode_( | 1514 var intvar = |
| 1513 [firstNode, comma, nodes[1]], [comma]); | 1515 this.makePunctuatedNode_([firstNode, comma, nodes[1]], [comma]); |
| 1514 intvar.role = cvox.SemanticAttr.Role.INTEGRAL; | 1516 intvar.role = cvox.SemanticAttr.Role.INTEGRAL; |
| 1515 return {integrand: args, intvar: intvar, rest: nodes.slice(2)}; | 1517 return {integrand: args, intvar: intvar, rest: nodes.slice(2)}; |
| 1516 } | 1518 } |
| 1517 args.push(nodes.shift()); | 1519 args.push(nodes.shift()); |
| 1518 return this.getIntegralArgs_(nodes, args); | 1520 return this.getIntegralArgs_(nodes, args); |
| 1519 }; | 1521 }; |
| 1520 | 1522 |
| 1521 | 1523 |
| 1522 /** | 1524 /** |
| 1523 * Create a function node. | 1525 * Create a function node. |
| 1524 * @param {!cvox.SemanticTree.Node} func The function operator. | 1526 * @param {!cvox.SemanticTree.Node} func The function operator. |
| 1525 * @param {!cvox.SemanticTree.Node} arg The argument. | 1527 * @param {!cvox.SemanticTree.Node} arg The argument. |
| 1526 * @return {!cvox.SemanticTree.Node} The new function node. | 1528 * @return {!cvox.SemanticTree.Node} The new function node. |
| 1527 * @private | 1529 * @private |
| 1528 */ | 1530 */ |
| 1529 cvox.SemanticTree.prototype.makeFunctionNode_ = function(func, arg) { | 1531 cvox.SemanticTree.prototype.makeFunctionNode_ = function(func, arg) { |
| 1530 var applNode = this.createNode_(); | 1532 var applNode = this.createNode_(); |
| 1531 applNode.updateContent_(cvox.SemanticAttr.functionApplication()); | 1533 applNode.updateContent_(cvox.SemanticAttr.functionApplication()); |
| 1532 applNode.type = cvox.SemanticAttr.Type.PUNCTUATION; | 1534 applNode.type = cvox.SemanticAttr.Type.PUNCTUATION; |
| 1533 applNode.role = cvox.SemanticAttr.Role.APPLICATION; | 1535 applNode.role = cvox.SemanticAttr.Role.APPLICATION; |
| 1534 var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.APPL, [func, arg], | 1536 var newNode = this.makeBranchNode_( |
| 1535 [applNode]); | 1537 cvox.SemanticAttr.Type.APPL, [func, arg], [applNode]); |
| 1536 newNode.role = func.role; | 1538 newNode.role = func.role; |
| 1537 return newNode; | 1539 return newNode; |
| 1538 }; | 1540 }; |
| 1539 | 1541 |
| 1540 | 1542 |
| 1541 /** | 1543 /** |
| 1542 * Create a big operator node. | 1544 * Create a big operator node. |
| 1543 * @param {!cvox.SemanticTree.Node} bigOp The big operator. | 1545 * @param {!cvox.SemanticTree.Node} bigOp The big operator. |
| 1544 * @param {!cvox.SemanticTree.Node} arg The argument. | 1546 * @param {!cvox.SemanticTree.Node} arg The argument. |
| 1545 * @return {!cvox.SemanticTree.Node} The new big operator node. | 1547 * @return {!cvox.SemanticTree.Node} The new big operator node. |
| 1546 * @private | 1548 * @private |
| 1547 */ | 1549 */ |
| 1548 cvox.SemanticTree.prototype.makeBigOpNode_ = function(bigOp, arg) { | 1550 cvox.SemanticTree.prototype.makeBigOpNode_ = function(bigOp, arg) { |
| 1549 var newNode = this.makeBranchNode_( | 1551 var newNode = |
| 1550 cvox.SemanticAttr.Type.BIGOP, [bigOp, arg], []); | 1552 this.makeBranchNode_(cvox.SemanticAttr.Type.BIGOP, [bigOp, arg], []); |
| 1551 newNode.role = bigOp.role; | 1553 newNode.role = bigOp.role; |
| 1552 return newNode; | 1554 return newNode; |
| 1553 }; | 1555 }; |
| 1554 | 1556 |
| 1555 | 1557 |
| 1556 /** | 1558 /** |
| 1557 * Create an integral node. It has three children: integral, integrand and | 1559 * Create an integral node. It has three children: integral, integrand and |
| 1558 * integration variable. The latter two can be omitted. | 1560 * integration variable. The latter two can be omitted. |
| 1559 * @param {!cvox.SemanticTree.Node} integral The integral operator. | 1561 * @param {!cvox.SemanticTree.Node} integral The integral operator. |
| 1560 * @param {cvox.SemanticTree.Node} integrand The integrand. | 1562 * @param {cvox.SemanticTree.Node} integrand The integrand. |
| 1561 * @param {cvox.SemanticTree.Node} intvar The integral variable. | 1563 * @param {cvox.SemanticTree.Node} intvar The integral variable. |
| 1562 * @return {!cvox.SemanticTree.Node} The new integral node. | 1564 * @return {!cvox.SemanticTree.Node} The new integral node. |
| 1563 * @private | 1565 * @private |
| 1564 */ | 1566 */ |
| 1565 cvox.SemanticTree.prototype.makeIntegralNode_ = function( | 1567 cvox.SemanticTree.prototype.makeIntegralNode_ = function( |
| 1566 integral, integrand, intvar) { | 1568 integral, integrand, intvar) { |
| 1567 integrand = integrand || this.makeEmptyNode_(); | 1569 integrand = integrand || this.makeEmptyNode_(); |
| 1568 intvar = intvar || this.makeEmptyNode_(); | 1570 intvar = intvar || this.makeEmptyNode_(); |
| 1569 var newNode = this.makeBranchNode_(cvox.SemanticAttr.Type.INTEGRAL, | 1571 var newNode = this.makeBranchNode_( |
| 1570 [integral, integrand, intvar], []); | 1572 cvox.SemanticAttr.Type.INTEGRAL, [integral, integrand, intvar], []); |
| 1571 newNode.role = integral.role; | 1573 newNode.role = integral.role; |
| 1572 return newNode; | 1574 return newNode; |
| 1573 }; | 1575 }; |
| 1574 | 1576 |
| 1575 | 1577 |
| 1576 /** | 1578 /** |
| 1577 * Predicate implementing the boundary criteria for simple functions: | 1579 * Predicate implementing the boundary criteria for simple functions: |
| 1578 * | 1580 * |
| 1579 * @param {!cvox.SemanticTree.Node} node A semantic node of type fenced. | 1581 * @param {!cvox.SemanticTree.Node} node A semantic node of type fenced. |
| 1580 * @return {boolean} True if the node meets the boundary criteria. | 1582 * @return {boolean} True if the node meets the boundary criteria. |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1618 | 1620 |
| 1619 | 1621 |
| 1620 /** | 1622 /** |
| 1621 * Predicate implementing the boundary criteria for integrals dx on two nodes. | 1623 * Predicate implementing the boundary criteria for integrals dx on two nodes. |
| 1622 * @param {cvox.SemanticTree.Node} firstNode A semantic node. | 1624 * @param {cvox.SemanticTree.Node} firstNode A semantic node. |
| 1623 * @param {cvox.SemanticTree.Node} secondNode The direct neighbour of first | 1625 * @param {cvox.SemanticTree.Node} secondNode The direct neighbour of first |
| 1624 * Node. | 1626 * Node. |
| 1625 * @return {boolean} True if the second node exists and the first node is a 'd'. | 1627 * @return {boolean} True if the second node exists and the first node is a 'd'. |
| 1626 * @private | 1628 * @private |
| 1627 */ | 1629 */ |
| 1628 cvox.SemanticTree.integralDxBoundary_ = function( | 1630 cvox.SemanticTree.integralDxBoundary_ = function(firstNode, secondNode) { |
| 1629 firstNode, secondNode) { | |
| 1630 return !!secondNode && | 1631 return !!secondNode && |
| 1631 cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(secondNode) && | 1632 cvox.SemanticTree.attrPred_('type', 'IDENTIFIER')(secondNode) && |
| 1632 cvox.SemanticAttr.isCharacterD(firstNode.textContent); | 1633 cvox.SemanticAttr.isCharacterD(firstNode.textContent); |
| 1633 }; | 1634 }; |
| 1634 | 1635 |
| 1635 | 1636 |
| 1636 /** | 1637 /** |
| 1637 * Predicate implementing the boundary criteria for integrals dx on a single | 1638 * Predicate implementing the boundary criteria for integrals dx on a single |
| 1638 * node. | 1639 * node. |
| 1639 * @param {cvox.SemanticTree.Node} node A semantic node. | 1640 * @param {cvox.SemanticTree.Node} node A semantic node. |
| 1640 * @return {boolean} True if the node meets the boundary criteria. | 1641 * @return {boolean} True if the node meets the boundary criteria. |
| 1641 * @private | 1642 * @private |
| 1642 */ | 1643 */ |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1697 }; | 1698 }; |
| 1698 | 1699 |
| 1699 | 1700 |
| 1700 /** | 1701 /** |
| 1701 * Decides if a node is a table or multiline element. | 1702 * Decides if a node is a table or multiline element. |
| 1702 * @param {cvox.SemanticTree.Node} node A node. | 1703 * @param {cvox.SemanticTree.Node} node A node. |
| 1703 * @return {boolean} True if node is either table or multiline. | 1704 * @return {boolean} True if node is either table or multiline. |
| 1704 * @private | 1705 * @private |
| 1705 */ | 1706 */ |
| 1706 cvox.SemanticTree.isTableOrMultiline_ = function(node) { | 1707 cvox.SemanticTree.isTableOrMultiline_ = function(node) { |
| 1707 return !!node && (cvox.SemanticTree.attrPred_('type', 'TABLE')(node) || | 1708 return !!node && |
| 1708 cvox.SemanticTree.attrPred_('type', 'MULTILINE')(node)); | 1709 (cvox.SemanticTree.attrPred_('type', 'TABLE')(node) || |
| 1710 cvox.SemanticTree.attrPred_('type', 'MULTILINE')(node)); |
| 1709 }; | 1711 }; |
| 1710 | 1712 |
| 1711 | 1713 |
| 1712 /** | 1714 /** |
| 1713 * Heuristic to decide if we have a matrix: An expression fenced on both sides | 1715 * Heuristic to decide if we have a matrix: An expression fenced on both sides |
| 1714 * without any other content is considered a fenced node. | 1716 * without any other content is considered a fenced node. |
| 1715 * @param {cvox.SemanticTree.Node} node A node. | 1717 * @param {cvox.SemanticTree.Node} node A node. |
| 1716 * @return {boolean} True if we believe we have a matrix. | 1718 * @return {boolean} True if we believe we have a matrix. |
| 1717 * @private | 1719 * @private |
| 1718 */ | 1720 */ |
| 1719 cvox.SemanticTree.tableIsMatrixOrVector_ = function(node) { | 1721 cvox.SemanticTree.tableIsMatrixOrVector_ = function(node) { |
| 1720 return !!node && cvox.SemanticTree.attrPred_('type', 'FENCED')(node) && | 1722 return !!node && cvox.SemanticTree.attrPred_('type', 'FENCED')(node) && |
| 1721 cvox.SemanticTree.attrPred_('role', 'LEFTRIGHT')(node) && | 1723 cvox.SemanticTree.attrPred_('role', 'LEFTRIGHT')(node) && |
| 1722 node.childNodes.length == 1 && | 1724 node.childNodes.length == 1 && |
| 1723 cvox.SemanticTree.isTableOrMultiline_(node.childNodes[0]); | 1725 cvox.SemanticTree.isTableOrMultiline_(node.childNodes[0]); |
| 1724 }; | 1726 }; |
| 1725 | 1727 |
| 1726 | 1728 |
| 1727 /** | 1729 /** |
| 1728 * Replaces a fenced node by a matrix or vector node. | 1730 * Replaces a fenced node by a matrix or vector node. |
| 1729 * @param {!cvox.SemanticTree.Node} node The fenced node to be rewritten. | 1731 * @param {!cvox.SemanticTree.Node} node The fenced node to be rewritten. |
| 1730 * @return {!cvox.SemanticTree.Node} The matrix or vector node. | 1732 * @return {!cvox.SemanticTree.Node} The matrix or vector node. |
| 1731 * @private | 1733 * @private |
| 1732 */ | 1734 */ |
| 1733 cvox.SemanticTree.prototype.tableToMatrixOrVector_ = function(node) { | 1735 cvox.SemanticTree.prototype.tableToMatrixOrVector_ = function(node) { |
| 1734 var matrix = node.childNodes[0]; | 1736 var matrix = node.childNodes[0]; |
| 1735 var type = cvox.SemanticTree.attrPred_('type', 'MULTILINE')(matrix) ? | 1737 var type = cvox.SemanticTree.attrPred_('type', 'MULTILINE')(matrix) ? |
| 1736 'VECTOR' : 'MATRIX'; | 1738 'VECTOR' : |
| 1739 'MATRIX'; |
| 1737 matrix.type = cvox.SemanticAttr.Type[type]; | 1740 matrix.type = cvox.SemanticAttr.Type[type]; |
| 1738 node.contentNodes.forEach(goog.bind(matrix.appendContentNode_, matrix)); | 1741 node.contentNodes.forEach(goog.bind(matrix.appendContentNode_, matrix)); |
| 1739 for (var i = 0, row; row = matrix.childNodes[i]; i++) { | 1742 for (var i = 0, row; row = matrix.childNodes[i]; i++) { |
| 1740 cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role[type]); | 1743 cvox.SemanticTree.assignRoleToRow_(row, cvox.SemanticAttr.Role[type]); |
| 1741 } | 1744 } |
| 1742 return matrix; | 1745 return matrix; |
| 1743 }; | 1746 }; |
| 1744 | 1747 |
| 1745 | 1748 |
| 1746 /** | 1749 /** |
| 1747 * Heuristic to decide if we have a case statement: An expression with a | 1750 * Heuristic to decide if we have a case statement: An expression with a |
| 1748 * singular open fence before it. | 1751 * singular open fence before it. |
| 1749 * @param {!cvox.SemanticTree.Node} table A table node. | 1752 * @param {!cvox.SemanticTree.Node} table A table node. |
| 1750 * @param {!Array<cvox.SemanticTree.Node>} prevNodes A list of previous nodes. | 1753 * @param {!Array<cvox.SemanticTree.Node>} prevNodes A list of previous nodes. |
| 1751 * @return {boolean} True if we believe we have a case statement. | 1754 * @return {boolean} True if we believe we have a case statement. |
| 1752 * @private | 1755 * @private |
| 1753 */ | 1756 */ |
| 1754 cvox.SemanticTree.tableIsCases_ = function(table, prevNodes) { | 1757 cvox.SemanticTree.tableIsCases_ = function(table, prevNodes) { |
| 1755 return prevNodes.length > 0 && | 1758 return prevNodes.length > 0 && |
| 1756 cvox.SemanticTree.attrPred_('role', 'OPENFENCE')( | 1759 cvox.SemanticTree.attrPred_( |
| 1757 prevNodes[prevNodes.length - 1]); | 1760 'role', 'OPENFENCE')(prevNodes[prevNodes.length - 1]); |
| 1758 }; | 1761 }; |
| 1759 | 1762 |
| 1760 | 1763 |
| 1761 /** | 1764 /** |
| 1762 * Makes case node out of a table and a fence. | 1765 * Makes case node out of a table and a fence. |
| 1763 * @param {!cvox.SemanticTree.Node} table The table containing the cases. | 1766 * @param {!cvox.SemanticTree.Node} table The table containing the cases. |
| 1764 * @param {!cvox.SemanticTree.Node} openFence The left delimiter of the case | 1767 * @param {!cvox.SemanticTree.Node} openFence The left delimiter of the case |
| 1765 * statement. | 1768 * statement. |
| 1766 * @return {!cvox.SemanticTree.Node} The cases node. | 1769 * @return {!cvox.SemanticTree.Node} The cases node. |
| 1767 * @private | 1770 * @private |
| (...skipping 15 matching lines...) Expand all Loading... |
| 1783 // similarities in columns (e.g., single relation symbols, like equalities or | 1786 // similarities in columns (e.g., single relation symbols, like equalities or |
| 1784 // inequalities in the same column could indicate an equation array). | 1787 // inequalities in the same column could indicate an equation array). |
| 1785 /** | 1788 /** |
| 1786 * Heuristic to decide if we have a multiline formula. A table is considered a | 1789 * Heuristic to decide if we have a multiline formula. A table is considered a |
| 1787 * multiline formula if it does not have any separate cells. | 1790 * multiline formula if it does not have any separate cells. |
| 1788 * @param {!cvox.SemanticTree.Node} table A table node. | 1791 * @param {!cvox.SemanticTree.Node} table A table node. |
| 1789 * @return {boolean} True if we believe we have a mulitline formula. | 1792 * @return {boolean} True if we believe we have a mulitline formula. |
| 1790 * @private | 1793 * @private |
| 1791 */ | 1794 */ |
| 1792 cvox.SemanticTree.tableIsMultiline_ = function(table) { | 1795 cvox.SemanticTree.tableIsMultiline_ = function(table) { |
| 1793 return table.childNodes.every( | 1796 return table.childNodes.every(function(row) { |
| 1794 function(row) { | 1797 var length = row.childNodes.length; |
| 1795 var length = row.childNodes.length; | 1798 return length <= 1; |
| 1796 return length <= 1;}); | 1799 }); |
| 1797 }; | 1800 }; |
| 1798 | 1801 |
| 1799 | 1802 |
| 1800 /** | 1803 /** |
| 1801 * Rewrites a table to multiline structure, simplifying it by getting rid of the | 1804 * Rewrites a table to multiline structure, simplifying it by getting rid of the |
| 1802 * cell hierarchy level. | 1805 * cell hierarchy level. |
| 1803 * @param {!cvox.SemanticTree.Node} table The node to be rewritten a multiline. | 1806 * @param {!cvox.SemanticTree.Node} table The node to be rewritten a multiline. |
| 1804 * @private | 1807 * @private |
| 1805 */ | 1808 */ |
| 1806 cvox.SemanticTree.prototype.tableToMultiline_ = function(table) { | 1809 cvox.SemanticTree.prototype.tableToMultiline_ = function(table) { |
| 1807 table.type = cvox.SemanticAttr.Type.MULTILINE; | 1810 table.type = cvox.SemanticAttr.Type.MULTILINE; |
| 1808 for (var i = 0, row; row = table.childNodes[i]; i++) { | 1811 for (var i = 0, row; row = table.childNodes[i]; i++) { |
| 1809 cvox.SemanticTree.rowToLine_(row, cvox.SemanticAttr.Role.MULTILINE); | 1812 cvox.SemanticTree.rowToLine_(row, cvox.SemanticAttr.Role.MULTILINE); |
| 1810 } | 1813 } |
| 1811 }; | 1814 }; |
| 1812 | 1815 |
| 1813 | 1816 |
| 1814 /** | 1817 /** |
| 1815 * Converts a row that only contains one cell into a single line. | 1818 * Converts a row that only contains one cell into a single line. |
| 1816 * @param {!cvox.SemanticTree.Node} row The row to convert. | 1819 * @param {!cvox.SemanticTree.Node} row The row to convert. |
| 1817 * @param {cvox.SemanticAttr.Role=} role The new role for the line. | 1820 * @param {cvox.SemanticAttr.Role=} role The new role for the line. |
| 1818 * @private | 1821 * @private |
| 1819 */ | 1822 */ |
| 1820 cvox.SemanticTree.rowToLine_ = function(row, role) { | 1823 cvox.SemanticTree.rowToLine_ = function(row, role) { |
| 1821 role = role || cvox.SemanticAttr.Role.UNKNOWN; | 1824 role = role || cvox.SemanticAttr.Role.UNKNOWN; |
| 1822 if (cvox.SemanticTree.attrPred_('type', 'ROW')(row) && | 1825 if (cvox.SemanticTree.attrPred_('type', 'ROW')(row) && |
| 1823 row.childNodes.length == 1 && | 1826 row.childNodes.length == 1 && |
| 1824 cvox.SemanticTree.attrPred_('type', 'CELL')(row.childNodes[0])) { | 1827 cvox.SemanticTree.attrPred_('type', 'CELL')(row.childNodes[0])) { |
| 1825 row.type = cvox.SemanticAttr.Type.LINE; | 1828 row.type = cvox.SemanticAttr.Type.LINE; |
| 1826 row.role = role; | 1829 row.role = role; |
| 1827 row.childNodes = row.childNodes[0].childNodes; | 1830 row.childNodes = row.childNodes[0].childNodes; |
| 1828 } | 1831 } |
| 1829 }; | 1832 }; |
| 1830 | 1833 |
| 1831 | 1834 |
| 1832 /** | 1835 /** |
| 1833 * Assign a row and its contained cells a new role value. | 1836 * Assign a row and its contained cells a new role value. |
| 1834 * @param {!cvox.SemanticTree.Node} row The row to be updated. | 1837 * @param {!cvox.SemanticTree.Node} row The row to be updated. |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1864 * @private | 1867 * @private |
| 1865 */ | 1868 */ |
| 1866 cvox.SemanticTree.sliceNodes_ = function(nodes, pred, reverse) { | 1869 cvox.SemanticTree.sliceNodes_ = function(nodes, pred, reverse) { |
| 1867 if (reverse) { | 1870 if (reverse) { |
| 1868 nodes.reverse(); | 1871 nodes.reverse(); |
| 1869 } | 1872 } |
| 1870 var head = []; | 1873 var head = []; |
| 1871 for (var i = 0, node; node = nodes[i]; i++) { | 1874 for (var i = 0, node; node = nodes[i]; i++) { |
| 1872 if (pred(node)) { | 1875 if (pred(node)) { |
| 1873 if (reverse) { | 1876 if (reverse) { |
| 1874 return {head: nodes.slice(i + 1).reverse(), | 1877 return { |
| 1875 div: node, | 1878 head: nodes.slice(i + 1).reverse(), |
| 1876 tail: head.reverse()}; | 1879 div: node, |
| 1880 tail: head.reverse() |
| 1881 }; |
| 1877 } | 1882 } |
| 1878 return {head: head, | 1883 return {head: head, div: node, tail: nodes.slice(i + 1)}; |
| 1879 div: node, | |
| 1880 tail: nodes.slice(i + 1)}; | |
| 1881 } | 1884 } |
| 1882 head.push(node); | 1885 head.push(node); |
| 1883 } | 1886 } |
| 1884 if (reverse) { | 1887 if (reverse) { |
| 1885 return {head: [], div: null, tail: head.reverse()}; | 1888 return {head: [], div: null, tail: head.reverse()}; |
| 1886 } | 1889 } |
| 1887 return {head: head, div: null, tail: []}; | 1890 return {head: head, div: null, tail: []}; |
| 1888 }; | 1891 }; |
| 1889 | 1892 |
| 1890 | 1893 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1924 * Constructs a predicate to check the semantic attribute of a node. | 1927 * Constructs a predicate to check the semantic attribute of a node. |
| 1925 * @param {!string} prop The property of a node. | 1928 * @param {!string} prop The property of a node. |
| 1926 * @param {!string} attr The attribute. | 1929 * @param {!string} attr The attribute. |
| 1927 * @return {function(cvox.SemanticTree.Node): boolean} The predicate. | 1930 * @return {function(cvox.SemanticTree.Node): boolean} The predicate. |
| 1928 * @private | 1931 * @private |
| 1929 */ | 1932 */ |
| 1930 | 1933 |
| 1931 cvox.SemanticTree.attrPred_ = function(prop, attr) { | 1934 cvox.SemanticTree.attrPred_ = function(prop, attr) { |
| 1932 var getAttr = function(prop) { | 1935 var getAttr = function(prop) { |
| 1933 switch (prop) { | 1936 switch (prop) { |
| 1934 case 'type': return cvox.SemanticAttr.Type[attr]; | 1937 case 'type': |
| 1935 case 'role': return cvox.SemanticAttr.Role[attr]; | 1938 return cvox.SemanticAttr.Type[attr]; |
| 1936 case 'font': return cvox.SemanticAttr.Font[attr]; | 1939 case 'role': |
| 1940 return cvox.SemanticAttr.Role[attr]; |
| 1941 case 'font': |
| 1942 return cvox.SemanticAttr.Font[attr]; |
| 1937 } | 1943 } |
| 1938 }; | 1944 }; |
| 1939 | 1945 |
| 1940 return function(node) {return node[prop] == getAttr(prop);}; | 1946 return function(node) { |
| 1947 return node[prop] == getAttr(prop); |
| 1948 }; |
| 1941 }; | 1949 }; |
| OLD | NEW |