case_embellished.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import * as DomUtil from '../common/dom_util.js';
  2. import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
  3. import { SemanticNode } from '../semantic_tree/semantic_node.js';
  4. import { MMLTAGS } from '../semantic_tree/semantic_util.js';
  5. import { AbstractEnrichCase } from './abstract_enrich_case.js';
  6. import { CaseDoubleScript } from './case_double_script.js';
  7. import { CaseMultiscripts } from './case_multiscripts.js';
  8. import { CaseTensor } from './case_tensor.js';
  9. import * as EnrichMathml from './enrich_mathml.js';
  10. import { addMrow, setAttributes, Attribute } from './enrich_attr.js';
  11. export class CaseEmbellished extends AbstractEnrichCase {
  12. static test(semantic) {
  13. return !!(semantic.mathmlTree &&
  14. semantic.fencePointer &&
  15. !semantic.mathmlTree.getAttribute('data-semantic-type'));
  16. }
  17. static makeEmptyNode_(id) {
  18. const mrow = addMrow();
  19. const empty = new SemanticNode(id);
  20. empty.type = SemanticType.EMPTY;
  21. empty.mathmlTree = mrow;
  22. return empty;
  23. }
  24. static fencedMap_(fence, ids) {
  25. ids[fence.id] = fence.mathmlTree;
  26. if (!fence.embellished) {
  27. return;
  28. }
  29. CaseEmbellished.fencedMap_(fence.childNodes[0], ids);
  30. }
  31. constructor(semantic) {
  32. super(semantic);
  33. this.fenced = null;
  34. this.fencedMml = null;
  35. this.fencedMmlNodes = [];
  36. this.ofence = null;
  37. this.ofenceMml = null;
  38. this.ofenceMap = {};
  39. this.cfence = null;
  40. this.cfenceMml = null;
  41. this.cfenceMap = {};
  42. this.parentCleanup = [];
  43. }
  44. getMathml() {
  45. this.getFenced_();
  46. this.fencedMml = EnrichMathml.walkTree(this.fenced);
  47. this.getFencesMml_();
  48. if (this.fenced.type === SemanticType.EMPTY && !this.fencedMml.parentNode) {
  49. this.fencedMml.setAttribute(Attribute.ADDED, 'true');
  50. this.cfenceMml.parentNode.insertBefore(this.fencedMml, this.cfenceMml);
  51. }
  52. this.getFencedMml_();
  53. const rewrite = this.rewrite_();
  54. return rewrite;
  55. }
  56. fencedElement(node) {
  57. return (node.type === SemanticType.FENCED ||
  58. node.type === SemanticType.MATRIX ||
  59. node.type === SemanticType.VECTOR);
  60. }
  61. getFenced_() {
  62. let currentNode = this.semantic;
  63. while (!this.fencedElement(currentNode)) {
  64. currentNode = currentNode.childNodes[0];
  65. }
  66. this.fenced = currentNode.childNodes[0];
  67. this.ofence = currentNode.contentNodes[0];
  68. this.cfence = currentNode.contentNodes[1];
  69. CaseEmbellished.fencedMap_(this.ofence, this.ofenceMap);
  70. CaseEmbellished.fencedMap_(this.cfence, this.cfenceMap);
  71. }
  72. getFencedMml_() {
  73. let sibling = this.ofenceMml.nextSibling;
  74. sibling = sibling === this.fencedMml ? sibling : this.fencedMml;
  75. while (sibling && sibling !== this.cfenceMml) {
  76. this.fencedMmlNodes.push(sibling);
  77. sibling = sibling.nextSibling;
  78. }
  79. }
  80. getFencesMml_() {
  81. let currentNode = this.semantic;
  82. const ofenceIds = Object.keys(this.ofenceMap);
  83. const cfenceIds = Object.keys(this.cfenceMap);
  84. while ((!this.ofenceMml || !this.cfenceMml) &&
  85. currentNode !== this.fenced) {
  86. if (ofenceIds.indexOf(currentNode.fencePointer) !== -1 &&
  87. !this.ofenceMml) {
  88. this.ofenceMml = currentNode.mathmlTree;
  89. }
  90. if (cfenceIds.indexOf(currentNode.fencePointer) !== -1 &&
  91. !this.cfenceMml) {
  92. this.cfenceMml = currentNode.mathmlTree;
  93. }
  94. currentNode = currentNode.childNodes[0];
  95. }
  96. if (!this.ofenceMml) {
  97. this.ofenceMml = this.ofence.mathmlTree;
  98. }
  99. if (!this.cfenceMml) {
  100. this.cfenceMml = this.cfence.mathmlTree;
  101. }
  102. if (this.ofenceMml) {
  103. this.ofenceMml = EnrichMathml.ascendNewNode(this.ofenceMml);
  104. }
  105. if (this.cfenceMml) {
  106. this.cfenceMml = EnrichMathml.ascendNewNode(this.cfenceMml);
  107. }
  108. }
  109. rewrite_() {
  110. let currentNode = this.semantic;
  111. let result = null;
  112. const newNode = this.introduceNewLayer_();
  113. setAttributes(newNode, this.fenced.parent);
  114. while (!this.fencedElement(currentNode)) {
  115. const mml = currentNode.mathmlTree;
  116. const specialCase = this.specialCase_(currentNode, mml);
  117. if (specialCase) {
  118. currentNode = specialCase;
  119. }
  120. else {
  121. setAttributes(mml, currentNode);
  122. const mmlChildren = [];
  123. for (let i = 1, child; (child = currentNode.childNodes[i]); i++) {
  124. mmlChildren.push(EnrichMathml.walkTree(child));
  125. }
  126. currentNode = currentNode.childNodes[0];
  127. }
  128. const dummy = DomUtil.createElement('dummy');
  129. const saveChild = mml.childNodes[0];
  130. DomUtil.replaceNode(mml, dummy);
  131. DomUtil.replaceNode(newNode, mml);
  132. DomUtil.replaceNode(mml.childNodes[0], newNode);
  133. DomUtil.replaceNode(dummy, saveChild);
  134. if (!result) {
  135. result = mml;
  136. }
  137. }
  138. EnrichMathml.walkTree(this.ofence);
  139. EnrichMathml.walkTree(this.cfence);
  140. this.cleanupParents_();
  141. return result || newNode;
  142. }
  143. specialCase_(semantic, mml) {
  144. const mmlTag = DomUtil.tagName(mml);
  145. let parent = null;
  146. let caller;
  147. if (mmlTag === MMLTAGS.MSUBSUP) {
  148. parent = semantic.childNodes[0];
  149. caller = CaseDoubleScript;
  150. }
  151. else if (mmlTag === MMLTAGS.MMULTISCRIPTS) {
  152. if (semantic.type === SemanticType.SUPERSCRIPT ||
  153. semantic.type === SemanticType.SUBSCRIPT) {
  154. caller = CaseMultiscripts;
  155. }
  156. else if (semantic.type === SemanticType.TENSOR) {
  157. caller = CaseTensor;
  158. }
  159. if (caller &&
  160. semantic.childNodes[0] &&
  161. semantic.childNodes[0].role === SemanticRole.SUBSUP) {
  162. parent = semantic.childNodes[0];
  163. }
  164. else {
  165. parent = semantic;
  166. }
  167. }
  168. if (!parent) {
  169. return null;
  170. }
  171. const base = parent.childNodes[0];
  172. const empty = CaseEmbellished.makeEmptyNode_(base.id);
  173. parent.childNodes[0] = empty;
  174. mml = new caller(semantic).getMathml();
  175. parent.childNodes[0] = base;
  176. this.parentCleanup.push(mml);
  177. return parent.childNodes[0];
  178. }
  179. introduceNewLayer_() {
  180. const fullOfence = this.fullFence(this.ofenceMml);
  181. const fullCfence = this.fullFence(this.cfenceMml);
  182. let newNode = addMrow();
  183. DomUtil.replaceNode(this.fencedMml, newNode);
  184. this.fencedMmlNodes.forEach((node) => newNode.appendChild(node));
  185. newNode.insertBefore(fullOfence, this.fencedMml);
  186. newNode.appendChild(fullCfence);
  187. if (!newNode.parentNode) {
  188. const mrow = addMrow();
  189. while (newNode.childNodes.length > 0) {
  190. mrow.appendChild(newNode.childNodes[0]);
  191. }
  192. newNode.appendChild(mrow);
  193. newNode = mrow;
  194. }
  195. return newNode;
  196. }
  197. fullFence(fence) {
  198. const parent = this.fencedMml.parentNode;
  199. let currentFence = fence;
  200. while (currentFence.parentNode && currentFence.parentNode !== parent) {
  201. currentFence = currentFence.parentNode;
  202. }
  203. return currentFence;
  204. }
  205. cleanupParents_() {
  206. this.parentCleanup.forEach(function (x) {
  207. const parent = x.childNodes[1].getAttribute(Attribute.PARENT);
  208. x.childNodes[0].setAttribute(Attribute.PARENT, parent);
  209. });
  210. }
  211. }