ed-mathml2tex 0.0.9 → 0.1.1
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 +484 -359
- package/lib/mathml2latex.browser.es.js +484 -359
- package/lib/mathml2latex.browser.umd.js +484 -359
- package/lib/mathml2latex.cjs.js +484 -359
- package/lib/mathml2latex.es.js +484 -359
- package/lib/mathml2latex.umd.js +484 -359
- 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,31 +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
|
-
|
|
818
|
+
|
|
819
|
+
if (Brackets.isRight(op)) {
|
|
795
820
|
const nearLeft = lefts[lefts.length - 1];
|
|
796
|
-
if(nearLeft){
|
|
797
|
-
if(Brackets.isPair(nearLeft, op)){
|
|
821
|
+
if (nearLeft) {
|
|
822
|
+
if (Brackets.isPair(nearLeft, op)) {
|
|
798
823
|
parts.push(Brackets.parseRight(op, stretchy));
|
|
799
824
|
lefts.pop();
|
|
800
825
|
} else {
|
|
801
|
-
|
|
802
|
-
parts.push(Brackets.parseLeft(op, stretchy));
|
|
803
|
-
lefts.push(op);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
}else {
|
|
807
|
-
if(Brackets.isLeft(op)) {
|
|
808
|
-
parts.push(Brackets.parseLeft(op, stretchy));
|
|
809
|
-
lefts.push(op);
|
|
826
|
+
parts.push(`\\right${op}`);
|
|
810
827
|
}
|
|
828
|
+
} else {
|
|
829
|
+
parts.push(op); // Chỉ thêm dấu ngoặc đóng khi đứng một mình
|
|
811
830
|
}
|
|
812
831
|
} else {
|
|
813
|
-
|
|
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
|
+
}
|
|
814
840
|
lefts.push(op);
|
|
815
841
|
}
|
|
816
842
|
} else {
|
|
@@ -820,345 +846,444 @@ function renderChildren(children) {
|
|
|
820
846
|
parts.push(parse(node));
|
|
821
847
|
}
|
|
822
848
|
});
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
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'))) {
|
|
852
|
+
parts.push("\\right.");
|
|
827
853
|
}
|
|
828
854
|
lefts = undefined;
|
|
829
855
|
return parts;
|
|
830
856
|
}
|
|
831
857
|
|
|
832
|
-
|
|
833
858
|
function getRender(node) {
|
|
834
859
|
let render = undefined;
|
|
835
860
|
const nodeName = NodeTool.getNodeName(node);
|
|
836
|
-
|
|
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
|
+
|
|
837
958
|
case 'msub':
|
|
838
|
-
render = function(node, children) {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const base = parse(
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
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];
|
|
845
964
|
if (!sub) return base;
|
|
846
965
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
(!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() === '')) {
|
|
852
970
|
const lastChild = sub.lastChild;
|
|
853
|
-
|
|
854
|
-
return `${base}_${parse(lastChild)}`;
|
|
971
|
+
return lastChild ? `${base}_${parse(lastChild)}` : base;
|
|
855
972
|
}
|
|
856
|
-
|
|
857
|
-
// Regular subscript
|
|
858
973
|
return `${base}_{${parse(sub)}}`;
|
|
859
974
|
};
|
|
860
975
|
break;
|
|
976
|
+
|
|
861
977
|
case 'msup':
|
|
862
|
-
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
|
+
};
|
|
863
985
|
break;
|
|
986
|
+
|
|
864
987
|
case 'msubsup':
|
|
865
|
-
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
|
+
};
|
|
866
1006
|
break;
|
|
1007
|
+
|
|
867
1008
|
case 'mover':
|
|
868
|
-
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
|
+
};
|
|
869
1022
|
break;
|
|
1023
|
+
|
|
870
1024
|
case 'munder':
|
|
871
|
-
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
|
+
};
|
|
872
1035
|
break;
|
|
1036
|
+
|
|
873
1037
|
case 'munderover':
|
|
874
|
-
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
|
+
};
|
|
875
1052
|
break;
|
|
1053
|
+
|
|
876
1054
|
case 'mmultiscripts':
|
|
877
|
-
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
|
+
};
|
|
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
|
+
};
|
|
878
1087
|
break;
|
|
1088
|
+
|
|
879
1089
|
case 'mroot':
|
|
880
|
-
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
|
+
};
|
|
881
1097
|
break;
|
|
1098
|
+
|
|
882
1099
|
case 'msqrt':
|
|
883
|
-
render =
|
|
1100
|
+
render = function (node, children) {
|
|
1101
|
+
const childrenArray = Array.from(children);
|
|
1102
|
+
const content = renderChildren(childrenArray).join('');
|
|
1103
|
+
return `\\sqrt{${content}}`;
|
|
1104
|
+
};
|
|
884
1105
|
break;
|
|
1106
|
+
|
|
885
1107
|
case 'mtable':
|
|
886
|
-
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
|
+
};
|
|
887
1157
|
break;
|
|
1158
|
+
|
|
888
1159
|
case 'mtr':
|
|
889
|
-
render = getRender_joinSeparator("@content
|
|
1160
|
+
render = getRender_joinSeparator("@content", " & ");
|
|
890
1161
|
break;
|
|
1162
|
+
|
|
891
1163
|
case 'mtd':
|
|
892
1164
|
render = getRender_joinSeparator("@content");
|
|
893
1165
|
break;
|
|
1166
|
+
|
|
894
1167
|
case 'mfrac':
|
|
895
|
-
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
|
+
};
|
|
896
1177
|
break;
|
|
1178
|
+
|
|
897
1179
|
case 'mfenced':
|
|
898
|
-
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
|
+
};
|
|
899
1249
|
break;
|
|
1250
|
+
|
|
900
1251
|
case 'mi':
|
|
901
1252
|
case 'mn':
|
|
902
1253
|
case 'mo':
|
|
903
1254
|
case 'ms':
|
|
904
1255
|
case 'mtext':
|
|
905
|
-
// they may contains <mglyph>
|
|
906
1256
|
render = getRender_joinSeparator("@content");
|
|
907
1257
|
break;
|
|
1258
|
+
|
|
908
1259
|
case 'mphantom':
|
|
909
|
-
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
|
+
};
|
|
910
1278
|
break;
|
|
1279
|
+
|
|
911
1280
|
default:
|
|
912
|
-
// math, mstyle, mrow
|
|
913
1281
|
render = getRender_joinSeparator("@content");
|
|
914
1282
|
break;
|
|
915
1283
|
}
|
|
916
1284
|
return render;
|
|
917
1285
|
}
|
|
918
1286
|
|
|
919
|
-
function renderTable(node, children) {
|
|
920
|
-
const template = "\\begin{array}{l}@content\\end{array}";
|
|
921
|
-
// Remove extra backslash and add proper spacing
|
|
922
|
-
const render = getRender_joinSeparator(template, " \\\\ ");
|
|
923
|
-
return render(node, children);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
function renderMover(node, children) {
|
|
927
|
-
// Check if children exists and has enough elements
|
|
928
|
-
if (!children || children.length < 2) return '';
|
|
929
|
-
|
|
930
|
-
const baseNode = children[0];
|
|
931
|
-
const overNode = children[1];
|
|
932
|
-
|
|
933
|
-
// Check if nodes exist before accessing
|
|
934
|
-
if (!baseNode || !overNode) return '';
|
|
935
|
-
|
|
936
|
-
const overText = NodeTool.getNodeText(overNode)?.trim() || '';
|
|
937
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
938
|
-
|
|
939
|
-
// Handle vector notation
|
|
940
|
-
if (overText === "→" && isAccent) {
|
|
941
|
-
return `\\vec{${parse(baseNode)}}`;
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
return parse(baseNode);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
function renderMfenced(node, children) {
|
|
948
|
-
const [open, close, separatorsStr] = [
|
|
949
|
-
NodeTool.getAttr(node, 'open', '('),
|
|
950
|
-
NodeTool.getAttr(node, 'close', ')'),
|
|
951
|
-
NodeTool.getAttr(node, 'separators', ',')
|
|
952
|
-
];
|
|
953
|
-
|
|
954
|
-
const parts = renderChildren(children);
|
|
955
|
-
const content = parts.join(separatorsStr === '|' ? ',' : separatorsStr).trim();
|
|
956
|
-
|
|
957
|
-
// Handle system of equations with curly brace
|
|
958
|
-
if (open === '{') {
|
|
959
|
-
// If no closing brace specified, use \right.
|
|
960
|
-
return `\\left\\{${content}${close ? '\\right\\}' : '\\right.'}`;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
// Handle absolute value notation
|
|
964
|
-
if (open === '|' && (close === '|' || close === '')) {
|
|
965
|
-
return `\\left|${content}\\right|`;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
// Regular cases with proper closing
|
|
969
|
-
return `\\left${open}${content}\\right${close || '.'}`;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
function renderMfrac(node, children){
|
|
973
|
-
const [linethickness, bevelled] = [
|
|
974
|
-
NodeTool.getAttr(node, 'linethickness', 'medium'),
|
|
975
|
-
NodeTool.getAttr(node, 'bevelled', 'false')
|
|
976
|
-
];
|
|
977
|
-
|
|
978
|
-
let render = null;
|
|
979
|
-
if(bevelled === 'true') {
|
|
980
|
-
render = getRender_default("{}^{@1}/_{@2}");
|
|
981
|
-
} else if(['0', '0px'].indexOf(linethickness) > -1) {
|
|
982
|
-
const [prevNode, nextNode] = [
|
|
983
|
-
NodeTool.getPrevNode(node),
|
|
984
|
-
NodeTool.getNextNode(node)
|
|
985
|
-
];
|
|
986
|
-
if((prevNode && NodeTool.getNodeText(prevNode).trim() === '(') &&
|
|
987
|
-
(nextNode && NodeTool.getNodeText(nextNode).trim() === ')')
|
|
988
|
-
) {
|
|
989
|
-
render = getRender_default("\\binom{@1}{@2}");
|
|
990
|
-
} else {
|
|
991
|
-
render = getRender_default("\\frac{@1}{@2}");
|
|
992
|
-
}
|
|
993
|
-
} else {
|
|
994
|
-
render = getRender_default("\\frac{@1}{@2}");
|
|
995
|
-
}
|
|
996
|
-
return render(node, children);
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
function renderMmultiscripts(node, children) {
|
|
1000
|
-
if(children.length === 0) { return '' }
|
|
1001
|
-
let sepIndex = -1;
|
|
1002
|
-
let mprescriptsNode = null;
|
|
1003
|
-
Array.prototype.forEach.call(children, (node) => {
|
|
1004
|
-
if(NodeTool.getNodeName(node) === 'mprescripts'){
|
|
1005
|
-
mprescriptsNode = node;
|
|
1006
|
-
}
|
|
1007
|
-
});
|
|
1008
|
-
if(mprescriptsNode) {
|
|
1009
|
-
sepIndex = Array.prototype.indexOf.call(children, mprescriptsNode);
|
|
1010
|
-
}
|
|
1011
|
-
const parts = renderChildren(children);
|
|
1012
|
-
|
|
1013
|
-
const splitArray = (arr, index) => {
|
|
1014
|
-
return [arr.slice(0, index), arr.slice(index + 1, arr.length)]
|
|
1015
|
-
};
|
|
1016
|
-
const renderScripts = (items) => {
|
|
1017
|
-
if(items.length > 0) {
|
|
1018
|
-
const subs = [];
|
|
1019
|
-
const sups = [];
|
|
1020
|
-
items.forEach((item, index) => {
|
|
1021
|
-
// one render as sub script, one as super script
|
|
1022
|
-
if((index + 1) % 2 === 0){
|
|
1023
|
-
sups.push(item);
|
|
1024
|
-
} else {
|
|
1025
|
-
subs.push(item);
|
|
1026
|
-
}
|
|
1027
|
-
});
|
|
1028
|
-
return [
|
|
1029
|
-
(subs.length > 0 ? `_{${subs.join(' ')}}` : ''),
|
|
1030
|
-
(sups.length > 0 ? `^{${sups.join(' ')}}` : '')
|
|
1031
|
-
].join('');
|
|
1032
|
-
} else {
|
|
1033
|
-
return '';
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
const base = parts.shift();
|
|
1037
|
-
let prevScripts = [];
|
|
1038
|
-
let backScripts = [];
|
|
1039
|
-
if(sepIndex === -1){
|
|
1040
|
-
backScripts = parts;
|
|
1041
|
-
} else {
|
|
1042
|
-
[backScripts, prevScripts] = splitArray(parts, sepIndex - 1);
|
|
1043
|
-
}
|
|
1044
|
-
return [renderScripts(prevScripts), base, renderScripts(backScripts)].join('');
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
function renderMunder(node, children){
|
|
1048
|
-
const nodes = flattenNodeTreeByNodeName(node, 'munder');
|
|
1049
|
-
let result = undefined;
|
|
1050
|
-
for(let i = 0; i < nodes.length - 1; i++) {
|
|
1051
|
-
if(!result){ result = parse(nodes[i]); }
|
|
1052
|
-
|
|
1053
|
-
const underNode = nodes[i + 1];
|
|
1054
|
-
const underText = NodeTool.getNodeText(underNode).trim();
|
|
1055
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1056
|
-
|
|
1057
|
-
// Special handling for arrow accent
|
|
1058
|
-
if (underText === "→" && isAccent) {
|
|
1059
|
-
return `\\underset{${result}}{\\rightarrow}`;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
const under = parse(underNode);
|
|
1063
|
-
const template = getMatchValueByChar({
|
|
1064
|
-
decimals: MathSymbol.underScript.decimals,
|
|
1065
|
-
values: MathSymbol.underScript.templates,
|
|
1066
|
-
judgeChar: underText,
|
|
1067
|
-
defaultValue: "@1_{@2}"
|
|
1068
|
-
});
|
|
1069
|
-
result = renderTemplate(template.replace("@v", "@1"), [result, under]);
|
|
1070
|
-
}
|
|
1071
|
-
return result;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
function renderMunderover(node, children){
|
|
1075
|
-
const nodes = flattenNodeTreeByNodeName(node, 'munderover');
|
|
1076
|
-
let result = undefined;
|
|
1077
|
-
for(let i = 0; i < nodes.length - 1; i++) {
|
|
1078
|
-
if(!result){ result = parse(nodes[i]); }
|
|
1079
|
-
|
|
1080
|
-
const overNode = nodes[i + 1];
|
|
1081
|
-
const overText = NodeTool.getNodeText(overNode).trim();
|
|
1082
|
-
const underNode = nodes[i + 2];
|
|
1083
|
-
const underText = NodeTool.getNodeText(underNode).trim();
|
|
1084
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1085
|
-
|
|
1086
|
-
// Special handling for arrow accent
|
|
1087
|
-
if (overText === "→" && isAccent) {
|
|
1088
|
-
return `\\overset{${result}}{\\underset{${underText}}{\\rightarrow}}`;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
const over = parse(overNode);
|
|
1092
|
-
const under = parse(underNode);
|
|
1093
|
-
const template = getMatchValueByChar({
|
|
1094
|
-
decimals: MathSymbol.underoverScript.decimals,
|
|
1095
|
-
values: MathSymbol.underoverScript.templates,
|
|
1096
|
-
judgeChar: overText,
|
|
1097
|
-
defaultValue: "@1_{@2}^{@3}"
|
|
1098
|
-
});
|
|
1099
|
-
result = renderTemplate(template.replace("@v", "@1"), [over, under]);
|
|
1100
|
-
}
|
|
1101
|
-
return result;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
function renderMphantom(node, children){
|
|
1105
|
-
const nodes = flattenNodeTreeByNodeName(node, 'mphantom');
|
|
1106
|
-
let result = undefined;
|
|
1107
|
-
for(let i = 0; i < nodes.length - 1; i++) {
|
|
1108
|
-
if(!result){ result = parse(nodes[i]); }
|
|
1109
|
-
|
|
1110
|
-
const phantomNode = nodes[i + 1];
|
|
1111
|
-
const phantomText = NodeTool.getNodeText(phantomNode).trim();
|
|
1112
|
-
const isAccent = NodeTool.getAttr(node, "accent", "false") === "true";
|
|
1113
|
-
|
|
1114
|
-
// Special handling for arrow accent
|
|
1115
|
-
if (phantomText === "→" && isAccent) {
|
|
1116
|
-
return `\\overrightarrow{${result}}`;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
const phantom = parse(phantomNode);
|
|
1120
|
-
const template = getMatchValueByChar({
|
|
1121
|
-
decimals: MathSymbol.phantomScript.decimals,
|
|
1122
|
-
values: MathSymbol.phantomScript.templates,
|
|
1123
|
-
judgeChar: phantomText,
|
|
1124
|
-
defaultValue: "@1^{@2}"
|
|
1125
|
-
});
|
|
1126
|
-
result = renderTemplate(template.replace("@v", "@1"), [result, phantom]);
|
|
1127
|
-
}
|
|
1128
|
-
return result;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
function renderTemplate(template, args) {
|
|
1132
|
-
return template.replace(/@(\d+)/g, (match, index) => {
|
|
1133
|
-
const arg = args[index - 1];
|
|
1134
|
-
return arg || match;
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
function getMatchValueByChar(options) {
|
|
1139
|
-
const { decimals, values, judgeChar, defaultValue } = options;
|
|
1140
|
-
const match = values.find(value => value.judgeChar === judgeChar);
|
|
1141
|
-
return match || defaultValue;
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
function flattenNodeTreeByNodeName(node, nodeName) {
|
|
1145
|
-
const nodes = [];
|
|
1146
|
-
const children = NodeTool.getChildren(node);
|
|
1147
|
-
if (children && children.length > 0) {
|
|
1148
|
-
// Convert HTMLCollection to Array before using forEach
|
|
1149
|
-
Array.from(children).forEach(child => {
|
|
1150
|
-
if (NodeTool.getNodeName(child) === nodeName) {
|
|
1151
|
-
nodes.push(child);
|
|
1152
|
-
} else {
|
|
1153
|
-
// Recursively search in child nodes
|
|
1154
|
-
const childNodes = flattenNodeTreeByNodeName(child, nodeName);
|
|
1155
|
-
nodes.push(...childNodes);
|
|
1156
|
-
}
|
|
1157
|
-
});
|
|
1158
|
-
}
|
|
1159
|
-
return nodes;
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
1287
|
// Export the convert function
|
|
1163
1288
|
var mathml2latex = {
|
|
1164
1289
|
convert: convert
|