ed-mathml2tex 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -582,6 +582,39 @@ T.createMarker = function() {
582
582
  }
583
583
  };
584
584
 
585
+ function getRender_default(template) {
586
+ return function(node, children) {
587
+ const parts = renderChildren(children);
588
+ return renderTemplate(template, parts);
589
+ };
590
+ }
591
+
592
+ function getRender_joinSeparator(template, separator = '') {
593
+ return function(node, children) {
594
+ const parts = renderChildren(children);
595
+ return template.replace('@content', parts.join(separator));
596
+ };
597
+ }
598
+
599
+ function getRender_joinSeparators(template, separators) {
600
+ return function(node, children) {
601
+ const parts = renderChildren(children);
602
+ let content = '';
603
+ if (separators.length === 0) {
604
+ content = parts.join('');
605
+ } else {
606
+ content = parts.reduce((accumulator, part, index) => {
607
+ accumulator += part;
608
+ if (index < parts.length - 1) {
609
+ accumulator += separators[index] || separators[separators.length - 1];
610
+ }
611
+ return accumulator;
612
+ }, '');
613
+ }
614
+ return template.replace('@content', content);
615
+ };
616
+ }
617
+
585
618
  function convert(mathmlHtml){
586
619
  const math = NodeTool.parseMath(mathmlHtml);
587
620
 
@@ -628,6 +661,18 @@ function toLatex(result) {
628
661
  result = result.replace(/→\\limits_\{([^}]*)\}\^\{([^}]*)\}/g, "\\overset{$2}{\\underset{$1}{\\rightarrow}}");
629
662
  result = result.replace(/\\rightarrow\\limits_\{([^}]*)\}\^\{([^}]*)\}/g, "\\overset{$2}{\\underset{$1}{\\rightarrow}}");
630
663
 
664
+ // Case 4: mover - fix expressions with arrow superscript
665
+ // Simple expression with arrow superscript: expr^{\rightarrow} → \overrightarrow{expr}
666
+ result = result.replace(/([^{}\s]+)\^\{\\rightarrow\}/g, "\\overrightarrow{$1}");
667
+ result = result.replace(/\{([^{}]+)\}\^\{\\rightarrow\}/g, "\\overrightarrow{$1}");
668
+
669
+ // Complex expressions with subscripts and arrow: expr_{sub}^{\rightarrow} → \overrightarrow{expr_{sub}}
670
+ result = result.replace(/([A-Za-z0-9]+)_\{([^{}]+)\}\^\{\\rightarrow\}/g, "\\overrightarrow{$1_{$2}}");
671
+ result = result.replace(/([A-Za-z0-9]+)_([0-9])\^\{\\rightarrow\}/g, "\\overrightarrow{$1_$2}");
672
+
673
+ // Very complex expressions: (expr)^{\rightarrow} → \overrightarrow{(expr)}
674
+ result = result.replace(/(\([^()]+\))\^\{\\rightarrow\}/g, "\\overrightarrow{$1}");
675
+
631
676
  // Also match if there are spaces
632
677
  result = result.replace(/→\s*\\limits\s*_\s*{\s*}/g, "\\underset{}{\\rightarrow}");
633
678
  result = result.replace(/\\rightarrow\s*\\limits\s*_\s*{\s*}/g, "\\underset{}{\\rightarrow}");
@@ -696,14 +741,27 @@ function parseOperator(node) {
696
741
  }
697
742
 
698
743
  // Math identifier
