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