ed-mathml2tex 0.1.0 → 0.1.2
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 +481 -357
- package/lib/mathml2latex.browser.es.js +481 -357
- package/lib/mathml2latex.browser.umd.js +481 -357
- package/lib/mathml2latex.cjs.js +481 -357
- package/lib/mathml2latex.es.js +481 -357
- package/lib/mathml2latex.umd.js +481 -357
- package/package.json +1 -1
|
@@ -584,74 +584,75 @@ T.createMarker = function() {
|
|
|
584
584
|
}
|
|
585
585
|
};
|
|
586
586
|
|
|
587
|
-
function getRender_default(template) {
|
|
588
|
-
return function(node, children) {
|
|
589
|
-
const parts = renderChildren(children);
|
|
590
|
-
return renderTemplate(template, parts);
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
|
|
594
587
|
function getRender_joinSeparator(template, separator = '') {
|
|
595
|
-
return function(node, children) {
|
|
588
|
+
return function (node, children) {
|
|
596
589
|
const parts = renderChildren(children);
|
|
597
590
|
return template.replace('@content', parts.join(separator));
|
|
598
591
|
};
|
|
599
592
|
}
|
|
600
593
|
|
|
601
|
-
function convert(mathmlHtml){
|
|
594
|
+
function convert(mathmlHtml) {
|
|
602
595
|
const math = NodeTool.parseMath(mathmlHtml);
|
|
603
596
|
let result = toLatex(parse(math));
|
|
604
|
-
|
|
605
|
-
//
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
597
|
+
|
|
598
|
+
// Xử lý sau cuối cho các mẫu đặc biệt
|
|
599
|
+
result = result.replace(/\\right\.\./g, "\\right."); // Loại bỏ dấu chấm trùng lặp
|
|
600
|
+
result = result.replace(/\.\s*\./g, "."); // Loại bỏ dấu chấm trùng lặp
|
|
601
|
+
|
|
602
|
+
if (mathmlHtml.includes("<munder>") &&
|
|
603
|
+
mathmlHtml.includes("<mo>→</mo>") &&
|
|
604
|
+
mathmlHtml.includes("<mrow/>")) {
|
|
609
605
|
if (result.includes("\\limits")) {
|
|
610
606
|
result = "\\underset{}{\\rightarrow}";
|
|
611
607
|
}
|
|
612
608
|
}
|
|
613
|
-
|
|
609
|
+
|
|
610
|
+
// Thêm xử lý cho các thẻ MathML khác
|
|
611
|
+
result = result
|
|
612
|
+
.replace(/∞/g, "\\infty") // Vô cực
|
|
613
|
+
.replace(/∑/g, "\\sum") // Tổng
|
|
614
|
+
.replace(/∏/g, "\\prod") // Tích
|
|
615
|
+
.replace(/∫/g, "\\int"); // Tích phân
|
|
616
|
+
|
|
614
617
|
return result;
|
|
615
618
|
}
|
|
616
619
|
|
|
617
620
|
function toLatex(result) {
|
|
618
|
-
// binomial coefficients
|
|
621
|
+
// Xử lý binomial coefficients
|
|
619
622
|
result = result.replace(/\\left\(\\DELETE_BRACKET_L/g, '');
|
|
620
623
|
result = result.replace(/\\DELETE_BRACKET_R\\right\)/g, '');
|
|
621
624
|
result = result.replace(/\\DELETE_BRACKET_L/g, '');
|
|
622
625
|
result = result.replace(/\\DELETE_BRACKET_R/g, '');
|
|
623
|
-
|
|
624
|
-
//
|
|
625
|
-
// Case 1: munder - arrow with empty subscript
|
|
626
|
+
|
|
627
|
+
// Xử lý các trường hợp mũi tên với giới hạn
|
|
626
628
|
result = result.replace(/→\\limits_{}/g, "\\underset{}{\\rightarrow}");
|
|
627
629
|
result = result.replace(/→\\limits_{(\s*)}/g, "\\underset{}{\\rightarrow}");
|
|
628
630
|
result = result.replace(/\\rightarrow\\limits_{}/g, "\\underset{}{\\rightarrow}");
|
|
629
631
|
result = result.replace(/\\rightarrow\\limits_{(\s*)}/g, "\\underset{}{\\rightarrow}");
|
|
630
|
-
|
|
631
|
-
// Case 2: munder - arrow with non-empty subscript
|
|
632
|
+
|
|
632
633
|
result = result.replace(/→\\limits_\{([^}]*)\}/g, "\\underset{$1}{\\rightarrow}");
|
|
633
634
|
result = result.replace(/\\rightarrow\\limits_\{([^}]*)\}/g, "\\underset{$1}{\\rightarrow}");
|
|
634
|
-
|
|
635
|
-
// Case 3: munderover - arrow with both subscript and superscript
|
|
635
|
+
|
|
636
636
|
result = result.replace(/→\\limits_\{([^}]*)\}\^\{([^}]*)\}/g, "\\overset{$2}{\\underset{$1}{\\rightarrow}}");
|
|
637
637
|
result = result.replace(/\\rightarrow\\limits_\{([^}]*)\}\^\{([^}]*)\}/g, "\\overset{$2}{\\underset{$1}{\\rightarrow}}");
|
|
638
|
-
|
|
639
|
-
//
|
|
640
|
-
// Simple expression with arrow superscript: expr^{\rightarrow} → \overrightarrow{expr}
|
|
638
|
+
|
|
639
|
+
// Xử lý vector và các ký hiệu đặc biệt
|
|
641
640
|
result = result.replace(/([^{}\s]+)\^\{\\rightarrow\}/g, "\\overrightarrow{$1}");
|
|
642
641
|
result = result.replace(/\{([^{}]+)\}\^\{\\rightarrow\}/g, "\\overrightarrow{$1}");
|
|
643
|
-
|
|
644
|
-
// Complex expressions with subscripts and arrow: expr_{sub}^{\rightarrow} → \overrightarrow{expr_{sub}}
|
|
645
642
|
result = result.replace(/([A-Za-z0-9]+)_\{([^{}]+)\}\^\{\\rightarrow\}/g, "\\overrightarrow{$1_{$2}}");
|
|
646
643
|
result = result.replace(/([A-Za-z0-9]+)_([0-9])\^\{\\rightarrow\}/g, "\\overrightarrow{$1_$2}");
|
|
647
|
-
|
|
648
|
-
// Very complex expressions: (expr)^{\rightarrow} → \overrightarrow{(expr)}
|
|
649
644
|
result = result.replace(/(\([^()]+\))\^\{\\rightarrow\}/g, "\\overrightarrow{$1}");
|
|
650
|
-
|
|
651
|
-
//
|
|
652
|
-
result = result.replace(
|
|
653
|
-
result = result.replace(
|
|
654
|
-
|
|
645
|
+
|
|
646
|
+
// Thêm xử lý các ký hiệu toán học phổ biến
|
|
647
|
+
result = result.replace(/≤/g, "\\leq");
|
|
648
|
+
result = result.replace(/≥/g, "\\geq");
|
|
649
|
+
result = result.replace(/≠/g, "\\neq");
|
|
650
|
+
result = result.replace(/≈/g, "\\approx");
|
|
651
|
+
result = result.replace(/π/g, "\\pi");
|
|
652
|
+
result = result.replace(/α/g, "\\alpha");
|
|
653
|
+
result = result.replace(/β/g, "\\beta");
|
|
654
|
+
result = result.replace(/γ/g, "\\gamma");
|
|
655
|
+
|
|
655
656
|
return result;
|
|
656
657
|
}
|
|
657
658
|
|
|
@@ -665,15 +666,15 @@ function parse(node) {
|
|
|
665
666
|
}
|
|
666
667
|
|
|
667
668
|
// @see https://www.w3.org/TR/MathML3/chapter7.html
|
|
669
|
+
// Cải tiến parseLeaf để hỗ trợ thêm các ký hiệu
|
|
668
670
|
function parseLeaf(node) {
|
|
669
671
|
let r = '';
|
|
670
672
|
const nodeName = NodeTool.getNodeName(node);
|
|
671
|
-
|
|
672
|
-
// Special case for empty mrow
|
|
673
|
+
|
|
673
674
|
if (nodeName === "mrow" && NodeTool.getNodeText(node).trim() === "") {
|
|
674
675
|
return "";
|
|
675
676
|
}
|
|
676
|
-
|
|
677
|
+
|
|
677
678
|
switch (nodeName) {
|
|
678
679
|
case 'mi':
|
|
679
680
|
r = parseElementMi(node);
|
|
@@ -684,53 +685,76 @@ function parseLeaf(node) {
|
|
|
684
685
|
case 'mo':
|
|
685
686
|
r = parseOperator(node);
|
|
686
687
|
break;
|
|
687
|
-
case 'ms':
|
|
688
|
+
case 'ms':
|
|
689
|
+
r = parseElementMs(node);
|
|
688
690
|
break;
|
|
689
|
-
case 'mtext':
|
|
691
|
+
case 'mtext':
|
|
692
|
+
r = parseElementMtext(node);
|
|
690
693
|
break;
|
|
691
|
-
case 'mglyph':
|
|
694
|
+
case 'mglyph':
|
|
695
|
+
r = parseElementMglyph(node);
|
|
692
696
|
break;
|
|
693
|
-
case 'mprescripts':
|
|
697
|
+
case 'mprescripts':
|
|
698
|
+
r = '';
|
|
694
699
|
break;
|
|
695
|
-
case 'mspace':
|
|
696
|
-
|
|
697
|
-
//TODO other usecase of 'none' ?
|
|
700
|
+
case 'mspace':
|
|
701
|
+
r = parseElementMspace();
|
|
698
702
|
break;
|
|
699
|
-
|
|
703
|
+
case 'none':
|
|
704
|
+
r = '\\:';
|
|
705
|
+
break;
|
|
706
|
+
default:
|
|
707
|
+
r = escapeSpecialChars(NodeTool.getNodeText(node).trim());
|
|
700
708
|
break;
|
|
701
709
|
}
|
|
702
710
|
return r;
|
|
703
711
|
}
|
|
704
712
|
|
|
705
|
-
//
|
|
713
|
+
// Cải tiến parseOperator để hỗ trợ thêm toán tử
|
|
706
714
|
function parseOperator(node) {
|
|
707
715
|
let it = NodeTool.getNodeText(node).trim();
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
+
|
|
717
|
+
const operatorMap = {
|
|
718
|
+
"→": " \\rightarrow ",
|
|
719
|
+
"←": " \\leftarrow ",
|
|
720
|
+
"↔": " \\leftrightarrow ",
|
|
721
|
+
"⇒": " \\Rightarrow ",
|
|
722
|
+
"⇐": " \\Leftarrow ",
|
|
723
|
+
"⇔": " \\Leftrightarrow ",
|
|
724
|
+
"±": " \\pm ",
|
|
725
|
+
"×": " \\times ",
|
|
726
|
+
"÷": " \\div ",
|
|
727
|
+
"∑": " \\sum ",
|
|
728
|
+
"∏": " \\prod ",
|
|
729
|
+
"∫": " \\int ",
|
|
730
|
+
"−": "-",
|
|
731
|
+
"≠": " \\neq ",
|
|
732
|
+
">": " > ",
|
|
733
|
+
"=": " = ",
|
|
734
|
+
",": ", ", // Dấu phẩy trong tập hợp
|
|
735
|
+
";": ";", // Dấu chấm phẩy không cần khoảng trắng
|
|
736
|
+
"Ω": "\\Omega" // Thêm ký hiệu Omega
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
|
|
716
740
|
}
|
|
717
741
|
|
|
718
742
|
// Math identifier
|
|
719
743
|
function parseElementMi(node) {
|
|
720
744
|
let it = NodeTool.getNodeText(node).trim();
|
|
721
|
-
|
|
745
|
+
|
|
722
746
|
// Handle vectors (e.g. AB', AI)
|
|
723
747
|
if (it.includes("'")) {
|
|
724
748
|
return it; // Return as is to handle in mrow
|
|
725
749
|
}
|
|
726
|
-
|
|
750
|
+
|
|
727
751
|
// Handle subscripts (e.g. n₂)
|
|
728
|
-
if (it.match(/[a-zA-
|
|
752
|
+
if (it.match(/[a-zA-z]\d/)) {
|
|
729
753
|
const base = it[0];
|
|
730
754
|
const sub = it[1];
|
|
731
755
|
return `${base}_{${sub}}`;
|
|
732
756
|
}
|
|
733
|
-
|
|
757
|
+
|
|
734
758
|
it = MathSymbol.parseIdentifier(it);
|
|
735
759
|
return escapeSpecialChars(it);
|
|
736
760
|
}
|
|
@@ -742,33 +766,33 @@ function parseElementMn(node) {
|
|
|
742
766
|
}
|
|
743
767
|
|
|
744
768
|
// Math String
|
|
745
|
-
function parseElementMs(node){
|
|
769
|
+
function parseElementMs(node) {
|
|
746
770
|
const content = NodeTool.getNodeText(node).trimRight();
|
|
747
771
|
const it = escapeSpecialChars(content);
|
|
748
772
|
return ['"', it, '"'].join('');
|
|
749
773
|
}
|
|
750
774
|
|
|
751
775
|
// Math Text
|
|
752
|
-
function parseElementMtext(node){
|
|
776
|
+
function parseElementMtext(node) {
|
|
753
777
|
const content = NodeTool.getNodeText(node);
|
|
754
778
|
const it = escapeSpecialChars(content);
|
|
755
779
|
return `\\text{${it}}`;
|
|
756
780
|
}
|
|
757
781
|
|
|
758
782
|
// Math glyph (image)
|
|
759
|
-
function parseElementMglyph(node){
|
|
783
|
+
function parseElementMglyph(node) {
|
|
760
784
|
const it = ['"', NodeTool.getAttr(node, 'alt', ''), '"'].join('');
|
|
761
785
|
return escapeSpecialChars(it);
|
|
762
786
|
}
|
|
763
787
|
|
|
764
788
|
// TODO need or not
|
|
765
|
-
function parseElementMspace(node){
|
|
789
|
+
function parseElementMspace(node) {
|
|
766
790
|
return '';
|
|
767
791
|
}
|
|
768
792
|
|
|
769
793
|
function escapeSpecialChars(text) {
|
|
770
794
|
const specialChars = /\$|%|_|&|#|\{|\}/g;
|
|
771
|
-
text = text.replace(specialChars, char => `\\${
|
|
795
|
+
text = text.replace(specialChars, char => `\\${char}`);
|
|
772
796
|
return text;
|
|
773
797
|
}
|
|
774
798
|
|
|
@@ -786,26 +810,33 @@ function renderChildren(children) {
|
|
|
786
810
|
const parts = [];
|
|
787
811
|
let lefts = [];
|
|
788
812
|
Array.prototype.forEach.call(children, (node) => {
|
|
789
|
-
if(NodeTool.getNodeName(node) === 'mo'){
|
|
813
|
+
if (NodeTool.getNodeName(node) === 'mo') {
|
|
790
814
|
const op = NodeTool.getNodeText(node).trim();
|
|
791
|
-
if(Brackets.contains(op)){
|
|
815
|
+
if (Brackets.contains(op)) {
|
|
792
816
|
let stretchy = NodeTool.getAttr(node, 'stretchy', 'true');
|
|
793
817
|
stretchy = ['', 'true'].indexOf(stretchy) > -1;
|
|
794
|
-
|
|
795
|
-
if(Brackets.isRight(op)){
|
|
818
|
+
|
|
819
|
+
if (Brackets.isRight(op)) {
|
|
796
820
|
const nearLeft = lefts[lefts.length - 1];
|
|
797
|
-
if(nearLeft){
|
|
798
|
-
if(Brackets.isPair(nearLeft, op)){
|
|
821
|
+
if (nearLeft) {
|
|
822
|
+
if (Brackets.isPair(nearLeft, op)) {
|
|
799
823
|
parts.push(Brackets.parseRight(op, stretchy));
|
|
800
824
|
lefts.pop();
|
|
801
825
|
} else {
|
|
802
826
|
parts.push(`\\right${op}`);
|
|
803
827
|
}
|
|
804
828
|
} else {
|
|
805
|
-
parts.push(
|
|
829
|
+
parts.push(op); // Chỉ thêm dấu ngoặc đóng khi đứng một mình
|
|
806
830
|
}
|
|
807
831
|
} else {
|
|
808
|
-
|
|
832
|
+
// Xử lý dấu ngoặc mở
|
|
833
|
+
if (op === '{' && node.parentNode &&
|
|
834
|
+
Array.from(NodeTool.getChildren(node.parentNode)).some(child =>
|
|
835
|
+
NodeTool.getNodeName(child) === 'mtable')) {
|
|
836
|
+
parts.push('\\left\\{');
|
|
837
|
+
} else {
|
|
838
|
+
parts.push(Brackets.parseLeft(op, stretchy));
|
|
839
|
+
}
|
|
809
840
|
lefts.push(op);
|
|
810
841
|
}
|
|
811
842
|
} else {
|
|
@@ -815,351 +846,444 @@ function renderChildren(children) {
|
|
|
815
846
|
parts.push(parse(node));
|
|
816
847
|
}
|
|
817
848
|
});
|
|
818
|
-
|
|
819
|
-
|
|
849
|
+
|
|
850
|
+
// Chỉ thêm \right. nếu có dấu ngoặc mở chưa được đóng
|
|
851
|
+
if (lefts.length > 0 && !parts.some(p => p.includes('\\right'))) {
|
|
820
852
|
parts.push("\\right.");
|
|
821
853
|
}
|
|
822
854
|
lefts = undefined;
|
|
823
855
|
return parts;
|
|
824
856
|
}
|
|
825
857
|
|
|
826
|
-
|
|
827
858
|
function getRender(node) {
|
|
828
859
|
let render = undefined;
|
|
829
860
|
const nodeName = NodeTool.getNodeName(node);
|
|
830
|
-
|
|
861
|
+
|
|
862
|
+
switch (nodeName) {
|
|
863
|
+
case 'mrow':
|
|
864
|
+
render = function (node, children) {
|
|
865
|
+
const childrenArray = Array.from(children);
|
|
866
|
+
if (childrenArray.length >= 2 &&
|
|
867
|
+
NodeTool.getNodeName(childrenArray[0]) === 'mo' &&
|
|
868
|
+
NodeTool.getNodeName(childrenArray[childrenArray.length - 1]) === 'mo') {
|
|
869
|
+
const firstOp = NodeTool.getNodeText(childrenArray[0]).trim();
|
|
870
|
+
const lastOp = NodeTool.getNodeText(childrenArray[childrenArray.length - 1]).trim();
|
|
871
|
+
|
|
872
|
+
// Xử lý đặc biệt cho dấu ngoặc nhọn chứa mtable
|
|
873
|
+
if (firstOp === '{' && childrenArray.some(child =>
|
|
874
|
+
NodeTool.getNodeName(child) === 'mtable')) {
|
|
875
|
+
const innerContent = childrenArray
|
|
876
|
+
.slice(1, -1)
|
|
877
|
+
.map(child => parse(child))
|
|
878
|
+
.join('');
|
|
879
|
+
return `\\left\\{${innerContent}\\right.`;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// Xử lý cho trường hợp [a;b) và [a,b)
|
|
883
|
+
if (firstOp === '[' && lastOp === ')') {
|
|
884
|
+
const innerContent = childrenArray
|
|
885
|
+
.slice(1, -1)
|
|
886
|
+
.map(child => parse(child))
|
|
887
|
+
.join('');
|
|
888
|
+
return `\\left[${innerContent}\\right)`;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Xử lý ngoặc nhọn bình thường
|
|
892
|
+
if (firstOp === '{' && lastOp === '}') {
|
|
893
|
+
const innerContent = childrenArray
|
|
894
|
+
.slice(1, -1)
|
|
895
|
+
.map(child => parse(child))
|
|
896
|
+
.join('');
|
|
897
|
+
return `\\{${innerContent}\\}`;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Bỏ qua nếu firstOp là rỗng (trường hợp <mo></mo>)
|
|
901
|
+
if (!firstOp && !lastOp) {
|
|
902
|
+
return getRender_joinSeparator("@content")(node, childrenArray);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Xử lý đặc biệt cho dấu ngoặc vuông chứa mtable
|
|
906
|
+
if (firstOp === '[') {
|
|
907
|
+
const innerContent = childrenArray
|
|
908
|
+
.slice(1, -1) // Bỏ dấu ngoặc mở và đóng
|
|
909
|
+
.map(child => {
|
|
910
|
+
const parsed = parse(child);
|
|
911
|
+
// Nếu child là mtable, trả về nội dung đã được định dạng với \begin{array}{l} ... \end{array}
|
|
912
|
+
if (NodeTool.getNodeName(child) === 'mtable') {
|
|
913
|
+
const rows = Array.from(NodeTool.getChildren(child)).map(row => {
|
|
914
|
+
const rowChildren = Array.from(NodeTool.getChildren(row));
|
|
915
|
+
return rowChildren.map(cell => parse(cell)).join('');
|
|
916
|
+
});
|
|
917
|
+
return `\\begin{array}{l} ${rows.join(' \\\\ ')} \\end{array}`;
|
|
918
|
+
}
|
|
919
|
+
// Nếu child là mrow chứa mtable, xử lý tương tự
|
|
920
|
+
if (NodeTool.getNodeName(child) === 'mrow' &&
|
|
921
|
+
Array.from(NodeTool.getChildren(child)).some(c => NodeTool.getNodeName(c) === 'mtable')) {
|
|
922
|
+
const mtableChild = Array.from(NodeTool.getChildren(child)).find(c => NodeTool.getNodeName(c) === 'mtable');
|
|
923
|
+
const rows = Array.from(NodeTool.getChildren(mtableChild)).map(row => {
|
|
924
|
+
const rowChildren = Array.from(NodeTool.getChildren(row));
|
|
925
|
+
return rowChildren.map(cell => parse(cell)).join('');
|
|
926
|
+
});
|
|
927
|
+
return `\\begin{array}{l} ${rows.join(' \\\\ ')} \\end{array}`;
|
|
928
|
+
}
|
|
929
|
+
return parsed;
|
|
930
|
+
})
|
|
931
|
+
.join('');
|
|
932
|
+
|
|
933
|
+
// Giữ nguyên dấu ngoặc vuông lớn
|
|
934
|
+
return `\\left[${innerContent}\\right.`;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (Brackets.isPair(firstOp, lastOp)) {
|
|
938
|
+
const innerContent = childrenArray
|
|
939
|
+
.slice(1, -1)
|
|
940
|
+
.map(child => parse(child))
|
|
941
|
+
.join('');
|
|
942
|
+
return `\\left${firstOp}${innerContent}\\right${lastOp}`;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Giữ nguyên dấu ngoặc nhọn trong MathML
|
|
946
|
+
if (firstOp === '{' || lastOp === '}') {
|
|
947
|
+
const innerContent = childrenArray
|
|
948
|
+
.slice(1, lastOp === '}' ? -1 : undefined)
|
|
949
|
+
.map(child => parse(child))
|
|
950
|
+
.join('');
|
|
951
|
+
return `${firstOp === '{' ? '\\{' : ''}${innerContent}${lastOp === '}' ? '\\}' : ''}`;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return getRender_joinSeparator("@content")(node, childrenArray);
|
|
955
|
+
};
|
|
956
|
+
break;
|
|
957
|
+
|
|
831
958
|
case 'msub':
|
|
832
|
-
render = function(node, children) {
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
const base = parse(
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const sub = children[1];
|
|
959
|
+
render = function (node, children) {
|
|
960
|
+
const childrenArray = Array.from(children);
|
|
961
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
962
|
+
const base = parse(childrenArray[0]) || '';
|
|
963
|
+
const sub = childrenArray[1];
|
|
839
964
|
if (!sub) return base;
|
|
840
965
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
(!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
|
|
966
|
+
if (NodeTool.getNodeName(sub) === 'msub' &&
|
|
967
|
+
sub.firstChild &&
|
|
968
|
+
NodeTool.getNodeName(sub.firstChild) === 'mrow' &&
|
|
969
|
+
(!NodeTool.getNodeText(sub.firstChild) || NodeTool.getNodeText(sub.firstChild).trim() === '')) {
|
|
846
970
|
const lastChild = sub.lastChild;
|
|
847
|
-
|
|
848
|
-
return `${base}_${parse(lastChild)}`;
|
|
971
|
+
return lastChild ? `${base}_${parse(lastChild)}` : base;
|
|
849
972
|
}
|
|
850
|
-
|
|
851
|
-
// Regular subscript
|
|
852
973
|
return `${base}_{${parse(sub)}}`;
|
|
853
974
|
};
|
|
854
975
|
break;
|
|
976
|
+
|
|
855
977
|
case 'msup':
|
|
856
|
-
render =
|
|
978
|
+
render = function (node, children) {
|
|
979
|
+
const childrenArray = Array.from(children);
|
|
980
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
981
|
+
const base = parse(childrenArray[0]) || '';
|
|
982
|
+
const sup = parse(childrenArray[1]) || '';
|
|
983
|
+
return `${base}^{${sup}}`;
|
|
984
|
+
};
|
|
857
985
|
break;
|
|
986
|
+
|
|
858
987
|
case 'msubsup':
|
|
859
|
-
render =
|
|
988
|
+
render = function (node, children) {
|
|
989
|
+
const childrenArray = Array.from(children);
|
|
990
|
+
if (!childrenArray || childrenArray.length < 3) return '';
|
|
991
|
+
const base = parse(childrenArray[0]);
|
|
992
|
+
const sub = parse(childrenArray[1]);
|
|
993
|
+
const sup = parse(childrenArray[2]);
|
|
994
|
+
|
|
995
|
+
const lastChild = childrenArray[0].lastElementChild;
|
|
996
|
+
if (lastChild && NodeTool.getNodeName(lastChild) === 'mo' &&
|
|
997
|
+
NodeTool.getNodeText(lastChild).trim() === '|') {
|
|
998
|
+
const content = Array.from(childrenArray[0].children)
|
|
999
|
+
.slice(0, -1)
|
|
1000
|
+
.map(child => parse(child))
|
|
1001
|
+
.join('');
|
|
1002
|
+
return `\\left.${content}\\right|_{${sub}}^{${sup}}`;
|
|
1003
|
+
}
|
|
1004
|
+
return `${base}_{${sub}}^{${sup}}`;
|
|
1005
|
+
};
|
|
860
1006
|
break;
|
|
1007
|
+
|
|
861
1008
|
case 'mover':
|
|
862
|
-
render =
|
|
1009
|
+
render = function (node, children) {
|
|
1010
|
+
const childrenArray = Array.from(children);
|
|
1011
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
1012
|
+
const base = parse(childrenArray[0]) || '';
|
|
1013
|
+
const over = parse(childrenArray[1]) || '';
|
|
1014
|
+
const overText = NodeTool.getNodeText(childrenArray[1])?.trim() || '';
|
|
1015
|
+
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1016
|
+
|
|
1017
|
+
if (overText === "→" && isAccent) return `\\vec{${base}}`;
|
|
1018
|
+
if (overText === "¯" && isAccent) return `\\overline{${base}}`;
|
|
1019
|
+
if (overText === "^" && isAccent) return `\\hat{${base}}`;
|
|
1020
|
+
return `\\overset{${over}}{${base}}`;
|
|
1021
|
+
};
|
|
863
1022
|
break;
|
|
1023
|
+
|
|
864
1024
|
case 'munder':
|
|
865
|
-
render =
|
|
1025
|
+
render = function (node, children) {
|
|
1026
|
+
const childrenArray = Array.from(children);
|
|
1027
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
1028
|
+
const base = parse(childrenArray[0]) || '';
|
|
1029
|
+
const under = parse(childrenArray[1]) || '';
|
|
1030
|
+
const isUnderAccent = NodeTool.getAttr(node, "accentunder", "false") === "true";
|
|
1031
|
+
|
|
1032
|
+
if (base === "∫") return `\\int_{${under}}`;
|
|
1033
|
+
return `\\underset{${under}}{${base}}`;
|
|
1034
|
+
};
|
|
866
1035
|
break;
|
|
1036
|
+
|
|
867
1037
|
case 'munderover':
|
|
868
|
-
render =
|
|
1038
|
+
render = function (node, children) {
|
|
1039
|
+
const childrenArray = Array.from(children);
|
|
1040
|
+
if (!childrenArray || childrenArray.length < 3) return '';
|
|
1041
|
+
const base = parse(childrenArray[0]);
|
|
1042
|
+
const under = parse(childrenArray[1]);
|
|
1043
|
+
const over = parse(childrenArray[2]);
|
|
1044
|
+
const baseText = NodeTool.getNodeText(childrenArray[0]).trim();
|
|
1045
|
+
|
|
1046
|
+
if (baseText === '∫') return `\\int_{${under}}^{${over}}`;
|
|
1047
|
+
if (baseText === '∑') return `\\sum_{${under}}^{${over}}`;
|
|
1048
|
+
if (baseText === '∏') return `\\prod_{${under}}^{${over}}`;
|
|
1049
|
+
if (baseText === '|') return `\\big|_{${under}}^{${over}}`;
|
|
1050
|
+
return `${base}_{${under}}^{${over}}`;
|
|
1051
|
+
};
|
|
869
1052
|
break;
|
|
1053
|
+
|
|
870
1054
|
case 'mmultiscripts':
|
|
871
|
-
render =
|
|
1055
|
+
render = function (node, children) {
|
|
1056
|
+
const childrenArray = Array.from(children);
|
|
1057
|
+
if (!childrenArray || childrenArray.length < 1) return '';
|
|
1058
|
+
const base = parse(childrenArray[0]);
|
|
1059
|
+
let prescripts = '';
|
|
1060
|
+
let postscripts = '';
|
|
1061
|
+
let i = 1;
|
|
1062
|
+
|
|
1063
|
+
while (i < childrenArray.length) {
|
|
1064
|
+
if (NodeTool.getNodeName(childrenArray[i]) === 'mprescripts') {
|
|
1065
|
+
i++;
|
|
1066
|
+
if (i + 1 < childrenArray.length) {
|
|
1067
|
+
prescripts = `_{${parse(childrenArray[i])}}^{${parse(childrenArray[i + 1])}}`;
|
|
1068
|
+
i += 2;
|
|
1069
|
+
}
|
|
1070
|
+
} else {
|
|
1071
|
+
if (i + 1 < childrenArray.length) {
|
|
1072
|
+
postscripts += `_{${parse(childrenArray[i])}}^{${parse(childrenArray[i + 1])}}`;
|
|
1073
|
+
i += 2;
|
|
1074
|
+
} else break;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
return `${base}${prescripts}${postscripts}`;
|
|
1078
|
+
};
|
|
872
1079
|
break;
|
|
1080
|
+
|
|
1081
|
+
case 'mlongdiv':
|
|
1082
|
+
render = function (node, children) {
|
|
1083
|
+
const childrenArray = Array.from(children);
|
|
1084
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
1085
|
+
return `\\longdiv{${parse(childrenArray[0])}}{${parse(childrenArray[1])}}`;
|
|
1086
|
+
};
|
|
1087
|
+
break;
|
|
1088
|
+
|
|
873
1089
|
case 'mroot':
|
|
874
|
-
render =
|
|
1090
|
+
render = function (node, children) {
|
|
1091
|
+
const childrenArray = Array.from(children);
|
|
1092
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
1093
|
+
const base = parse(childrenArray[0]);
|
|
1094
|
+
const index = parse(childrenArray[1]);
|
|
1095
|
+
return `\\sqrt[${index}]{${base}}`;
|
|
1096
|
+
};
|
|
875
1097
|
break;
|
|
1098
|
+
|
|
876
1099
|
case 'msqrt':
|
|
877
|
-
render =
|
|
1100
|
+
render = function (node, children) {
|
|
1101
|
+
const childrenArray = Array.from(children);
|
|
1102
|
+
const content = renderChildren(childrenArray).join('');
|
|
1103
|
+
return `\\sqrt{${content}}`;
|
|
1104
|
+
};
|
|
878
1105
|
break;
|
|
1106
|
+
|
|
879
1107
|
case 'mtable':
|
|
880
|
-
render =
|
|
1108
|
+
render = function (node, children) {
|
|
1109
|
+
const childrenArray = Array.from(children);
|
|
1110
|
+
|
|
1111
|
+
// Kiểm tra xem mtable có phải là mtable con không
|
|
1112
|
+
let isNestedTable = false;
|
|
1113
|
+
let parent = node.parentNode;
|
|
1114
|
+
while (parent) {
|
|
1115
|
+
if (NodeTool.getNodeName(parent) === 'mtable') {
|
|
1116
|
+
isNestedTable = true;
|
|
1117
|
+
break;
|
|
1118
|
+
}
|
|
1119
|
+
parent = parent.parentNode;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Xử lý mỗi mtr như một hàng
|
|
1123
|
+
const rows = childrenArray.map(row => {
|
|
1124
|
+
const rowChildren = Array.from(NodeTool.getChildren(row));
|
|
1125
|
+
return rowChildren.map(cell => parse(cell)).join('');
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
// Nếu là mtable con, chỉ trả về các hàng mà không bao bọc trong \begin{array}...\end{array}
|
|
1129
|
+
if (isNestedTable) {
|
|
1130
|
+
return rows.join(' \\\\ ');
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// Nếu mtable nằm trong mrow với dấu ngoặc vuông, sẽ được xử lý ở mrow
|
|
1134
|
+
let isInsideSquareBrackets = false;
|
|
1135
|
+
parent = node.parentNode;
|
|
1136
|
+
while (parent) {
|
|
1137
|
+
if (NodeTool.getNodeName(parent) === 'mrow') {
|
|
1138
|
+
const childrenOfParent = Array.from(NodeTool.getChildren(parent));
|
|
1139
|
+
if (childrenOfParent.length >= 2 &&
|
|
1140
|
+
NodeTool.getNodeName(childrenOfParent[0]) === 'mo' &&
|
|
1141
|
+
NodeTool.getNodeText(childrenOfParent[0]).trim() === '[') {
|
|
1142
|
+
isInsideSquareBrackets = true;
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
parent = parent.parentNode;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
if (isInsideSquareBrackets) {
|
|
1150
|
+
return rows.join(' \\\\ ');
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// Nếu là mtable chính, bao bọc trong \begin{array}...\end{array}
|
|
1154
|
+
const arrayContent = rows.join(' \\\\ ');
|
|
1155
|
+
return `\\begin{array}{l} ${arrayContent} \\end{array}`;
|
|
1156
|
+
};
|
|
881
1157
|
break;
|
|
1158
|
+
|
|
882
1159
|
case 'mtr':
|
|
883
|
-
render = getRender_joinSeparator("@content
|
|
1160
|
+
render = getRender_joinSeparator("@content", " & ");
|
|
884
1161
|
break;
|
|
1162
|
+
|
|
885
1163
|
case 'mtd':
|
|
886
1164
|
render = getRender_joinSeparator("@content");
|
|
887
1165
|
break;
|
|
1166
|
+
|
|
888
1167
|
case 'mfrac':
|
|
889
|
-
render =
|
|
1168
|
+
render = function (node, children) {
|
|
1169
|
+
const childrenArray = Array.from(children);
|
|
1170
|
+
if (!childrenArray || childrenArray.length < 2) return '';
|
|
1171
|
+
const num = parse(childrenArray[0]);
|
|
1172
|
+
const den = parse(childrenArray[1]);
|
|
1173
|
+
const linethickness = NodeTool.getAttr(node, 'linethickness', 'medium');
|
|
1174
|
+
if (linethickness === '0') return `\\binom{${num}}{${den}}`;
|
|
1175
|
+
return `\\frac{${num}}{${den}}`;
|
|
1176
|
+
};
|
|
890
1177
|
break;
|
|
1178
|
+
|
|
891
1179
|
case 'mfenced':
|
|
892
|
-
render =
|
|
1180
|
+
render = function (node, children) {
|
|
1181
|
+
const childrenArray = Array.from(children);
|
|
1182
|
+
const open = NodeTool.getAttr(node, 'open', '(');
|
|
1183
|
+
const close = NodeTool.getAttr(node, 'close', ')');
|
|
1184
|
+
const separators = NodeTool.getAttr(node, 'separators', ',').split('');
|
|
1185
|
+
|
|
1186
|
+
// Xử lý đặc biệt cho mfenced chứa mtable
|
|
1187
|
+
if (open === '{' && !close) {
|
|
1188
|
+
// Kiểm tra xem có mtable trong cấu trúc không
|
|
1189
|
+
const hasMtable = childrenArray.some(child => {
|
|
1190
|
+
// Kiểm tra trực tiếp mtable
|
|
1191
|
+
if (NodeTool.getNodeName(child) === 'mtable') return true;
|
|
1192
|
+
// Kiểm tra mtable trong mrow
|
|
1193
|
+
if (NodeTool.getNodeName(child) === 'mrow') {
|
|
1194
|
+
return Array.from(NodeTool.getChildren(child)).some(
|
|
1195
|
+
grandChild => NodeTool.getNodeName(grandChild) === 'mtable'
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
return false;
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
if (hasMtable) {
|
|
1202
|
+
const content = childrenArray.map(child => parse(child)).join('');
|
|
1203
|
+
return `\\left\\{${content}\\right.`;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// Xử lý cho trường hợp [a;b)
|
|
1208
|
+
if (open === '[' && close === ')') {
|
|
1209
|
+
const parts = [];
|
|
1210
|
+
childrenArray.forEach((child, index) => {
|
|
1211
|
+
parts.push(parse(child));
|
|
1212
|
+
if (index < childrenArray.length - 1 && separators[index % separators.length]) {
|
|
1213
|
+
parts.push(separators[index % separators.length]);
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
return `\\left[${parts.join('')}\\right)`;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// Giữ nguyên xử lý cho các trường hợp khác
|
|
1220
|
+
const parts = [];
|
|
1221
|
+
childrenArray.forEach((child, index) => {
|
|
1222
|
+
parts.push(parse(child));
|
|
1223
|
+
if (index < childrenArray.length - 1 && separators[index % separators.length]) {
|
|
1224
|
+
parts.push(separators[index % separators.length]);
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
const content = parts.join('');
|
|
1228
|
+
|
|
1229
|
+
if (open === '{' && close === '}') return `\\{${content}\\}`;
|
|
1230
|
+
if (open === '|' && close === '|') return `\\left|${content}\\right|`;
|
|
1231
|
+
if (!close) return `\\left${open}${content}\\right.`;
|
|
1232
|
+
if (!open) return `${content}\\right${close}`;
|
|
1233
|
+
return `\\left${open}${content}\\right${close}`;
|
|
1234
|
+
};
|
|
1235
|
+
break;
|
|
1236
|
+
|
|
1237
|
+
case 'menclose':
|
|
1238
|
+
render = function (node, children) {
|
|
1239
|
+
const childrenArray = Array.from(children);
|
|
1240
|
+
const notation = NodeTool.getAttr(node, 'notation', 'longdiv');
|
|
1241
|
+
const content = renderChildren(childrenArray).join('');
|
|
1242
|
+
switch (notation) {
|
|
1243
|
+
case 'box': return `\\boxed{${content}}`;
|
|
1244
|
+
case 'circle': return `\\enclose{circle}{${content}}`;
|
|
1245
|
+
case 'roundedbox': return `\\fbox{${content}}`;
|
|
1246
|
+
default: return content;
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
893
1249
|
break;
|
|
1250
|
+
|
|
894
1251
|
case 'mi':
|
|
895
1252
|
case 'mn':
|
|
896
1253
|
case 'mo':
|
|
897
1254
|
case 'ms':
|
|
898
1255
|
case 'mtext':
|
|
899
|
-
// they may contains <mglyph>
|
|
900
1256
|
render = getRender_joinSeparator("@content");
|
|
901
1257
|
break;
|
|
1258
|
+
|
|
902
1259
|
case 'mphantom':
|
|
903
|
-
render =
|
|
1260
|
+
render = function (node, children) {
|
|
1261
|
+
const childrenArray = Array.from(children);
|
|
1262
|
+
const content = renderChildren(childrenArray).join('');
|
|
1263
|
+
return `\\phantom{${content}}`;
|
|
1264
|
+
};
|
|
1265
|
+
break;
|
|
1266
|
+
|
|
1267
|
+
case 'mstyle':
|
|
1268
|
+
render = function (node, children) {
|
|
1269
|
+
const childrenArray = Array.from(children);
|
|
1270
|
+
const mathsize = NodeTool.getAttr(node, 'mathsize', 'normal');
|
|
1271
|
+
const content = renderChildren(childrenArray).join('');
|
|
1272
|
+
switch (mathsize) {
|
|
1273
|
+
case 'big': return `\\large{${content}}`;
|
|
1274
|
+
case 'small': return `\\small{${content}}`;
|
|
1275
|
+
default: return content;
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
904
1278
|
break;
|
|
1279
|
+
|
|
905
1280
|
default:
|
|
906
|
-
// math, mstyle, mrow
|
|
907
1281
|
render = getRender_joinSeparator("@content");
|
|
908
1282
|
break;
|
|
909
1283
|
}
|
|
910
1284
|
return render;
|
|
911
1285
|
}
|
|
912
1286
|
|
|
913
|
-
function renderTable(node, children) {
|
|
914
|
-
const template = "\\begin{array}{l}@content\\end{array}";
|
|
915
|
-
// Remove extra backslash and add proper spacing
|
|
916
|
-
const render = getRender_joinSeparator(template, " \\\\ ");
|
|
917
|
-
return render(node, children);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
function renderMover(node, children) {
|
|
921
|
-
// Check if children exists and has enough elements
|
|
922
|
-
if (!children || children.length < 2) return '';
|
|
923
|
-
|
|
924
|
-
const baseNode = children[0];
|
|
925
|
-
const overNode = children[1];
|
|
926
|
-
|
|
927
|
-
// Check if nodes exist before accessing
|
|
928
|
-
if (!baseNode || !overNode) return '';
|
|
929
|
-
|
|
930
|
-
const overText = NodeTool.getNodeText(overNode)?.trim() || '';
|
|
931
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
932
|
-
|
|
933
|
-
// Handle vector notation
|
|
934
|
-
if (overText === "→" && isAccent) {
|
|
935
|
-
return `\\vec{${parse(baseNode)}}`;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
return parse(baseNode);
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
function renderMfenced(node, children) {
|
|
942
|
-
const [open, close, separatorsStr] = [
|
|
943
|
-
NodeTool.getAttr(node, 'open', '('),
|
|
944
|
-
NodeTool.getAttr(node, 'close', ')'),
|
|
945
|
-
NodeTool.getAttr(node, 'separators', ',')
|
|
946
|
-
];
|
|
947
|
-
|
|
948
|
-
const parts = renderChildren(children);
|
|
949
|
-
const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
|
|
950
|
-
|
|
951
|
-
// Handle nested absolute value cases
|
|
952
|
-
if (open === '|' && close === '') {
|
|
953
|
-
return `\\left|${content}`;
|
|
954
|
-
}
|
|
955
|
-
if (open === '' && close === '|') {
|
|
956
|
-
return `${content}\\right|`;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// Handle system of equations with curly brace
|
|
960
|
-
if (open === '{') {
|
|
961
|
-
return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Handle regular absolute value notation
|
|
965
|
-
if (open === '|' && close === '|') {
|
|
966
|
-
return `\\left|${content}\\right|`;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// Regular cases with proper closing
|
|
970
|
-
return `\\left${open}${content}\\right${close || '.'}`;
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
function renderMfrac(node, children){
|
|
974
|
-
const [linethickness, bevelled] = [
|
|
975
|
-
NodeTool.getAttr(node, 'linethickness', 'medium'),
|
|
976
|
-
NodeTool.getAttr(node, 'bevelled', 'false')
|
|
977
|
-
];
|
|
978
|
-
|
|
979
|
-
let render = null;
|
|
980
|
-
if(bevelled === 'true') {
|
|
981
|
-
render = getRender_default("{}^{@1}/_{@2}");
|
|
982
|
-
} else if(['0', '0px'].indexOf(linethickness) > -1) {
|
|
983
|
-
const [prevNode, nextNode] = [
|
|
984
|
-
NodeTool.getPrevNode(node),
|
|
985
|
-
NodeTool.getNextNode(node)
|
|
986
|
-
];
|
|
987
|
-
if((prevNode && NodeTool.getNodeText(prevNode).trim() === '(') &&
|
|
988
|
-
(nextNode && NodeTool.getNodeText(nextNode).trim() === ')')
|
|
989
|
-
) {
|
|
990
|
-
render = getRender_default("\\binom{@1}{@2}");
|
|
991
|
-
} else {
|
|
992
|
-
render = getRender_default("\\frac{@1}{@2}");
|
|
993
|
-
}
|
|
994
|
-
} else {
|
|
995
|
-
render = getRender_default("\\frac{@1}{@2}");
|
|
996
|
-
}
|
|
997
|
-
return render(node, children);
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
function renderMmultiscripts(node, children) {
|
|
1001
|
-
if(children.length === 0) { return '' }
|
|
1002
|
-
let sepIndex = -1;
|
|
1003
|
-
let mprescriptsNode = null;
|
|
1004
|
-
Array.prototype.forEach.call(children, (node) => {
|
|
1005
|
-
if(NodeTool.getNodeName(node) === 'mprescripts'){
|
|
1006
|
-
mprescriptsNode = node;
|
|
1007
|
-
}
|
|
1008
|
-
});
|
|
1009
|
-
if(mprescriptsNode) {
|
|
1010
|
-
sepIndex = Array.prototype.indexOf.call(children, mprescriptsNode);
|
|
1011
|
-
}
|
|
1012
|
-
const parts = renderChildren(children);
|
|
1013
|
-
|
|
1014
|
-
const splitArray = (arr, index) => {
|
|
1015
|
-
return [arr.slice(0, index), arr.slice(index + 1, arr.length)]
|
|
1016
|
-
};
|
|
1017
|
-
const renderScripts = (items) => {
|
|
1018
|
-
if(items.length > 0) {
|
|
1019
|
-
const subs = [];
|
|
1020
|
-
const sups = [];
|
|
1021
|
-
items.forEach((item, index) => {
|
|
1022
|
-
// one render as sub script, one as super script
|
|
1023
|
-
if((index + 1) % 2 === 0){
|
|
1024
|
-
sups.push(item);
|
|
1025
|
-
} else {
|
|
1026
|
-
subs.push(item);
|
|
1027
|
-
}
|
|
1028
|
-
});
|
|
1029
|
-
return [
|
|
1030
|
-
(subs.length > 0 ? `_{${subs.join(' ')}}` : ''),
|
|
1031
|
-
(sups.length > 0 ? `^{${sups.join(' ')}}` : '')
|
|
1032
|
-
].join('');
|
|
1033
|
-
} else {
|
|
1034
|
-
return '';
|
|
1035
|
-
}
|
|
1036
|
-
};
|
|
1037
|
-
const base = parts.shift();
|
|
1038
|
-
let prevScripts = [];
|
|
1039
|
-
let backScripts = [];
|
|
1040
|
-
if(sepIndex === -1){
|
|
1041
|
-
backScripts = parts;
|
|
1042
|
-
} else {
|
|
1043
|
-
[backScripts, prevScripts] = splitArray(parts, sepIndex - 1);
|
|
1044
|
-
}
|
|
1045
|
-
return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
function renderMunder(node, children){
|
|
1049
|
-
const nodes = flattenNodeTreeByNodeName(node, 'munder');
|
|
1050
|
-
let result = undefined;
|
|
1051
|
-
for(let i = 0; i < nodes.length - 1; i++) {
|
|
1052
|
-
if(!result){ result = parse(nodes[i]); }
|
|
1053
|
-
|
|
1054
|
-
const underNode = nodes[i + 1];
|
|
1055
|
-
const underText = NodeTool.getNodeText(underNode).trim();
|
|
1056
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1057
|
-
|
|
1058
|
-
// Special handling for arrow accent
|
|
1059
|
-
if (underText === "→" && isAccent) {
|
|
1060
|
-
return `\\underset{${result}}{\\rightarrow}`;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
const under = parse(underNode);
|
|
1064
|
-
const template = getMatchValueByChar({
|
|
1065
|
-
decimals: MathSymbol.underScript.decimals,
|
|
1066
|
-
values: MathSymbol.underScript.templates,
|
|
1067
|
-
judgeChar: underText,
|
|
1068
|
-
defaultValue: "@1_{@2}"
|
|
1069
|
-
});
|
|
1070
|
-
result = renderTemplate(template.replace("@v", "@1"), [result, under]);
|
|
1071
|
-
}
|
|
1072
|
-
return result;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
function renderMunderover(node, children){
|
|
1076
|
-
const nodes = flattenNodeTreeByNodeName(node, 'munderover');
|
|
1077
|
-
let result = undefined;
|
|
1078
|
-
for(let i = 0; i < nodes.length - 1; i++) {
|
|
1079
|
-
if(!result){ result = parse(nodes[i]); }
|
|
1080
|
-
|
|
1081
|
-
const overNode = nodes[i + 1];
|
|
1082
|
-
const overText = NodeTool.getNodeText(overNode).trim();
|
|
1083
|
-
const underNode = nodes[i + 2];
|
|
1084
|
-
const underText = NodeTool.getNodeText(underNode).trim();
|
|
1085
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1086
|
-
|
|
1087
|
-
// Special handling for arrow accent
|
|
1088
|
-
if (overText === "→" && isAccent) {
|
|
1089
|
-
return `\\overset{${result}}{\\underset{${underText}}{\\rightarrow}}`;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
const over = parse(overNode);
|
|
1093
|
-
const under = parse(underNode);
|
|
1094
|
-
const template = getMatchValueByChar({
|
|
1095
|
-
decimals: MathSymbol.underoverScript.decimals,
|
|
1096
|
-
values: MathSymbol.underoverScript.templates,
|
|
1097
|
-
judgeChar: overText,
|
|
1098
|
-
defaultValue: "@1_{@2}^{@3}"
|
|
1099
|
-
});
|
|
1100
|
-
result = renderTemplate(template.replace("@v", "@1"), [over, under]);
|
|
1101
|
-
}
|
|
1102
|
-
return result;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
function renderMphantom(node, children){
|
|
1106
|
-
const nodes = flattenNodeTreeByNodeName(node, 'mphantom');
|
|
1107
|
-
let result = undefined;
|
|
1108
|
-
for(let i = 0; i < nodes.length - 1; i++) {
|
|
1109
|
-
if(!result){ result = parse(nodes[i]); }
|
|
1110
|
-
|
|
1111
|
-
const phantomNode = nodes[i + 1];
|
|
1112
|
-
const phantomText = NodeTool.getNodeText(phantomNode).trim();
|
|
1113
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1114
|
-
|
|
1115
|
-
// Special handling for arrow accent
|
|
1116
|
-
if (phantomText === "→" && isAccent) {
|
|
1117
|
-
return `\\overrightarrow{${result}}`;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
const phantom = parse(phantomNode);
|
|
1121
|
-
const template = getMatchValueByChar({
|
|
1122
|
-
decimals: MathSymbol.phantomScript.decimals,
|
|
1123
|
-
values: MathSymbol.phantomScript.templates,
|
|
1124
|
-
judgeChar: phantomText,
|
|
1125
|
-
defaultValue: "@1^{@2}"
|
|
1126
|
-
});
|
|
1127
|
-
result = renderTemplate(template.replace("@v", "@1"), [result, phantom]);
|
|
1128
|
-
}
|
|
1129
|
-
return result;
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
function renderTemplate(template, args) {
|
|
1133
|
-
return template.replace(/@(\d+)/g, (match, index) => {
|
|
1134
|
-
const arg = args[index - 1];
|
|
1135
|
-
return arg || match;
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
function getMatchValueByChar(options) {
|
|
1140
|
-
const { decimals, values, judgeChar, defaultValue } = options;
|
|
1141
|
-
const match = values.find(value => value.judgeChar === judgeChar);
|
|
1142
|
-
return match || defaultValue;
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
function flattenNodeTreeByNodeName(node, nodeName) {
|
|
1146
|
-
const nodes = [];
|
|
1147
|
-
const children = NodeTool.getChildren(node);
|
|
1148
|
-
if (children && children.length > 0) {
|
|
1149
|
-
// Convert HTMLCollection to Array before using forEach
|
|
1150
|
-
Array.from(children).forEach(child => {
|
|
1151
|
-
if (NodeTool.getNodeName(child) === nodeName) {
|
|
1152
|
-
nodes.push(child);
|
|
1153
|
-
} else {
|
|
1154
|
-
// Recursively search in child nodes
|
|
1155
|
-
const childNodes = flattenNodeTreeByNodeName(child, nodeName);
|
|
1156
|
-
nodes.push(...childNodes);
|
|
1157
|
-
}
|
|
1158
|
-
});
|
|
1159
|
-
}
|
|
1160
|
-
return nodes;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
1287
|
// Export the convert function
|
|
1164
1288
|
var mathml2latex = {
|
|
1165
1289
|
convert: convert
|