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