semantic_util.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import * as DomUtil from '../common/dom_util.js';
  2. export var MMLTAGS;
  3. (function (MMLTAGS) {
  4. MMLTAGS["ANNOTATION"] = "ANNOTATION";
  5. MMLTAGS["ANNOTATIONXML"] = "ANNOTATION-XML";
  6. MMLTAGS["MACTION"] = "MACTION";
  7. MMLTAGS["MALIGNGROUP"] = "MALIGNGROUP";
  8. MMLTAGS["MALIGNMARK"] = "MALIGNMARK";
  9. MMLTAGS["MATH"] = "MATH";
  10. MMLTAGS["MENCLOSE"] = "MENCLOSE";
  11. MMLTAGS["MERROR"] = "MERROR";
  12. MMLTAGS["MFENCED"] = "MFENCED";
  13. MMLTAGS["MFRAC"] = "MFRAC";
  14. MMLTAGS["MGLYPH"] = "MGLYPH";
  15. MMLTAGS["MI"] = "MI";
  16. MMLTAGS["MLABELEDTR"] = "MLABELEDTR";
  17. MMLTAGS["MMULTISCRIPTS"] = "MMULTISCRIPTS";
  18. MMLTAGS["MN"] = "MN";
  19. MMLTAGS["MO"] = "MO";
  20. MMLTAGS["MOVER"] = "MOVER";
  21. MMLTAGS["MPADDED"] = "MPADDED";
  22. MMLTAGS["MPHANTOM"] = "MPHANTOM";
  23. MMLTAGS["MPRESCRIPTS"] = "MPRESCRIPTS";
  24. MMLTAGS["MROOT"] = "MROOT";
  25. MMLTAGS["MROW"] = "MROW";
  26. MMLTAGS["MS"] = "MS";
  27. MMLTAGS["MSPACE"] = "MSPACE";
  28. MMLTAGS["MSQRT"] = "MSQRT";
  29. MMLTAGS["MSTYLE"] = "MSTYLE";
  30. MMLTAGS["MSUB"] = "MSUB";
  31. MMLTAGS["MSUBSUP"] = "MSUBSUP";
  32. MMLTAGS["MSUP"] = "MSUP";
  33. MMLTAGS["MTABLE"] = "MTABLE";
  34. MMLTAGS["MTD"] = "MTD";
  35. MMLTAGS["MTEXT"] = "MTEXT";
  36. MMLTAGS["MTR"] = "MTR";
  37. MMLTAGS["MUNDER"] = "MUNDER";
  38. MMLTAGS["MUNDEROVER"] = "MUNDEROVER";
  39. MMLTAGS["NONE"] = "NONE";
  40. MMLTAGS["SEMANTICS"] = "SEMANTICS";
  41. })(MMLTAGS || (MMLTAGS = {}));
  42. const ALLTAGS = Object.values(MMLTAGS);
  43. const LEAFTAGS = [
  44. MMLTAGS.MO,
  45. MMLTAGS.MI,
  46. MMLTAGS.MN,
  47. MMLTAGS.MTEXT,
  48. MMLTAGS.MS,
  49. MMLTAGS.MSPACE
  50. ];
  51. const IGNORETAGS = [
  52. MMLTAGS.MERROR,
  53. MMLTAGS.MPHANTOM,
  54. MMLTAGS.MALIGNGROUP,
  55. MMLTAGS.MALIGNMARK,
  56. MMLTAGS.MPRESCRIPTS,
  57. MMLTAGS.ANNOTATION,
  58. MMLTAGS.ANNOTATIONXML
  59. ];
  60. const EMPTYTAGS = [
  61. MMLTAGS.MATH,
  62. MMLTAGS.MROW,
  63. MMLTAGS.MPADDED,
  64. MMLTAGS.MACTION,
  65. MMLTAGS.NONE,
  66. MMLTAGS.MSTYLE,
  67. MMLTAGS.SEMANTICS
  68. ];
  69. const DISPLAYTAGS = [MMLTAGS.MROOT, MMLTAGS.MSQRT];
  70. const directSpeechKeys = ['aria-label', 'exact-speech', 'alt'];
  71. export function hasMathTag(node) {
  72. return !!node && DomUtil.tagName(node) === MMLTAGS.MATH;
  73. }
  74. function hasLeafTag(node) {
  75. return !!node && LEAFTAGS.includes(DomUtil.tagName(node));
  76. }
  77. export function hasIgnoreTag(node) {
  78. return (!!node &&
  79. (IGNORETAGS.includes(DomUtil.tagName(node)) ||
  80. !ALLTAGS.includes(DomUtil.tagName(node))));
  81. }
  82. export function hasEmptyTag(node) {
  83. return !!node && EMPTYTAGS.includes(DomUtil.tagName(node));
  84. }
  85. export function hasDisplayTag(node) {
  86. return !!node && DISPLAYTAGS.includes(DomUtil.tagName(node));
  87. }
  88. export function isOrphanedGlyph(node) {
  89. return (!!node &&
  90. DomUtil.tagName(node) === MMLTAGS.MGLYPH &&
  91. !hasLeafTag(node.parentNode));
  92. }
  93. export function purgeNodes(nodes) {
  94. const nodeArray = [];
  95. for (let i = 0, node; (node = nodes[i]); i++) {
  96. if (node.nodeType !== DomUtil.NodeType.ELEMENT_NODE) {
  97. continue;
  98. }
  99. const tagName = DomUtil.tagName(node);
  100. if (IGNORETAGS.includes(tagName)) {
  101. continue;
  102. }
  103. if (EMPTYTAGS.includes(tagName) && node.childNodes.length === 0) {
  104. continue;
  105. }
  106. nodeArray.push(node);
  107. }
  108. return nodeArray;
  109. }
  110. export function isZeroLength(length) {
  111. if (!length) {
  112. return false;
  113. }
  114. const negativeNamedSpaces = [
  115. 'negativeveryverythinmathspace',
  116. 'negativeverythinmathspace',
  117. 'negativethinmathspace',
  118. 'negativemediummathspace',
  119. 'negativethickmathspace',
  120. 'negativeverythickmathspace',
  121. 'negativeveryverythickmathspace'
  122. ];
  123. if (negativeNamedSpaces.includes(length)) {
  124. return true;
  125. }
  126. const value = length.match(/[0-9.]+/);
  127. if (!value) {
  128. return false;
  129. }
  130. return parseFloat(value[0]) === 0;
  131. }
  132. export function addAttributes(to, from) {
  133. if (from.hasAttributes()) {
  134. const attrs = from.attributes;
  135. for (let i = attrs.length - 1; i >= 0; i--) {
  136. const key = attrs[i].name;
  137. if (key.match(/^ext/)) {
  138. to.attributes[key] = attrs[i].value;
  139. to.nobreaking = true;
  140. }
  141. if (directSpeechKeys.includes(key)) {
  142. to.attributes['ext-speech'] = attrs[i].value;
  143. to.nobreaking = true;
  144. }
  145. if (key.match(/texclass$/)) {
  146. to.attributes['texclass'] = attrs[i].value;
  147. }
  148. if (key.toLowerCase() === 'data-latex') {
  149. to.attributes['latex'] = attrs[i].value;
  150. }
  151. if (key === 'href') {
  152. to.attributes['href'] = attrs[i].value;
  153. to.nobreaking = true;
  154. }
  155. }
  156. }
  157. }
  158. export function getEmbellishedInner(node) {
  159. if (node && node.embellished && node.childNodes.length > 0) {
  160. return getEmbellishedInner(node.childNodes[0]);
  161. }
  162. return node;
  163. }
  164. export function sliceNodes(nodes, pred, opt_reverse) {
  165. if (opt_reverse) {
  166. nodes.reverse();
  167. }
  168. const head = [];
  169. for (let i = 0, node; (node = nodes[i]); i++) {
  170. if (pred(node)) {
  171. if (opt_reverse) {
  172. return {
  173. head: nodes.slice(i + 1).reverse(),
  174. div: node,
  175. tail: head.reverse()
  176. };
  177. }
  178. return { head: head, div: node, tail: nodes.slice(i + 1) };
  179. }
  180. head.push(node);
  181. }
  182. if (opt_reverse) {
  183. return { head: [], div: null, tail: head.reverse() };
  184. }
  185. return { head: head, div: null, tail: [] };
  186. }
  187. export function partitionNodes(nodes, pred) {
  188. let restNodes = nodes;
  189. const rel = [];
  190. const comp = [];
  191. let result = null;
  192. do {
  193. result = sliceNodes(restNodes, pred);
  194. comp.push(result.head);
  195. rel.push(result.div);
  196. restNodes = result.tail;
  197. } while (result.div);
  198. rel.pop();
  199. return { rel: rel, comp: comp };
  200. }