699
- function parseElementMi(node){
744
+ function parseElementMi(node) {
700
745
  let it = NodeTool.getNodeText(node).trim();
746
+
747
+ // Handle vectors (e.g. AB', AI)
748
+ if (it.includes("'")) {
749
+ return it; // Return as is to handle in mrow
750
+ }
751
+
752
+ // Handle subscripts (e.g. n₂)
753
+ if (it.match(/[a-zA-Z]\d/)) {
754
+ const base = it[0];
755
+ const sub = it[1];
756
+ return `${base}_{${sub}}`;
757
+ }
758
+
701
759
  it = MathSymbol.parseIdentifier(it);
702
760
  return escapeSpecialChars(it);
703
761
  }
704
762
 
705
763
  // Math Number
706
- function parseElementMn(node){
764
+ function parseElementMn(node) {
707
765
  let it = NodeTool.getNodeText(node).trim();
708
766
  return escapeSpecialChars(it);
709
767
  }
@@ -903,12 +961,29 @@ function renderMfrac(node, children){
903
961
  return render(node, children);
904
962
  }
905
963
 
906
- function renderMfenced(node, children){
964
+ function renderMfenced(node, children) {
907
965
  const [open, close, separatorsStr] = [
908
966
  NodeTool.getAttr(node, 'open', '('),
909
967
  NodeTool.getAttr(node, 'close', ')'),
910
968
  NodeTool.getAttr(node, 'separators', ',')
911
969
  ];
970
+
971
+ // Handle special case for vectors inside brackets
972
+ if (open === '[' && close === ']') {
973
+ const parts = renderChildren(children);
974
+ // Join parts with comma and space, preserving vector notation
975
+ const content = parts.join(', ');
976
+ return `\\left[${content}\\right]`;
977
+ }
978
+
979
+ // Handle special case for coordinates
980
+ if (open === '(' && close === ')') {
981
+ const parts = renderChildren(children);
982
+ // Join parts with semicolon
983
+ const content = parts.join(';');
984
+ return `\\left(${content}\\right)`;
985
+ }
986
+
912
987
  const [left, right] = [
913
988
  Brackets.parseLeft(open),
914
989
  Brackets.parseRight(close)
@@ -968,19 +1043,49 @@ function renderMmultiscripts(node, children) {
968
1043
  return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
969
1044
  }
970
1045
 
971
- function renderMover(node, children){
1046
+ function renderMover(node, children) {
972
1047
  const nodes = flattenNodeTreeByNodeName(node, 'mover');
973
1048
  let result = undefined;
1049
+
1050
+ // Get the base node and check if it's a subscript or mrow
1051
+ const baseNode = children[0];
1052
+ const nodeName = NodeTool.getNodeName(baseNode);
1053
+ const isSubscript = nodeName === 'msub';
1054
+ const isMrow = nodeName === 'mrow';
1055
+
1056
+ if (isSubscript) {
1057
+ // Handle case like n₂ with arrow
1058
+ const base = parse(baseNode);
1059
+ return `\\overrightarrow{${base}}`;
1060
+ }
1061
+
1062
+ if (isMrow) {
1063
+ // Handle case like AB or AI
1064
+ const base = parse(baseNode);
1065
+ const overNode = children[1];
1066
+ const overText = NodeTool.getNodeText(overNode).trim();
1067
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1068
+
1069
+ if (overText === "→" && isAccent) {
1070
+ return `\\overrightarrow{${base}}`;
1071
+ }
1072
+ }
1073
+
974
1074
  for(let i = 0; i < nodes.length - 1; i++) {
975
- if(!result){ result = parse(nodes[i]); }
976
- const over = parse(nodes[i + 1]);
977
- const template = getMatchValueByChar({
978
- decimals: MathSymbol.overScript.decimals,
979
- values: MathSymbol.overScript.templates,
980
- judgeChar: over,
981
- defaultValue: "@1^{@2}"
982
- });
983
- result = renderTemplate(template.replace("@v", "@1"), [result, over]);
1075
+ if(!result) {
1076
+ result = parse(nodes[i]);
1077
+ }
1078
+
1079
+ const overNode = nodes[i + 1];
1080
+ const overText = NodeTool.getNodeText(overNode).trim();
1081
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1082
+
1083
+ if (overText === "" && isAccent) {
1084
+ return `\\overrightarrow{${result}}`;
1085
+ }
1086
+
1087
+ const over = parse(overNode);
1088
+ result = `${result}^{${over}}`;
984
1089
  }
985
1090
  return result;
986
1091
  }
@@ -988,162 +1093,119 @@ function renderMover(node, children){
988
1093
  function renderMunder(node, children){
989
1094
  const nodes = flattenNodeTreeByNodeName(node, 'munder');
990
1095
  let result = undefined;
991
-
992
- // Early processing for arrow case
993
- const baseNode = children[0];
994
- if (baseNode && NodeTool.getNodeName(baseNode) === "mo") {
995
- const baseText = NodeTool.getNodeText(baseNode).trim();
996
- if (baseText === "→") {
997
- // This is an arrow with under script
998
- const underNode = children[1];
999
- if (!underNode || NodeTool.getNodeName(underNode) === "mrow" && NodeTool.getNodeText(underNode).trim() === "") {
1000
- // Empty under script or mrow
1001
- console.log("Arrow with empty underscript, using \\underset{}");
1002
- return "\\underset{}{\\rightarrow}";
1003
- } else {
1004
- // Non-empty under script
1005
- const under = parse(underNode);
1006
- console.log("Arrow with underscript:", under);
1007
- return `\\underset{${under}}{\\rightarrow}`;
1008
- }
1009
- }
1010
- }
1011
-
1012
1096
  for(let i = 0; i < nodes.length - 1; i++) {
1013
1097
  if(!result){ result = parse(nodes[i]); }
1014
1098
 
1015
1099
  const underNode = nodes[i + 1];
1016
1100
  const underText = NodeTool.getNodeText(underNode).trim();
1017
- const baseText = NodeTool.getNodeText(nodes[i]).trim();
1101
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1018
1102
 
1019
- // Skip processing for already processed cases
1020
- if (baseText === "→") {
1021
- continue;
1103
+ // Special handling for arrow accent
1104
+ if (underText === "→" && isAccent) {
1105
+ return `\\underset{${result}}{\\rightarrow}`;
1022
1106
  }
1023
1107
 
1024
- // Always use underset for operators to avoid \\limits errors
1025
- if (NodeTool.getNodeName(nodes[i]) === "mo") {
1026
- const under = parse(underNode);
1027
- result = `\\underset{${under}}{${result}}`;
1028
- } else {
1029
- const template = getMatchValueByChar({
1030
- decimals: MathSymbol.underScript.decimals,
1031
- values: MathSymbol.underScript.templates,
1032
- judgeChar: underText,
1033
- defaultValue: "@1_{@2}" // Use simple subscript instead of \limits
1034
- });
1035
-
1036
- const under = parse(underNode);
1037
- result = renderTemplate(template.replace("@v", "@1"), [result, under]);
1038
- }
1108
+ const under = parse(underNode);
1109
+ const template = getMatchValueByChar({
1110
+ decimals: MathSymbol.underScript.decimals,
1111
+ values: MathSymbol.underScript.templates,
1112
+ judgeChar: underText,
1113
+ defaultValue: "@1_{@2}"
1114
+ });
1115
+ result = renderTemplate(template.replace("@v", "@1"), [result, under]);
1039
1116
  }
1040
1117
  return result;
1041
1118
  }
1042
1119
 
1043
- function renderMunderover(node, children) {
1120
+ function renderMunderover(node, children){
1044
1121
  const nodes = flattenNodeTreeByNodeName(node, 'munderover');
1045
1122
  let result = undefined;
1046
-
1047
- if(nodes.length === 3) {
1048
- const baseNode = nodes[0];
1049
- const baseText = NodeTool.getNodeText(baseNode).trim();
1123
+ for(let i = 0; i < nodes.length - 1; i++) {
1124
+ if(!result){ result = parse(nodes[i]); }
1050
1125
 
1051
- // Special handling for arrow
1052
- if (baseText === "→") {
1053
- const under = parse(nodes[1]);
1054
- const over = parse(nodes[2]);
1055
- return `\\overset{${over}}{\\underset{${under}}{\\rightarrow}}`;
1056
- } else {
1057
- const base = parse(baseNode);
1058
- const under = parse(nodes[1]);
1059
- const over = parse(nodes[2]);
1060
- return `${base}\\limits_{${under}}^{${over}}`;
1126
+ const overNode = nodes[i + 1];
1127
+ const overText = NodeTool.getNodeText(overNode).trim();
1128
+ const underNode = nodes[i + 2];
1129
+ const underText = NodeTool.getNodeText(underNode).trim();
1130
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1131
+
1132
+ // Special handling for arrow accent
1133
+ if (overText === "→" && isAccent) {
1134
+ return `\\overset{${result}}{\\underset{${underText}}{\\rightarrow}}`;
1061
1135
  }
1136
+
1137
+ const over = parse(overNode);
1138
+ const under = parse(underNode);
1139
+ const template = getMatchValueByChar({
1140
+ decimals: MathSymbol.underoverScript.decimals,
1141
+ values: MathSymbol.underoverScript.templates,
1142
+ judgeChar: overText,
1143
+ defaultValue: "@1_{@2}^{@3}"
1144
+ });
1145
+ result = renderTemplate(template.replace("@v", "@1"), [over, under]);
1062
1146
  }
1063
-
1064
- for(let i = 0; i < nodes.length - 2; i++) {
1065
- if(!result){ result = parse(nodes[i]); }
1066
- const under = parse(nodes[i + 1]);
1067
- const over = parse(nodes[i + 2]);
1068
- result = renderTemplate("@1\\limits_{@2}^{@3}", [result, under, over]);
1069
- }
1070
- return result;
1071
- }
1072
-
1073
- function flattenNodeTreeByNodeName(root, nodeName) {
1074
- let result = [];
1075
- const children = NodeTool.getChildren(root);
1076
- Array.prototype.forEach.call(children, (node) => {
1077
- if (NodeTool.getNodeName(node) === nodeName) {
1078
- result = result.concat(flattenNodeTreeByNodeName(node, nodeName));
1079
- } else {
1080
- result.push(node);
1081
- }
1082
- });
1083
1147
  return result;
1084
1148
  }
1085
1149
 
1086
-
1087
- function getMatchValueByChar(params) {
1088
- const {decimals, values, judgeChar, defaultValue=null} = params;
1089
- if (judgeChar && judgeChar.length === 1) {
1090
- const index = decimals.indexOf(judgeChar.charCodeAt(0));
1091
- if (index > -1) {
1092
- return values[index];
1150
+ function renderMphantom(node, children){
1151
+ const nodes = flattenNodeTreeByNodeName(node, 'mphantom');
1152
+ let result = undefined;
1153
+ for(let i = 0; i < nodes.length - 1; i++) {
1154
+ if(!result){ result = parse(nodes[i]); }
1155
+
1156
+ const phantomNode = nodes[i + 1];
1157
+ const phantomText = NodeTool.getNodeText(phantomNode).trim();
1158
+ const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
1159
+
1160
+ // Special handling for arrow accent
1161
+ if (phantomText === "→" && isAccent) {
1162
+ return `\\overrightarrow{${result}}`;
1093
1163
  }
1164
+
1165
+ const phantom = parse(phantomNode);
1166
+ const template = getMatchValueByChar({
1167
+ decimals: MathSymbol.phantomScript.decimals,
1168
+ values: MathSymbol.phantomScript.templates,
1169
+ judgeChar: phantomText,
1170
+ defaultValue: "@1^{@2}"
1171
+ });
1172
+ result = renderTemplate(template.replace("@v", "@1"), [result, phantom]);
1094
1173
  }
1095
- return defaultValue;
1096
- }
1097
-
1098
- // https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mphantom
1099
- // FIXME :)
1100
- function renderMphantom(node, children) {
1101
- return '';
1102
- }
1103
-
1104
-
1105
-
1106
- function getRender_default(template) {
1107
- return function(node, children) {
1108
- const parts = renderChildren(children);
1109
- return renderTemplate(template, parts)
1110
- }
1174
+ return result;
1111
1175
  }
1112
1176
 
1113
- function renderTemplate(template, values) {
1114
- return template.replace(/\@\d+/g, (m) => {
1115
- const idx = parseInt(m.substring(1, m.length)) - 1;
1116
- return values[idx];
1177
+ function renderTemplate(template, args) {
1178
+ return template.replace(/@(\d+)/g, (match, index) => {
1179
+ const arg = args[index - 1];
1180
+ return arg || match;
1117
1181
  });
1118
1182
  }
1119
1183
 
1120
- function getRender_joinSeparator(template, separator = '') {
1121
- return function(node, children) {
1122
- const parts = renderChildren(children);
1123
- return template.replace("@content", parts.join(separator));
1124
- }
1184
+ function getMatchValueByChar(options) {
1185
+ const { decimals, values, judgeChar, defaultValue } = options;
1186
+ const match = values.find(value => value.judgeChar === judgeChar);
1187
+ return match || defaultValue;
1125
1188
  }
1126
1189
 
1127
- function getRender_joinSeparators(template, separators) {
1128
- return function(node, children) {
1129
- const parts = renderChildren(children);
1130
- let content = '';
1131
- if(separators.length === 0){
1132
- content = parts.join('');
1133
- } else {
1134
- content = parts.reduce((accumulator, part, index) => {
1135
- accumulator += part;
1136
- if(index < parts.length - 1){
1137
- accumulator += (separators[index] || separators[separators.length - 1]);
1138
- }
1139
- return accumulator;
1140
- }, '');
1141
- }
1142
- return template.replace("@content", content);
1190
+ function flattenNodeTreeByNodeName(node, nodeName) {
1191
+ const nodes = [];
1192
+ const children = NodeTool.getChildren(node);
1193
+ if (children && children.length > 0) {
1194
+ // Convert HTMLCollection to Array before using forEach
1195
+ Array.from(children).forEach(child => {
1196
+ if (NodeTool.getNodeName(child) === nodeName) {
1197
+ nodes.push(child);
1198
+ } else {
1199
+ // Recursively search in child nodes
1200
+ const childNodes = flattenNodeTreeByNodeName(child, nodeName);
1201
+ nodes.push(...childNodes);
1202
+ }
1203
+ });
1143
1204
  }
1205
+ return nodes;
1144
1206
  }
1145
1207
 
1146
- // Add exports at the end of file
1208
+ // Export the convert function
1147
1209
  var mathml2latex = {
1148
1210
  convert: convert
1149
1211
  };