ed-mathml2tex 0.1.7 → 0.1.9
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.
- package/lib/mathml2latex.browser.cjs.js +90 -21
- package/lib/mathml2latex.browser.es.js +90 -21
- package/lib/mathml2latex.browser.umd.js +90 -21
- package/lib/mathml2latex.cjs.js +90 -21
- package/lib/mathml2latex.es.js +90 -21
- package/lib/mathml2latex.umd.js +90 -21
- package/package.json +1 -1
|
@@ -813,11 +813,11 @@ function parseElementMs(node) {
|
|
|
813
813
|
// Math Text
|
|
814
814
|
function parseElementMtext(node) {
|
|
815
815
|
let content = NodeTool.getNodeText(node)
|
|
816
|
-
// Handle operators and spacing only
|
|
817
816
|
.replace(/\s*=\s*/g, " = ")
|
|
818
817
|
.replace(/\s*\.\s*/g, " \\cdot ")
|
|
819
818
|
.trim();
|
|
820
819
|
|
|
820
|
+
// Map đặc biệt
|
|
821
821
|
const specialMTextMap = {
|
|
822
822
|
ℝ: "\\mathbb{R}",
|
|
823
823
|
ℤ: "\\mathbb{Z}",
|
|
@@ -830,14 +830,21 @@ function parseElementMtext(node) {
|
|
|
830
830
|
};
|
|
831
831
|
if (specialMTextMap[content]) return specialMTextMap[content];
|
|
832
832
|
|
|
833
|
-
//
|
|
833
|
+
// Nếu đã là lệnh LaTeX thì giữ nguyên
|
|
834
|
+
if (content.startsWith("\\")) return content;
|
|
835
|
+
|
|
836
|
+
// Bọc token chữ/số liền nhau bằng \text{...} (hóa học: Cu, OH, NaOH, ...)
|
|
837
|
+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(content)) {
|
|
838
|
+
return `\\text{${content}}`;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// (Giữ nguyên xử lý units nếu mtext chứa dấu ngoặc tròn)
|
|
834
842
|
if (content.includes("(") && content.includes(")")) {
|
|
835
843
|
const parts = content.split(/(\([^)]+\))/);
|
|
836
844
|
content = parts
|
|
837
845
|
.map((part) => {
|
|
838
846
|
if (part.startsWith("(") && part.endsWith(")")) {
|
|
839
|
-
|
|
840
|
-
return `\\mathrm{${part}}`;
|
|
847
|
+
return `\\text{${part}}`;
|
|
841
848
|
}
|
|
842
849
|
return part;
|
|
843
850
|
})
|
|
@@ -889,9 +896,8 @@ function parseContainer(node, children) {
|
|
|
889
896
|
|
|
890
897
|
function renderChildren(children) {
|
|
891
898
|
const parts = [];
|
|
892
|
-
let lefts = [];
|
|
899
|
+
let lefts = []; // PATCH: Special case for set-builder style: leading { ... trailing }
|
|
893
900
|
|
|
894
|
-
// PATCH: Special case for set-builder style: leading { ... trailing }
|
|
895
901
|
if (
|
|
896
902
|
children.length >= 3 &&
|
|
897
903
|
NodeTool.getNodeName(children[0]) === "mo" &&
|
|
@@ -903,8 +909,9 @@ function renderChildren(children) {
|
|
|
903
909
|
const innerContent = Array.prototype.slice
|
|
904
910
|
.call(children, 1, -1)
|
|
905
911
|
.map((child) => parse(child))
|
|
906
|
-
.join("");
|
|
907
|
-
|
|
912
|
+
.join(""); // Chỉ trả về nếu nội dung không rỗng
|
|
913
|
+
const result = `\\left\\{${innerContent}\\right\\}`;
|
|
914
|
+
return result;
|
|
908
915
|
}
|
|
909
916
|
|
|
910
917
|
Array.prototype.forEach.call(children, (node, idx) => {
|
|
@@ -922,18 +929,19 @@ function renderChildren(children) {
|
|
|
922
929
|
) {
|
|
923
930
|
// Only vars, nums, ops (possibly Greek); join with thin space
|
|
924
931
|
const str = mrowKids.map((k) => parse(k)).join("\\,");
|
|
925
|
-
|
|
932
|
+
if (str) {
|
|
933
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
934
|
+
parts.push(str);
|
|
935
|
+
}
|
|
926
936
|
return;
|
|
927
937
|
}
|
|
928
|
-
}
|
|
929
|
-
// END PATCH
|
|
938
|
+
} // END PATCH
|
|
930
939
|
if (NodeTool.getNodeName(node) === "mo") {
|
|
931
940
|
const op = NodeTool.getNodeText(node).trim();
|
|
932
941
|
if (Brackets.contains(op)) {
|
|
933
942
|
let stretchy = NodeTool.getAttr(node, "stretchy", "true");
|
|
934
943
|
stretchy = ["", "true"].indexOf(stretchy) > -1;
|
|
935
944
|
|
|
936
|
-
// Luôn escape dấu ngoặc nhọn cho LaTeX
|
|
937
945
|
let escapedOp = op;
|
|
938
946
|
if (op === "{" || op === "}") {
|
|
939
947
|
escapedOp = `\\${op}`;
|
|
@@ -941,19 +949,27 @@ function renderChildren(children) {
|
|
|
941
949
|
|
|
942
950
|
if (Brackets.isRight(op)) {
|
|
943
951
|
const nearLeft = lefts[lefts.length - 1];
|
|
944
|
-
if (nearLeft
|
|
952
|
+
if (nearLeft) {
|
|
945
953
|
const parentNode = node.parentNode;
|
|
946
954
|
const isInPower =
|
|
947
955
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
948
956
|
|
|
957
|
+
let partToPush = "";
|
|
949
958
|
if (stretchy && !isInPower) {
|
|
950
|
-
|
|
959
|
+
partToPush = `\\right${escapedOp}`;
|
|
951
960
|
} else {
|
|
952
|
-
|
|
961
|
+
partToPush = escapedOp;
|
|
962
|
+
}
|
|
963
|
+
if (partToPush) {
|
|
964
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
965
|
+
parts.push(partToPush);
|
|
953
966
|
}
|
|
954
967
|
lefts.pop();
|
|
955
968
|
} else {
|
|
956
|
-
|
|
969
|
+
if (escapedOp) {
|
|
970
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
971
|
+
parts.push(escapedOp);
|
|
972
|
+
}
|
|
957
973
|
}
|
|
958
974
|
} else {
|
|
959
975
|
// Là ngoặc trái
|
|
@@ -961,18 +977,71 @@ function renderChildren(children) {
|
|
|
961
977
|
const isInPower =
|
|
962
978
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
963
979
|
|
|
980
|
+
let partToPush = "";
|
|
964
981
|
if (stretchy && !isInPower) {
|
|
965
|
-
|
|
982
|
+
partToPush = `\\left${escapedOp}`;
|
|
966
983
|
} else {
|
|
967
|
-
|
|
984
|
+
partToPush = escapedOp;
|
|
985
|
+
}
|
|
986
|
+
if (partToPush) {
|
|
987
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
988
|
+
parts.push(partToPush);
|
|
989
|
+
}
|
|
990
|
+
lefts.push(op);
|
|
991
|
+
}
|
|
992
|
+
} else {
|
|
993
|
+
const parsedOperator = parseOperator(node);
|
|
994
|
+
if (parsedOperator) {
|
|
995
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
996
|
+
parts.push(parsedOperator);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// --- START PATCH V6 (Giữ nguyên logic V5) ---
|
|
1001
|
+
} else if (NodeTool.getNodeName(node) === "msub") {
|
|
1002
|
+
const subChildren = Array.from(NodeTool.getChildren(node));
|
|
1003
|
+
if (
|
|
1004
|
+
subChildren.length === 2 &&
|
|
1005
|
+
NodeTool.getNodeName(subChildren[0]) === "mo" &&
|
|
1006
|
+
NodeTool.getNodeText(subChildren[0]).trim() === ")"
|
|
1007
|
+
) {
|
|
1008
|
+
// ĐÚNG LÀ NGOẠI LỆ
|
|
1009
|
+
|
|
1010
|
+
const sub = parse(subChildren[1]);
|
|
1011
|
+
// Mảng 'parts' lúc này "sạch" và là: ["\text{Cu}", "\left(", "\text{OH}"]
|
|
1012
|
+
const lastPart = parts.pop(); // lastPart = "\text{OH}"
|
|
1013
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\left("]
|
|
1014
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1015
|
+
if (parts[i] && parts[i].trim() === "\\left(") {
|
|
1016
|
+
parts.splice(i, 1); // Xóa "\left("
|
|
1017
|
+
break;
|
|
968
1018
|
}
|
|
969
|
-
|
|
1019
|
+
_;
|
|
1020
|
+
} // Mảng 'parts' lúc này là: ["\text{Cu}"]
|
|
1021
|
+
|
|
1022
|
+
parts.push(`${lastPart}_{${sub}}`); // Push "\text{OH}_{2}"
|
|
1023
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\text{OH}_{2}"]
|
|
1024
|
+
|
|
1025
|
+
const nearLeft = lefts[lefts.length - 1];
|
|
1026
|
+
if (nearLeft) {
|
|
1027
|
+
lefts.pop();
|
|
970
1028
|
}
|
|
971
1029
|
} else {
|
|
972
|
-
|
|
1030
|
+
// <msub> bình thường
|
|
1031
|
+
const parsed = parse(node);
|
|
1032
|
+
if (parsed) {
|
|
1033
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1034
|
+
parts.push(parsed);
|
|
1035
|
+
}
|
|
973
1036
|
}
|
|
1037
|
+
// --- END PATCH V6 ---
|
|
974
1038
|
} else {
|
|
975
|
-
|
|
1039
|
+
// Các node khác như <mtext>, #text, v.v.
|
|
1040
|
+
const parsed = parse(node);
|
|
1041
|
+
if (parsed) {
|
|
1042
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1043
|
+
parts.push(parsed);
|
|
1044
|
+
}
|
|
976
1045
|
}
|
|
977
1046
|
});
|
|
978
1047
|
lefts = undefined;
|
|
@@ -811,11 +811,11 @@ function parseElementMs(node) {
|
|
|
811
811
|
// Math Text
|
|
812
812
|
function parseElementMtext(node) {
|
|
813
813
|
let content = NodeTool.getNodeText(node)
|
|
814
|
-
// Handle operators and spacing only
|
|
815
814
|
.replace(/\s*=\s*/g, " = ")
|
|
816
815
|
.replace(/\s*\.\s*/g, " \\cdot ")
|
|
817
816
|
.trim();
|
|
818
817
|
|
|
818
|
+
// Map đặc biệt
|
|
819
819
|
const specialMTextMap = {
|
|
820
820
|
ℝ: "\\mathbb{R}",
|
|
821
821
|
ℤ: "\\mathbb{Z}",
|
|
@@ -828,14 +828,21 @@ function parseElementMtext(node) {
|
|
|
828
828
|
};
|
|
829
829
|
if (specialMTextMap[content]) return specialMTextMap[content];
|
|
830
830
|
|
|
831
|
-
//
|
|
831
|
+
// Nếu đã là lệnh LaTeX thì giữ nguyên
|
|
832
|
+
if (content.startsWith("\\")) return content;
|
|
833
|
+
|
|
834
|
+
// Bọc token chữ/số liền nhau bằng \text{...} (hóa học: Cu, OH, NaOH, ...)
|
|
835
|
+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(content)) {
|
|
836
|
+
return `\\text{${content}}`;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// (Giữ nguyên xử lý units nếu mtext chứa dấu ngoặc tròn)
|
|
832
840
|
if (content.includes("(") && content.includes(")")) {
|
|
833
841
|
const parts = content.split(/(\([^)]+\))/);
|
|
834
842
|
content = parts
|
|
835
843
|
.map((part) => {
|
|
836
844
|
if (part.startsWith("(") && part.endsWith(")")) {
|
|
837
|
-
|
|
838
|
-
return `\\mathrm{${part}}`;
|
|
845
|
+
return `\\text{${part}}`;
|
|
839
846
|
}
|
|
840
847
|
return part;
|
|
841
848
|
})
|
|
@@ -887,9 +894,8 @@ function parseContainer(node, children) {
|
|
|
887
894
|
|
|
888
895
|
function renderChildren(children) {
|
|
889
896
|
const parts = [];
|
|
890
|
-
let lefts = [];
|
|
897
|
+
let lefts = []; // PATCH: Special case for set-builder style: leading { ... trailing }
|
|
891
898
|
|
|
892
|
-
// PATCH: Special case for set-builder style: leading { ... trailing }
|
|
893
899
|
if (
|
|
894
900
|
children.length >= 3 &&
|
|
895
901
|
NodeTool.getNodeName(children[0]) === "mo" &&
|
|
@@ -901,8 +907,9 @@ function renderChildren(children) {
|
|
|
901
907
|
const innerContent = Array.prototype.slice
|
|
902
908
|
.call(children, 1, -1)
|
|
903
909
|
.map((child) => parse(child))
|
|
904
|
-
.join("");
|
|
905
|
-
|
|
910
|
+
.join(""); // Chỉ trả về nếu nội dung không rỗng
|
|
911
|
+
const result = `\\left\\{${innerContent}\\right\\}`;
|
|
912
|
+
return result;
|
|
906
913
|
}
|
|
907
914
|
|
|
908
915
|
Array.prototype.forEach.call(children, (node, idx) => {
|
|
@@ -920,18 +927,19 @@ function renderChildren(children) {
|
|
|
920
927
|
) {
|
|
921
928
|
// Only vars, nums, ops (possibly Greek); join with thin space
|
|
922
929
|
const str = mrowKids.map((k) => parse(k)).join("\\,");
|
|
923
|
-
|
|
930
|
+
if (str) {
|
|
931
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
932
|
+
parts.push(str);
|
|
933
|
+
}
|
|
924
934
|
return;
|
|
925
935
|
}
|
|
926
|
-
}
|
|
927
|
-
// END PATCH
|
|
936
|
+
} // END PATCH
|
|
928
937
|
if (NodeTool.getNodeName(node) === "mo") {
|
|
929
938
|
const op = NodeTool.getNodeText(node).trim();
|
|
930
939
|
if (Brackets.contains(op)) {
|
|
931
940
|
let stretchy = NodeTool.getAttr(node, "stretchy", "true");
|
|
932
941
|
stretchy = ["", "true"].indexOf(stretchy) > -1;
|
|
933
942
|
|
|
934
|
-
// Luôn escape dấu ngoặc nhọn cho LaTeX
|
|
935
943
|
let escapedOp = op;
|
|
936
944
|
if (op === "{" || op === "}") {
|
|
937
945
|
escapedOp = `\\${op}`;
|
|
@@ -939,19 +947,27 @@ function renderChildren(children) {
|
|
|
939
947
|
|
|
940
948
|
if (Brackets.isRight(op)) {
|
|
941
949
|
const nearLeft = lefts[lefts.length - 1];
|
|
942
|
-
if (nearLeft
|
|
950
|
+
if (nearLeft) {
|
|
943
951
|
const parentNode = node.parentNode;
|
|
944
952
|
const isInPower =
|
|
945
953
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
946
954
|
|
|
955
|
+
let partToPush = "";
|
|
947
956
|
if (stretchy && !isInPower) {
|
|
948
|
-
|
|
957
|
+
partToPush = `\\right${escapedOp}`;
|
|
949
958
|
} else {
|
|
950
|
-
|
|
959
|
+
partToPush = escapedOp;
|
|
960
|
+
}
|
|
961
|
+
if (partToPush) {
|
|
962
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
963
|
+
parts.push(partToPush);
|
|
951
964
|
}
|
|
952
965
|
lefts.pop();
|
|
953
966
|
} else {
|
|
954
|
-
|
|
967
|
+
if (escapedOp) {
|
|
968
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
969
|
+
parts.push(escapedOp);
|
|
970
|
+
}
|
|
955
971
|
}
|
|
956
972
|
} else {
|
|
957
973
|
// Là ngoặc trái
|
|
@@ -959,18 +975,71 @@ function renderChildren(children) {
|
|
|
959
975
|
const isInPower =
|
|
960
976
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
961
977
|
|
|
978
|
+
let partToPush = "";
|
|
962
979
|
if (stretchy && !isInPower) {
|
|
963
|
-
|
|
980
|
+
partToPush = `\\left${escapedOp}`;
|
|
964
981
|
} else {
|
|
965
|
-
|
|
982
|
+
partToPush = escapedOp;
|
|
983
|
+
}
|
|
984
|
+
if (partToPush) {
|
|
985
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
986
|
+
parts.push(partToPush);
|
|
987
|
+
}
|
|
988
|
+
lefts.push(op);
|
|
989
|
+
}
|
|
990
|
+
} else {
|
|
991
|
+
const parsedOperator = parseOperator(node);
|
|
992
|
+
if (parsedOperator) {
|
|
993
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
994
|
+
parts.push(parsedOperator);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// --- START PATCH V6 (Giữ nguyên logic V5) ---
|
|
999
|
+
} else if (NodeTool.getNodeName(node) === "msub") {
|
|
1000
|
+
const subChildren = Array.from(NodeTool.getChildren(node));
|
|
1001
|
+
if (
|
|
1002
|
+
subChildren.length === 2 &&
|
|
1003
|
+
NodeTool.getNodeName(subChildren[0]) === "mo" &&
|
|
1004
|
+
NodeTool.getNodeText(subChildren[0]).trim() === ")"
|
|
1005
|
+
) {
|
|
1006
|
+
// ĐÚNG LÀ NGOẠI LỆ
|
|
1007
|
+
|
|
1008
|
+
const sub = parse(subChildren[1]);
|
|
1009
|
+
// Mảng 'parts' lúc này "sạch" và là: ["\text{Cu}", "\left(", "\text{OH}"]
|
|
1010
|
+
const lastPart = parts.pop(); // lastPart = "\text{OH}"
|
|
1011
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\left("]
|
|
1012
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1013
|
+
if (parts[i] && parts[i].trim() === "\\left(") {
|
|
1014
|
+
parts.splice(i, 1); // Xóa "\left("
|
|
1015
|
+
break;
|
|
966
1016
|
}
|
|
967
|
-
|
|
1017
|
+
_;
|
|
1018
|
+
} // Mảng 'parts' lúc này là: ["\text{Cu}"]
|
|
1019
|
+
|
|
1020
|
+
parts.push(`${lastPart}_{${sub}}`); // Push "\text{OH}_{2}"
|
|
1021
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\text{OH}_{2}"]
|
|
1022
|
+
|
|
1023
|
+
const nearLeft = lefts[lefts.length - 1];
|
|
1024
|
+
if (nearLeft) {
|
|
1025
|
+
lefts.pop();
|
|
968
1026
|
}
|
|
969
1027
|
} else {
|
|
970
|
-
|
|
1028
|
+
// <msub> bình thường
|
|
1029
|
+
const parsed = parse(node);
|
|
1030
|
+
if (parsed) {
|
|
1031
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1032
|
+
parts.push(parsed);
|
|
1033
|
+
}
|
|
971
1034
|
}
|
|
1035
|
+
// --- END PATCH V6 ---
|
|
972
1036
|
} else {
|
|
973
|
-
|
|
1037
|
+
// Các node khác như <mtext>, #text, v.v.
|
|
1038
|
+
const parsed = parse(node);
|
|
1039
|
+
if (parsed) {
|
|
1040
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1041
|
+
parts.push(parsed);
|
|
1042
|
+
}
|
|
974
1043
|
}
|
|
975
1044
|
});
|
|
976
1045
|
lefts = undefined;
|
|
@@ -817,11 +817,11 @@
|
|
|
817
817
|
// Math Text
|
|
818
818
|
function parseElementMtext(node) {
|
|
819
819
|
let content = NodeTool.getNodeText(node)
|
|
820
|
-
// Handle operators and spacing only
|
|
821
820
|
.replace(/\s*=\s*/g, " = ")
|
|
822
821
|
.replace(/\s*\.\s*/g, " \\cdot ")
|
|
823
822
|
.trim();
|
|
824
823
|
|
|
824
|
+
// Map đặc biệt
|
|
825
825
|
const specialMTextMap = {
|
|
826
826
|
ℝ: "\\mathbb{R}",
|
|
827
827
|
ℤ: "\\mathbb{Z}",
|
|
@@ -834,14 +834,21 @@
|
|
|
834
834
|
};
|
|
835
835
|
if (specialMTextMap[content]) return specialMTextMap[content];
|
|
836
836
|
|
|
837
|
-
//
|
|
837
|
+
// Nếu đã là lệnh LaTeX thì giữ nguyên
|
|
838
|
+
if (content.startsWith("\\")) return content;
|
|
839
|
+
|
|
840
|
+
// Bọc token chữ/số liền nhau bằng \text{...} (hóa học: Cu, OH, NaOH, ...)
|
|
841
|
+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(content)) {
|
|
842
|
+
return `\\text{${content}}`;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// (Giữ nguyên xử lý units nếu mtext chứa dấu ngoặc tròn)
|
|
838
846
|
if (content.includes("(") && content.includes(")")) {
|
|
839
847
|
const parts = content.split(/(\([^)]+\))/);
|
|
840
848
|
content = parts
|
|
841
849
|
.map((part) => {
|
|
842
850
|
if (part.startsWith("(") && part.endsWith(")")) {
|
|
843
|
-
|
|
844
|
-
return `\\mathrm{${part}}`;
|
|
851
|
+
return `\\text{${part}}`;
|
|
845
852
|
}
|
|
846
853
|
return part;
|
|
847
854
|
})
|
|
@@ -893,9 +900,8 @@
|
|
|
893
900
|
|
|
894
901
|
function renderChildren(children) {
|
|
895
902
|
const parts = [];
|
|
896
|
-
let lefts = [];
|
|
903
|
+
let lefts = []; // PATCH: Special case for set-builder style: leading { ... trailing }
|
|
897
904
|
|
|
898
|
-
// PATCH: Special case for set-builder style: leading { ... trailing }
|
|
899
905
|
if (
|
|
900
906
|
children.length >= 3 &&
|
|
901
907
|
NodeTool.getNodeName(children[0]) === "mo" &&
|
|
@@ -907,8 +913,9 @@
|
|
|
907
913
|
const innerContent = Array.prototype.slice
|
|
908
914
|
.call(children, 1, -1)
|
|
909
915
|
.map((child) => parse(child))
|
|
910
|
-
.join("");
|
|
911
|
-
|
|
916
|
+
.join(""); // Chỉ trả về nếu nội dung không rỗng
|
|
917
|
+
const result = `\\left\\{${innerContent}\\right\\}`;
|
|
918
|
+
return result;
|
|
912
919
|
}
|
|
913
920
|
|
|
914
921
|
Array.prototype.forEach.call(children, (node, idx) => {
|
|
@@ -926,18 +933,19 @@
|
|
|
926
933
|
) {
|
|
927
934
|
// Only vars, nums, ops (possibly Greek); join with thin space
|
|
928
935
|
const str = mrowKids.map((k) => parse(k)).join("\\,");
|
|
929
|
-
|
|
936
|
+
if (str) {
|
|
937
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
938
|
+
parts.push(str);
|
|
939
|
+
}
|
|
930
940
|
return;
|
|
931
941
|
}
|
|
932
|
-
}
|
|
933
|
-
// END PATCH
|
|
942
|
+
} // END PATCH
|
|
934
943
|
if (NodeTool.getNodeName(node) === "mo") {
|
|
935
944
|
const op = NodeTool.getNodeText(node).trim();
|
|
936
945
|
if (Brackets.contains(op)) {
|
|
937
946
|
let stretchy = NodeTool.getAttr(node, "stretchy", "true");
|
|
938
947
|
stretchy = ["", "true"].indexOf(stretchy) > -1;
|
|
939
948
|
|
|
940
|
-
// Luôn escape dấu ngoặc nhọn cho LaTeX
|
|
941
949
|
let escapedOp = op;
|
|
942
950
|
if (op === "{" || op === "}") {
|
|
943
951
|
escapedOp = `\\${op}`;
|
|
@@ -945,19 +953,27 @@
|
|
|
945
953
|
|
|
946
954
|
if (Brackets.isRight(op)) {
|
|
947
955
|
const nearLeft = lefts[lefts.length - 1];
|
|
948
|
-
if (nearLeft
|
|
956
|
+
if (nearLeft) {
|
|
949
957
|
const parentNode = node.parentNode;
|
|
950
958
|
const isInPower =
|
|
951
959
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
952
960
|
|
|
961
|
+
let partToPush = "";
|
|
953
962
|
if (stretchy && !isInPower) {
|
|
954
|
-
|
|
963
|
+
partToPush = `\\right${escapedOp}`;
|
|
955
964
|
} else {
|
|
956
|
-
|
|
965
|
+
partToPush = escapedOp;
|
|
966
|
+
}
|
|
967
|
+
if (partToPush) {
|
|
968
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
969
|
+
parts.push(partToPush);
|
|
957
970
|
}
|
|
958
971
|
lefts.pop();
|
|
959
972
|
} else {
|
|
960
|
-
|
|
973
|
+
if (escapedOp) {
|
|
974
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
975
|
+
parts.push(escapedOp);
|
|
976
|
+
}
|
|
961
977
|
}
|
|
962
978
|
} else {
|
|
963
979
|
// Là ngoặc trái
|
|
@@ -965,18 +981,71 @@
|
|
|
965
981
|
const isInPower =
|
|
966
982
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
967
983
|
|
|
984
|
+
let partToPush = "";
|
|
968
985
|
if (stretchy && !isInPower) {
|
|
969
|
-
|
|
986
|
+
partToPush = `\\left${escapedOp}`;
|
|
970
987
|
} else {
|
|
971
|
-
|
|
988
|
+
partToPush = escapedOp;
|
|
989
|
+
}
|
|
990
|
+
if (partToPush) {
|
|
991
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
992
|
+
parts.push(partToPush);
|
|
993
|
+
}
|
|
994
|
+
lefts.push(op);
|
|
995
|
+
}
|
|
996
|
+
} else {
|
|
997
|
+
const parsedOperator = parseOperator(node);
|
|
998
|
+
if (parsedOperator) {
|
|
999
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1000
|
+
parts.push(parsedOperator);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// --- START PATCH V6 (Giữ nguyên logic V5) ---
|
|
1005
|
+
} else if (NodeTool.getNodeName(node) === "msub") {
|
|
1006
|
+
const subChildren = Array.from(NodeTool.getChildren(node));
|
|
1007
|
+
if (
|
|
1008
|
+
subChildren.length === 2 &&
|
|
1009
|
+
NodeTool.getNodeName(subChildren[0]) === "mo" &&
|
|
1010
|
+
NodeTool.getNodeText(subChildren[0]).trim() === ")"
|
|
1011
|
+
) {
|
|
1012
|
+
// ĐÚNG LÀ NGOẠI LỆ
|
|
1013
|
+
|
|
1014
|
+
const sub = parse(subChildren[1]);
|
|
1015
|
+
// Mảng 'parts' lúc này "sạch" và là: ["\text{Cu}", "\left(", "\text{OH}"]
|
|
1016
|
+
const lastPart = parts.pop(); // lastPart = "\text{OH}"
|
|
1017
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\left("]
|
|
1018
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1019
|
+
if (parts[i] && parts[i].trim() === "\\left(") {
|
|
1020
|
+
parts.splice(i, 1); // Xóa "\left("
|
|
1021
|
+
break;
|
|
972
1022
|
}
|
|
973
|
-
|
|
1023
|
+
_;
|
|
1024
|
+
} // Mảng 'parts' lúc này là: ["\text{Cu}"]
|
|
1025
|
+
|
|
1026
|
+
parts.push(`${lastPart}_{${sub}}`); // Push "\text{OH}_{2}"
|
|
1027
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\text{OH}_{2}"]
|
|
1028
|
+
|
|
1029
|
+
const nearLeft = lefts[lefts.length - 1];
|
|
1030
|
+
if (nearLeft) {
|
|
1031
|
+
lefts.pop();
|
|
974
1032
|
}
|
|
975
1033
|
} else {
|
|
976
|
-
|
|
1034
|
+
// <msub> bình thường
|
|
1035
|
+
const parsed = parse(node);
|
|
1036
|
+
if (parsed) {
|
|
1037
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1038
|
+
parts.push(parsed);
|
|
1039
|
+
}
|
|
977
1040
|
}
|
|
1041
|
+
// --- END PATCH V6 ---
|
|
978
1042
|
} else {
|
|
979
|
-
|
|
1043
|
+
// Các node khác như <mtext>, #text, v.v.
|
|
1044
|
+
const parsed = parse(node);
|
|
1045
|
+
if (parsed) {
|
|
1046
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1047
|
+
parts.push(parsed);
|
|
1048
|
+
}
|
|
980
1049
|
}
|
|
981
1050
|
});
|
|
982
1051
|
lefts = undefined;
|
package/lib/mathml2latex.cjs.js
CHANGED
|
@@ -813,11 +813,11 @@ function parseElementMs(node) {
|
|
|
813
813
|
// Math Text
|
|
814
814
|
function parseElementMtext(node) {
|
|
815
815
|
let content = NodeTool.getNodeText(node)
|
|
816
|
-
// Handle operators and spacing only
|
|
817
816
|
.replace(/\s*=\s*/g, " = ")
|
|
818
817
|
.replace(/\s*\.\s*/g, " \\cdot ")
|
|
819
818
|
.trim();
|
|
820
819
|
|
|
820
|
+
// Map đặc biệt
|
|
821
821
|
const specialMTextMap = {
|
|
822
822
|
ℝ: "\\mathbb{R}",
|
|
823
823
|
ℤ: "\\mathbb{Z}",
|
|
@@ -830,14 +830,21 @@ function parseElementMtext(node) {
|
|
|
830
830
|
};
|
|
831
831
|
if (specialMTextMap[content]) return specialMTextMap[content];
|
|
832
832
|
|
|
833
|
-
//
|
|
833
|
+
// Nếu đã là lệnh LaTeX thì giữ nguyên
|
|
834
|
+
if (content.startsWith("\\")) return content;
|
|
835
|
+
|
|
836
|
+
// Bọc token chữ/số liền nhau bằng \text{...} (hóa học: Cu, OH, NaOH, ...)
|
|
837
|
+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(content)) {
|
|
838
|
+
return `\\text{${content}}`;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// (Giữ nguyên xử lý units nếu mtext chứa dấu ngoặc tròn)
|
|
834
842
|
if (content.includes("(") && content.includes(")")) {
|
|
835
843
|
const parts = content.split(/(\([^)]+\))/);
|
|
836
844
|
content = parts
|
|
837
845
|
.map((part) => {
|
|
838
846
|
if (part.startsWith("(") && part.endsWith(")")) {
|
|
839
|
-
|
|
840
|
-
return `\\mathrm{${part}}`;
|
|
847
|
+
return `\\text{${part}}`;
|
|
841
848
|
}
|
|
842
849
|
return part;
|
|
843
850
|
})
|
|
@@ -889,9 +896,8 @@ function parseContainer(node, children) {
|
|
|
889
896
|
|
|
890
897
|
function renderChildren(children) {
|
|
891
898
|
const parts = [];
|
|
892
|
-
let lefts = [];
|
|
899
|
+
let lefts = []; // PATCH: Special case for set-builder style: leading { ... trailing }
|
|
893
900
|
|
|
894
|
-
// PATCH: Special case for set-builder style: leading { ... trailing }
|
|
895
901
|
if (
|
|
896
902
|
children.length >= 3 &&
|
|
897
903
|
NodeTool.getNodeName(children[0]) === "mo" &&
|
|
@@ -903,8 +909,9 @@ function renderChildren(children) {
|
|
|
903
909
|
const innerContent = Array.prototype.slice
|
|
904
910
|
.call(children, 1, -1)
|
|
905
911
|
.map((child) => parse(child))
|
|
906
|
-
.join("");
|
|
907
|
-
|
|
912
|
+
.join(""); // Chỉ trả về nếu nội dung không rỗng
|
|
913
|
+
const result = `\\left\\{${innerContent}\\right\\}`;
|
|
914
|
+
return result;
|
|
908
915
|
}
|
|
909
916
|
|
|
910
917
|
Array.prototype.forEach.call(children, (node, idx) => {
|
|
@@ -922,18 +929,19 @@ function renderChildren(children) {
|
|
|
922
929
|
) {
|
|
923
930
|
// Only vars, nums, ops (possibly Greek); join with thin space
|
|
924
931
|
const str = mrowKids.map((k) => parse(k)).join("\\,");
|
|
925
|
-
|
|
932
|
+
if (str) {
|
|
933
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
934
|
+
parts.push(str);
|
|
935
|
+
}
|
|
926
936
|
return;
|
|
927
937
|
}
|
|
928
|
-
}
|
|
929
|
-
// END PATCH
|
|
938
|
+
} // END PATCH
|
|
930
939
|
if (NodeTool.getNodeName(node) === "mo") {
|
|
931
940
|
const op = NodeTool.getNodeText(node).trim();
|
|
932
941
|
if (Brackets.contains(op)) {
|
|
933
942
|
let stretchy = NodeTool.getAttr(node, "stretchy", "true");
|
|
934
943
|
stretchy = ["", "true"].indexOf(stretchy) > -1;
|
|
935
944
|
|
|
936
|
-
// Luôn escape dấu ngoặc nhọn cho LaTeX
|
|
937
945
|
let escapedOp = op;
|
|
938
946
|
if (op === "{" || op === "}") {
|
|
939
947
|
escapedOp = `\\${op}`;
|
|
@@ -941,19 +949,27 @@ function renderChildren(children) {
|
|
|
941
949
|
|
|
942
950
|
if (Brackets.isRight(op)) {
|
|
943
951
|
const nearLeft = lefts[lefts.length - 1];
|
|
944
|
-
if (nearLeft
|
|
952
|
+
if (nearLeft) {
|
|
945
953
|
const parentNode = node.parentNode;
|
|
946
954
|
const isInPower =
|
|
947
955
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
948
956
|
|
|
957
|
+
let partToPush = "";
|
|
949
958
|
if (stretchy && !isInPower) {
|
|
950
|
-
|
|
959
|
+
partToPush = `\\right${escapedOp}`;
|
|
951
960
|
} else {
|
|
952
|
-
|
|
961
|
+
partToPush = escapedOp;
|
|
962
|
+
}
|
|
963
|
+
if (partToPush) {
|
|
964
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
965
|
+
parts.push(partToPush);
|
|
953
966
|
}
|
|
954
967
|
lefts.pop();
|
|
955
968
|
} else {
|
|
956
|
-
|
|
969
|
+
if (escapedOp) {
|
|
970
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
971
|
+
parts.push(escapedOp);
|
|
972
|
+
}
|
|
957
973
|
}
|
|
958
974
|
} else {
|
|
959
975
|
// Là ngoặc trái
|
|
@@ -961,18 +977,71 @@ function renderChildren(children) {
|
|
|
961
977
|
const isInPower =
|
|
962
978
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
963
979
|
|
|
980
|
+
let partToPush = "";
|
|
964
981
|
if (stretchy && !isInPower) {
|
|
965
|
-
|
|
982
|
+
partToPush = `\\left${escapedOp}`;
|
|
966
983
|
} else {
|
|
967
|
-
|
|
984
|
+
partToPush = escapedOp;
|
|
985
|
+
}
|
|
986
|
+
if (partToPush) {
|
|
987
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
988
|
+
parts.push(partToPush);
|
|
989
|
+
}
|
|
990
|
+
lefts.push(op);
|
|
991
|
+
}
|
|
992
|
+
} else {
|
|
993
|
+
const parsedOperator = parseOperator(node);
|
|
994
|
+
if (parsedOperator) {
|
|
995
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
996
|
+
parts.push(parsedOperator);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// --- START PATCH V6 (Giữ nguyên logic V5) ---
|
|
1001
|
+
} else if (NodeTool.getNodeName(node) === "msub") {
|
|
1002
|
+
const subChildren = Array.from(NodeTool.getChildren(node));
|
|
1003
|
+
if (
|
|
1004
|
+
subChildren.length === 2 &&
|
|
1005
|
+
NodeTool.getNodeName(subChildren[0]) === "mo" &&
|
|
1006
|
+
NodeTool.getNodeText(subChildren[0]).trim() === ")"
|
|
1007
|
+
) {
|
|
1008
|
+
// ĐÚNG LÀ NGOẠI LỆ
|
|
1009
|
+
|
|
1010
|
+
const sub = parse(subChildren[1]);
|
|
1011
|
+
// Mảng 'parts' lúc này "sạch" và là: ["\text{Cu}", "\left(", "\text{OH}"]
|
|
1012
|
+
const lastPart = parts.pop(); // lastPart = "\text{OH}"
|
|
1013
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\left("]
|
|
1014
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1015
|
+
if (parts[i] && parts[i].trim() === "\\left(") {
|
|
1016
|
+
parts.splice(i, 1); // Xóa "\left("
|
|
1017
|
+
break;
|
|
968
1018
|
}
|
|
969
|
-
|
|
1019
|
+
_;
|
|
1020
|
+
} // Mảng 'parts' lúc này là: ["\text{Cu}"]
|
|
1021
|
+
|
|
1022
|
+
parts.push(`${lastPart}_{${sub}}`); // Push "\text{OH}_{2}"
|
|
1023
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\text{OH}_{2}"]
|
|
1024
|
+
|
|
1025
|
+
const nearLeft = lefts[lefts.length - 1];
|
|
1026
|
+
if (nearLeft) {
|
|
1027
|
+
lefts.pop();
|
|
970
1028
|
}
|
|
971
1029
|
} else {
|
|
972
|
-
|
|
1030
|
+
// <msub> bình thường
|
|
1031
|
+
const parsed = parse(node);
|
|
1032
|
+
if (parsed) {
|
|
1033
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1034
|
+
parts.push(parsed);
|
|
1035
|
+
}
|
|
973
1036
|
}
|
|
1037
|
+
// --- END PATCH V6 ---
|
|
974
1038
|
} else {
|
|
975
|
-
|
|
1039
|
+
// Các node khác như <mtext>, #text, v.v.
|
|
1040
|
+
const parsed = parse(node);
|
|
1041
|
+
if (parsed) {
|
|
1042
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1043
|
+
parts.push(parsed);
|
|
1044
|
+
}
|
|
976
1045
|
}
|
|
977
1046
|
});
|
|
978
1047
|
lefts = undefined;
|
package/lib/mathml2latex.es.js
CHANGED
|
@@ -811,11 +811,11 @@ function parseElementMs(node) {
|
|
|
811
811
|
// Math Text
|
|
812
812
|
function parseElementMtext(node) {
|
|
813
813
|
let content = NodeTool.getNodeText(node)
|
|
814
|
-
// Handle operators and spacing only
|
|
815
814
|
.replace(/\s*=\s*/g, " = ")
|
|
816
815
|
.replace(/\s*\.\s*/g, " \\cdot ")
|
|
817
816
|
.trim();
|
|
818
817
|
|
|
818
|
+
// Map đặc biệt
|
|
819
819
|
const specialMTextMap = {
|
|
820
820
|
ℝ: "\\mathbb{R}",
|
|
821
821
|
ℤ: "\\mathbb{Z}",
|
|
@@ -828,14 +828,21 @@ function parseElementMtext(node) {
|
|
|
828
828
|
};
|
|
829
829
|
if (specialMTextMap[content]) return specialMTextMap[content];
|
|
830
830
|
|
|
831
|
-
//
|
|
831
|
+
// Nếu đã là lệnh LaTeX thì giữ nguyên
|
|
832
|
+
if (content.startsWith("\\")) return content;
|
|
833
|
+
|
|
834
|
+
// Bọc token chữ/số liền nhau bằng \text{...} (hóa học: Cu, OH, NaOH, ...)
|
|
835
|
+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(content)) {
|
|
836
|
+
return `\\text{${content}}`;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// (Giữ nguyên xử lý units nếu mtext chứa dấu ngoặc tròn)
|
|
832
840
|
if (content.includes("(") && content.includes(")")) {
|
|
833
841
|
const parts = content.split(/(\([^)]+\))/);
|
|
834
842
|
content = parts
|
|
835
843
|
.map((part) => {
|
|
836
844
|
if (part.startsWith("(") && part.endsWith(")")) {
|
|
837
|
-
|
|
838
|
-
return `\\mathrm{${part}}`;
|
|
845
|
+
return `\\text{${part}}`;
|
|
839
846
|
}
|
|
840
847
|
return part;
|
|
841
848
|
})
|
|
@@ -887,9 +894,8 @@ function parseContainer(node, children) {
|
|
|
887
894
|
|
|
888
895
|
function renderChildren(children) {
|
|
889
896
|
const parts = [];
|
|
890
|
-
let lefts = [];
|
|
897
|
+
let lefts = []; // PATCH: Special case for set-builder style: leading { ... trailing }
|
|
891
898
|
|
|
892
|
-
// PATCH: Special case for set-builder style: leading { ... trailing }
|
|
893
899
|
if (
|
|
894
900
|
children.length >= 3 &&
|
|
895
901
|
NodeTool.getNodeName(children[0]) === "mo" &&
|
|
@@ -901,8 +907,9 @@ function renderChildren(children) {
|
|
|
901
907
|
const innerContent = Array.prototype.slice
|
|
902
908
|
.call(children, 1, -1)
|
|
903
909
|
.map((child) => parse(child))
|
|
904
|
-
.join("");
|
|
905
|
-
|
|
910
|
+
.join(""); // Chỉ trả về nếu nội dung không rỗng
|
|
911
|
+
const result = `\\left\\{${innerContent}\\right\\}`;
|
|
912
|
+
return result;
|
|
906
913
|
}
|
|
907
914
|
|
|
908
915
|
Array.prototype.forEach.call(children, (node, idx) => {
|
|
@@ -920,18 +927,19 @@ function renderChildren(children) {
|
|
|
920
927
|
) {
|
|
921
928
|
// Only vars, nums, ops (possibly Greek); join with thin space
|
|
922
929
|
const str = mrowKids.map((k) => parse(k)).join("\\,");
|
|
923
|
-
|
|
930
|
+
if (str) {
|
|
931
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
932
|
+
parts.push(str);
|
|
933
|
+
}
|
|
924
934
|
return;
|
|
925
935
|
}
|
|
926
|
-
}
|
|
927
|
-
// END PATCH
|
|
936
|
+
} // END PATCH
|
|
928
937
|
if (NodeTool.getNodeName(node) === "mo") {
|
|
929
938
|
const op = NodeTool.getNodeText(node).trim();
|
|
930
939
|
if (Brackets.contains(op)) {
|
|
931
940
|
let stretchy = NodeTool.getAttr(node, "stretchy", "true");
|
|
932
941
|
stretchy = ["", "true"].indexOf(stretchy) > -1;
|
|
933
942
|
|
|
934
|
-
// Luôn escape dấu ngoặc nhọn cho LaTeX
|
|
935
943
|
let escapedOp = op;
|
|
936
944
|
if (op === "{" || op === "}") {
|
|
937
945
|
escapedOp = `\\${op}`;
|
|
@@ -939,19 +947,27 @@ function renderChildren(children) {
|
|
|
939
947
|
|
|
940
948
|
if (Brackets.isRight(op)) {
|
|
941
949
|
const nearLeft = lefts[lefts.length - 1];
|
|
942
|
-
if (nearLeft
|
|
950
|
+
if (nearLeft) {
|
|
943
951
|
const parentNode = node.parentNode;
|
|
944
952
|
const isInPower =
|
|
945
953
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
946
954
|
|
|
955
|
+
let partToPush = "";
|
|
947
956
|
if (stretchy && !isInPower) {
|
|
948
|
-
|
|
957
|
+
partToPush = `\\right${escapedOp}`;
|
|
949
958
|
} else {
|
|
950
|
-
|
|
959
|
+
partToPush = escapedOp;
|
|
960
|
+
}
|
|
961
|
+
if (partToPush) {
|
|
962
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
963
|
+
parts.push(partToPush);
|
|
951
964
|
}
|
|
952
965
|
lefts.pop();
|
|
953
966
|
} else {
|
|
954
|
-
|
|
967
|
+
if (escapedOp) {
|
|
968
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
969
|
+
parts.push(escapedOp);
|
|
970
|
+
}
|
|
955
971
|
}
|
|
956
972
|
} else {
|
|
957
973
|
// Là ngoặc trái
|
|
@@ -959,18 +975,71 @@ function renderChildren(children) {
|
|
|
959
975
|
const isInPower =
|
|
960
976
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
961
977
|
|
|
978
|
+
let partToPush = "";
|
|
962
979
|
if (stretchy && !isInPower) {
|
|
963
|
-
|
|
980
|
+
partToPush = `\\left${escapedOp}`;
|
|
964
981
|
} else {
|
|
965
|
-
|
|
982
|
+
partToPush = escapedOp;
|
|
983
|
+
}
|
|
984
|
+
if (partToPush) {
|
|
985
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
986
|
+
parts.push(partToPush);
|
|
987
|
+
}
|
|
988
|
+
lefts.push(op);
|
|
989
|
+
}
|
|
990
|
+
} else {
|
|
991
|
+
const parsedOperator = parseOperator(node);
|
|
992
|
+
if (parsedOperator) {
|
|
993
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
994
|
+
parts.push(parsedOperator);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// --- START PATCH V6 (Giữ nguyên logic V5) ---
|
|
999
|
+
} else if (NodeTool.getNodeName(node) === "msub") {
|
|
1000
|
+
const subChildren = Array.from(NodeTool.getChildren(node));
|
|
1001
|
+
if (
|
|
1002
|
+
subChildren.length === 2 &&
|
|
1003
|
+
NodeTool.getNodeName(subChildren[0]) === "mo" &&
|
|
1004
|
+
NodeTool.getNodeText(subChildren[0]).trim() === ")"
|
|
1005
|
+
) {
|
|
1006
|
+
// ĐÚNG LÀ NGOẠI LỆ
|
|
1007
|
+
|
|
1008
|
+
const sub = parse(subChildren[1]);
|
|
1009
|
+
// Mảng 'parts' lúc này "sạch" và là: ["\text{Cu}", "\left(", "\text{OH}"]
|
|
1010
|
+
const lastPart = parts.pop(); // lastPart = "\text{OH}"
|
|
1011
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\left("]
|
|
1012
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1013
|
+
if (parts[i] && parts[i].trim() === "\\left(") {
|
|
1014
|
+
parts.splice(i, 1); // Xóa "\left("
|
|
1015
|
+
break;
|
|
966
1016
|
}
|
|
967
|
-
|
|
1017
|
+
_;
|
|
1018
|
+
} // Mảng 'parts' lúc này là: ["\text{Cu}"]
|
|
1019
|
+
|
|
1020
|
+
parts.push(`${lastPart}_{${sub}}`); // Push "\text{OH}_{2}"
|
|
1021
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\text{OH}_{2}"]
|
|
1022
|
+
|
|
1023
|
+
const nearLeft = lefts[lefts.length - 1];
|
|
1024
|
+
if (nearLeft) {
|
|
1025
|
+
lefts.pop();
|
|
968
1026
|
}
|
|
969
1027
|
} else {
|
|
970
|
-
|
|
1028
|
+
// <msub> bình thường
|
|
1029
|
+
const parsed = parse(node);
|
|
1030
|
+
if (parsed) {
|
|
1031
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1032
|
+
parts.push(parsed);
|
|
1033
|
+
}
|
|
971
1034
|
}
|
|
1035
|
+
// --- END PATCH V6 ---
|
|
972
1036
|
} else {
|
|
973
|
-
|
|
1037
|
+
// Các node khác như <mtext>, #text, v.v.
|
|
1038
|
+
const parsed = parse(node);
|
|
1039
|
+
if (parsed) {
|
|
1040
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1041
|
+
parts.push(parsed);
|
|
1042
|
+
}
|
|
974
1043
|
}
|
|
975
1044
|
});
|
|
976
1045
|
lefts = undefined;
|
package/lib/mathml2latex.umd.js
CHANGED
|
@@ -817,11 +817,11 @@
|
|
|
817
817
|
// Math Text
|
|
818
818
|
function parseElementMtext(node) {
|
|
819
819
|
let content = NodeTool.getNodeText(node)
|
|
820
|
-
// Handle operators and spacing only
|
|
821
820
|
.replace(/\s*=\s*/g, " = ")
|
|
822
821
|
.replace(/\s*\.\s*/g, " \\cdot ")
|
|
823
822
|
.trim();
|
|
824
823
|
|
|
824
|
+
// Map đặc biệt
|
|
825
825
|
const specialMTextMap = {
|
|
826
826
|
ℝ: "\\mathbb{R}",
|
|
827
827
|
ℤ: "\\mathbb{Z}",
|
|
@@ -834,14 +834,21 @@
|
|
|
834
834
|
};
|
|
835
835
|
if (specialMTextMap[content]) return specialMTextMap[content];
|
|
836
836
|
|
|
837
|
-
//
|
|
837
|
+
// Nếu đã là lệnh LaTeX thì giữ nguyên
|
|
838
|
+
if (content.startsWith("\\")) return content;
|
|
839
|
+
|
|
840
|
+
// Bọc token chữ/số liền nhau bằng \text{...} (hóa học: Cu, OH, NaOH, ...)
|
|
841
|
+
if (/^[A-Za-z][A-Za-z0-9]*$/.test(content)) {
|
|
842
|
+
return `\\text{${content}}`;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// (Giữ nguyên xử lý units nếu mtext chứa dấu ngoặc tròn)
|
|
838
846
|
if (content.includes("(") && content.includes(")")) {
|
|
839
847
|
const parts = content.split(/(\([^)]+\))/);
|
|
840
848
|
content = parts
|
|
841
849
|
.map((part) => {
|
|
842
850
|
if (part.startsWith("(") && part.endsWith(")")) {
|
|
843
|
-
|
|
844
|
-
return `\\mathrm{${part}}`;
|
|
851
|
+
return `\\text{${part}}`;
|
|
845
852
|
}
|
|
846
853
|
return part;
|
|
847
854
|
})
|
|
@@ -893,9 +900,8 @@
|
|
|
893
900
|
|
|
894
901
|
function renderChildren(children) {
|
|
895
902
|
const parts = [];
|
|
896
|
-
let lefts = [];
|
|
903
|
+
let lefts = []; // PATCH: Special case for set-builder style: leading { ... trailing }
|
|
897
904
|
|
|
898
|
-
// PATCH: Special case for set-builder style: leading { ... trailing }
|
|
899
905
|
if (
|
|
900
906
|
children.length >= 3 &&
|
|
901
907
|
NodeTool.getNodeName(children[0]) === "mo" &&
|
|
@@ -907,8 +913,9 @@
|
|
|
907
913
|
const innerContent = Array.prototype.slice
|
|
908
914
|
.call(children, 1, -1)
|
|
909
915
|
.map((child) => parse(child))
|
|
910
|
-
.join("");
|
|
911
|
-
|
|
916
|
+
.join(""); // Chỉ trả về nếu nội dung không rỗng
|
|
917
|
+
const result = `\\left\\{${innerContent}\\right\\}`;
|
|
918
|
+
return result;
|
|
912
919
|
}
|
|
913
920
|
|
|
914
921
|
Array.prototype.forEach.call(children, (node, idx) => {
|
|
@@ -926,18 +933,19 @@
|
|
|
926
933
|
) {
|
|
927
934
|
// Only vars, nums, ops (possibly Greek); join with thin space
|
|
928
935
|
const str = mrowKids.map((k) => parse(k)).join("\\,");
|
|
929
|
-
|
|
936
|
+
if (str) {
|
|
937
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
938
|
+
parts.push(str);
|
|
939
|
+
}
|
|
930
940
|
return;
|
|
931
941
|
}
|
|
932
|
-
}
|
|
933
|
-
// END PATCH
|
|
942
|
+
} // END PATCH
|
|
934
943
|
if (NodeTool.getNodeName(node) === "mo") {
|
|
935
944
|
const op = NodeTool.getNodeText(node).trim();
|
|
936
945
|
if (Brackets.contains(op)) {
|
|
937
946
|
let stretchy = NodeTool.getAttr(node, "stretchy", "true");
|
|
938
947
|
stretchy = ["", "true"].indexOf(stretchy) > -1;
|
|
939
948
|
|
|
940
|
-
// Luôn escape dấu ngoặc nhọn cho LaTeX
|
|
941
949
|
let escapedOp = op;
|
|
942
950
|
if (op === "{" || op === "}") {
|
|
943
951
|
escapedOp = `\\${op}`;
|
|
@@ -945,19 +953,27 @@
|
|
|
945
953
|
|
|
946
954
|
if (Brackets.isRight(op)) {
|
|
947
955
|
const nearLeft = lefts[lefts.length - 1];
|
|
948
|
-
if (nearLeft
|
|
956
|
+
if (nearLeft) {
|
|
949
957
|
const parentNode = node.parentNode;
|
|
950
958
|
const isInPower =
|
|
951
959
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
952
960
|
|
|
961
|
+
let partToPush = "";
|
|
953
962
|
if (stretchy && !isInPower) {
|
|
954
|
-
|
|
963
|
+
partToPush = `\\right${escapedOp}`;
|
|
955
964
|
} else {
|
|
956
|
-
|
|
965
|
+
partToPush = escapedOp;
|
|
966
|
+
}
|
|
967
|
+
if (partToPush) {
|
|
968
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
969
|
+
parts.push(partToPush);
|
|
957
970
|
}
|
|
958
971
|
lefts.pop();
|
|
959
972
|
} else {
|
|
960
|
-
|
|
973
|
+
if (escapedOp) {
|
|
974
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
975
|
+
parts.push(escapedOp);
|
|
976
|
+
}
|
|
961
977
|
}
|
|
962
978
|
} else {
|
|
963
979
|
// Là ngoặc trái
|
|
@@ -965,18 +981,71 @@
|
|
|
965
981
|
const isInPower =
|
|
966
982
|
parentNode && NodeTool.getNodeName(parentNode) === "msup";
|
|
967
983
|
|
|
984
|
+
let partToPush = "";
|
|
968
985
|
if (stretchy && !isInPower) {
|
|
969
|
-
|
|
986
|
+
partToPush = `\\left${escapedOp}`;
|
|
970
987
|
} else {
|
|
971
|
-
|
|
988
|
+
partToPush = escapedOp;
|
|
989
|
+
}
|
|
990
|
+
if (partToPush) {
|
|
991
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
992
|
+
parts.push(partToPush);
|
|
993
|
+
}
|
|
994
|
+
lefts.push(op);
|
|
995
|
+
}
|
|
996
|
+
} else {
|
|
997
|
+
const parsedOperator = parseOperator(node);
|
|
998
|
+
if (parsedOperator) {
|
|
999
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1000
|
+
parts.push(parsedOperator);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// --- START PATCH V6 (Giữ nguyên logic V5) ---
|
|
1005
|
+
} else if (NodeTool.getNodeName(node) === "msub") {
|
|
1006
|
+
const subChildren = Array.from(NodeTool.getChildren(node));
|
|
1007
|
+
if (
|
|
1008
|
+
subChildren.length === 2 &&
|
|
1009
|
+
NodeTool.getNodeName(subChildren[0]) === "mo" &&
|
|
1010
|
+
NodeTool.getNodeText(subChildren[0]).trim() === ")"
|
|
1011
|
+
) {
|
|
1012
|
+
// ĐÚNG LÀ NGOẠI LỆ
|
|
1013
|
+
|
|
1014
|
+
const sub = parse(subChildren[1]);
|
|
1015
|
+
// Mảng 'parts' lúc này "sạch" và là: ["\text{Cu}", "\left(", "\text{OH}"]
|
|
1016
|
+
const lastPart = parts.pop(); // lastPart = "\text{OH}"
|
|
1017
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\left("]
|
|
1018
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1019
|
+
if (parts[i] && parts[i].trim() === "\\left(") {
|
|
1020
|
+
parts.splice(i, 1); // Xóa "\left("
|
|
1021
|
+
break;
|
|
972
1022
|
}
|
|
973
|
-
|
|
1023
|
+
_;
|
|
1024
|
+
} // Mảng 'parts' lúc này là: ["\text{Cu}"]
|
|
1025
|
+
|
|
1026
|
+
parts.push(`${lastPart}_{${sub}}`); // Push "\text{OH}_{2}"
|
|
1027
|
+
// Mảng 'parts' lúc này là: ["\text{Cu}", "\text{OH}_{2}"]
|
|
1028
|
+
|
|
1029
|
+
const nearLeft = lefts[lefts.length - 1];
|
|
1030
|
+
if (nearLeft) {
|
|
1031
|
+
lefts.pop();
|
|
974
1032
|
}
|
|
975
1033
|
} else {
|
|
976
|
-
|
|
1034
|
+
// <msub> bình thường
|
|
1035
|
+
const parsed = parse(node);
|
|
1036
|
+
if (parsed) {
|
|
1037
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1038
|
+
parts.push(parsed);
|
|
1039
|
+
}
|
|
977
1040
|
}
|
|
1041
|
+
// --- END PATCH V6 ---
|
|
978
1042
|
} else {
|
|
979
|
-
|
|
1043
|
+
// Các node khác như <mtext>, #text, v.v.
|
|
1044
|
+
const parsed = parse(node);
|
|
1045
|
+
if (parsed) {
|
|
1046
|
+
// CHỈ PUSH NẾU KHÔNG RỖNG
|
|
1047
|
+
parts.push(parsed);
|
|
1048
|
+
}
|
|
980
1049
|
}
|
|
981
1050
|
});
|
|
982
1051
|
lefts = undefined;
|