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