1
0

add initial marp implementation with sample content and build configuration

This commit is contained in:
2025-09-13 18:13:22 +02:00
parent dcacc9b409
commit e5f219507f
10319 changed files with 1402023 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { EnrichCase } from './enrich_case.js';
export declare abstract class AbstractEnrichCase implements EnrichCase {
semantic: SemanticNode;
abstract getMathml(): Element;
constructor(semantic: SemanticNode);
}

View File

@@ -0,0 +1,5 @@
export class AbstractEnrichCase {
constructor(semantic) {
this.semantic = semantic;
}
}

View File

@@ -0,0 +1,8 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseBinomial extends AbstractEnrichCase {
mml: Element;
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,31 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import { walkTree } from './enrich_mathml.js';
import { addMrow, setAttributes, Attribute } from './enrich_attr.js';
export class CaseBinomial extends AbstractEnrichCase {
static test(semantic) {
return (!semantic.mathmlTree &&
semantic.type === SemanticType.LINE &&
semantic.role === SemanticRole.BINOMIAL);
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
getMathml() {
if (!this.semantic.childNodes.length) {
return this.mml;
}
const child = this.semantic.childNodes[0];
this.mml = walkTree(child);
if (this.mml.hasAttribute(Attribute.TYPE)) {
const mrow = addMrow();
DomUtil.replaceNode(this.mml, mrow);
mrow.appendChild(this.mml);
this.mml = mrow;
}
setAttributes(this.mml, this.semantic);
return this.mml;
}
}

View File

@@ -0,0 +1,8 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseDoubleScript extends AbstractEnrichCase {
mml: Element;
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,40 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticRole } from '../semantic_tree/semantic_meaning.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { makeIdList, setAttributes, Attribute } from './enrich_attr.js';
export class CaseDoubleScript extends AbstractEnrichCase {
static test(semantic) {
if (!semantic.mathmlTree || !semantic.childNodes.length) {
return false;
}
const mmlTag = DomUtil.tagName(semantic.mathmlTree);
const role = semantic.childNodes[0].role;
return ((mmlTag === MMLTAGS.MSUBSUP && role === SemanticRole.SUBSUP) ||
(mmlTag === MMLTAGS.MUNDEROVER && role === SemanticRole.UNDEROVER));
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
getMathml() {
const ignore = this.semantic.childNodes[0];
const baseSem = ignore.childNodes[0];
const supSem = this.semantic.childNodes[1];
const subSem = ignore.childNodes[1];
const supMml = EnrichMathml.walkTree(supSem);
const baseMml = EnrichMathml.walkTree(baseSem);
const subMml = EnrichMathml.walkTree(subSem);
setAttributes(this.mml, this.semantic);
this.mml.setAttribute(Attribute.CHILDREN, makeIdList([baseSem, subSem, supSem]));
[baseMml, subMml, supMml].forEach((child) => EnrichMathml.getInnerNode(child).setAttribute(Attribute.PARENT, this.mml.getAttribute(Attribute.ID)));
this.mml.setAttribute(Attribute.TYPE, ignore.role);
EnrichMathml.addCollapsedAttribute(this.mml, [
this.semantic.id,
[ignore.id, baseSem.id, subSem.id],
supSem.id
]);
return this.mml;
}
}

View File

@@ -0,0 +1,32 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseEmbellished extends AbstractEnrichCase {
fenced: SemanticNode;
fencedMml: Element;
fencedMmlNodes: Element[];
ofence: SemanticNode;
ofenceMml: Element;
ofenceMap: {
[key: number]: Element;
};
cfence: SemanticNode;
cfenceMml: Element;
cfenceMap: {
[key: number]: Element;
};
parentCleanup: Element[];
static test(semantic: SemanticNode): boolean;
private static makeEmptyNode_;
private static fencedMap_;
constructor(semantic: SemanticNode);
getMathml(): Element;
private fencedElement;
private getFenced_;
private getFencedMml_;
private getFencesMml_;
private rewrite_;
private specialCase_;
private introduceNewLayer_;
private fullFence;
private cleanupParents_;
}

View File

@@ -0,0 +1,211 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import { CaseDoubleScript } from './case_double_script.js';
import { CaseMultiscripts } from './case_multiscripts.js';
import { CaseTensor } from './case_tensor.js';
import * as EnrichMathml from './enrich_mathml.js';
import { addMrow, setAttributes, Attribute } from './enrich_attr.js';
export class CaseEmbellished extends AbstractEnrichCase {
static test(semantic) {
return !!(semantic.mathmlTree &&
semantic.fencePointer &&
!semantic.mathmlTree.getAttribute('data-semantic-type'));
}
static makeEmptyNode_(id) {
const mrow = addMrow();
const empty = new SemanticNode(id);
empty.type = SemanticType.EMPTY;
empty.mathmlTree = mrow;
return empty;
}
static fencedMap_(fence, ids) {
ids[fence.id] = fence.mathmlTree;
if (!fence.embellished) {
return;
}
CaseEmbellished.fencedMap_(fence.childNodes[0], ids);
}
constructor(semantic) {
super(semantic);
this.fenced = null;
this.fencedMml = null;
this.fencedMmlNodes = [];
this.ofence = null;
this.ofenceMml = null;
this.ofenceMap = {};
this.cfence = null;
this.cfenceMml = null;
this.cfenceMap = {};
this.parentCleanup = [];
}
getMathml() {
this.getFenced_();
this.fencedMml = EnrichMathml.walkTree(this.fenced);
this.getFencesMml_();
if (this.fenced.type === SemanticType.EMPTY && !this.fencedMml.parentNode) {
this.fencedMml.setAttribute(Attribute.ADDED, 'true');
this.cfenceMml.parentNode.insertBefore(this.fencedMml, this.cfenceMml);
}
this.getFencedMml_();
const rewrite = this.rewrite_();
return rewrite;
}
fencedElement(node) {
return (node.type === SemanticType.FENCED ||
node.type === SemanticType.MATRIX ||
node.type === SemanticType.VECTOR);
}
getFenced_() {
let currentNode = this.semantic;
while (!this.fencedElement(currentNode)) {
currentNode = currentNode.childNodes[0];
}
this.fenced = currentNode.childNodes[0];
this.ofence = currentNode.contentNodes[0];
this.cfence = currentNode.contentNodes[1];
CaseEmbellished.fencedMap_(this.ofence, this.ofenceMap);
CaseEmbellished.fencedMap_(this.cfence, this.cfenceMap);
}
getFencedMml_() {
let sibling = this.ofenceMml.nextSibling;
sibling = sibling === this.fencedMml ? sibling : this.fencedMml;
while (sibling && sibling !== this.cfenceMml) {
this.fencedMmlNodes.push(sibling);
sibling = sibling.nextSibling;
}
}
getFencesMml_() {
let currentNode = this.semantic;
const ofenceIds = Object.keys(this.ofenceMap);
const cfenceIds = Object.keys(this.cfenceMap);
while ((!this.ofenceMml || !this.cfenceMml) &&
currentNode !== this.fenced) {
if (ofenceIds.indexOf(currentNode.fencePointer) !== -1 &&
!this.ofenceMml) {
this.ofenceMml = currentNode.mathmlTree;
}
if (cfenceIds.indexOf(currentNode.fencePointer) !== -1 &&
!this.cfenceMml) {
this.cfenceMml = currentNode.mathmlTree;
}
currentNode = currentNode.childNodes[0];
}
if (!this.ofenceMml) {
this.ofenceMml = this.ofence.mathmlTree;
}
if (!this.cfenceMml) {
this.cfenceMml = this.cfence.mathmlTree;
}
if (this.ofenceMml) {
this.ofenceMml = EnrichMathml.ascendNewNode(this.ofenceMml);
}
if (this.cfenceMml) {
this.cfenceMml = EnrichMathml.ascendNewNode(this.cfenceMml);
}
}
rewrite_() {
let currentNode = this.semantic;
let result = null;
const newNode = this.introduceNewLayer_();
setAttributes(newNode, this.fenced.parent);
while (!this.fencedElement(currentNode)) {
const mml = currentNode.mathmlTree;
const specialCase = this.specialCase_(currentNode, mml);
if (specialCase) {
currentNode = specialCase;
}
else {
setAttributes(mml, currentNode);
const mmlChildren = [];
for (let i = 1, child; (child = currentNode.childNodes[i]); i++) {
mmlChildren.push(EnrichMathml.walkTree(child));
}
currentNode = currentNode.childNodes[0];
}
const dummy = DomUtil.createElement('dummy');
const saveChild = mml.childNodes[0];
DomUtil.replaceNode(mml, dummy);
DomUtil.replaceNode(newNode, mml);
DomUtil.replaceNode(mml.childNodes[0], newNode);
DomUtil.replaceNode(dummy, saveChild);
if (!result) {
result = mml;
}
}
EnrichMathml.walkTree(this.ofence);
EnrichMathml.walkTree(this.cfence);
this.cleanupParents_();
return result || newNode;
}
specialCase_(semantic, mml) {
const mmlTag = DomUtil.tagName(mml);
let parent = null;
let caller;
if (mmlTag === MMLTAGS.MSUBSUP) {
parent = semantic.childNodes[0];
caller = CaseDoubleScript;
}
else if (mmlTag === MMLTAGS.MMULTISCRIPTS) {
if (semantic.type === SemanticType.SUPERSCRIPT ||
semantic.type === SemanticType.SUBSCRIPT) {
caller = CaseMultiscripts;
}
else if (semantic.type === SemanticType.TENSOR) {
caller = CaseTensor;
}
if (caller &&
semantic.childNodes[0] &&
semantic.childNodes[0].role === SemanticRole.SUBSUP) {
parent = semantic.childNodes[0];
}
else {
parent = semantic;
}
}
if (!parent) {
return null;
}
const base = parent.childNodes[0];
const empty = CaseEmbellished.makeEmptyNode_(base.id);
parent.childNodes[0] = empty;
mml = new caller(semantic).getMathml();
parent.childNodes[0] = base;
this.parentCleanup.push(mml);
return parent.childNodes[0];
}
introduceNewLayer_() {
const fullOfence = this.fullFence(this.ofenceMml);
const fullCfence = this.fullFence(this.cfenceMml);
let newNode = addMrow();
DomUtil.replaceNode(this.fencedMml, newNode);
this.fencedMmlNodes.forEach((node) => newNode.appendChild(node));
newNode.insertBefore(fullOfence, this.fencedMml);
newNode.appendChild(fullCfence);
if (!newNode.parentNode) {
const mrow = addMrow();
while (newNode.childNodes.length > 0) {
mrow.appendChild(newNode.childNodes[0]);
}
newNode.appendChild(mrow);
newNode = mrow;
}
return newNode;
}
fullFence(fence) {
const parent = this.fencedMml.parentNode;
let currentFence = fence;
while (currentFence.parentNode && currentFence.parentNode !== parent) {
currentFence = currentFence.parentNode;
}
return currentFence;
}
cleanupParents_() {
this.parentCleanup.forEach(function (x) {
const parent = x.childNodes[1].getAttribute(Attribute.PARENT);
x.childNodes[0].setAttribute(Attribute.PARENT, parent);
});
}
}

View File

@@ -0,0 +1,11 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseEmpheq extends AbstractEnrichCase {
mml: Element;
private mrows;
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
private recurseToTable;
private finalizeTable;
}

View File

@@ -0,0 +1,79 @@
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { addMrow, setAttributes } from './enrich_attr.js';
import * as DomUtil from '../common/dom_util.js';
export class CaseEmpheq extends AbstractEnrichCase {
static test(semantic) {
return !!semantic.mathmlTree && semantic.hasAnnotation('Emph', 'top');
}
constructor(semantic) {
super(semantic);
this.mrows = [];
this.mml = semantic.mathmlTree;
}
getMathml() {
this.recurseToTable(this.semantic);
if (this.mrows.length) {
const newRow = addMrow();
const parent = this.mml.parentNode;
parent.insertBefore(newRow, this.mml);
for (const mrow of this.mrows) {
newRow.appendChild(mrow);
}
newRow.appendChild(this.mml);
}
return this.mml;
}
recurseToTable(node) {
var _a, _b;
if (!(node.hasAnnotation('Emph', 'top') || node.hasAnnotation('Emph', 'fence')) &&
(node.hasAnnotation('Emph', 'left') ||
node.hasAnnotation('Emph', 'right'))) {
EnrichMathml.walkTree(node);
return;
}
if (!node.mathmlTree ||
(DomUtil.tagName(node.mathmlTree) === MMLTAGS.MTABLE &&
((_a = node.annotation['Emph']) === null || _a === void 0 ? void 0 : _a.length) &&
node.annotation['Emph'][0] !== 'table')) {
const newNode = addMrow();
setAttributes(newNode, node);
this.mrows.unshift(newNode);
}
else {
if (DomUtil.tagName(node.mathmlTree) === MMLTAGS.MTABLE &&
((_b = node.annotation['Emph']) === null || _b === void 0 ? void 0 : _b.length) &&
node.annotation['Emph'][0] === 'table') {
this.finalizeTable(node);
return;
}
setAttributes(node.mathmlTree, node);
}
node.childNodes.forEach(this.recurseToTable.bind(this));
if (node.textContent || node.type === 'punctuated') {
const newContent = node.contentNodes.map((x) => {
const newNode = EnrichMathml.cloneContentNode(x);
if (newNode.hasAttribute('data-semantic-added')) {
this.mrows.unshift(newNode);
}
else {
this.recurseToTable(x);
}
return newNode;
});
EnrichMathml.setOperatorAttribute(node, newContent);
return;
}
node.contentNodes.forEach(this.recurseToTable.bind(this));
}
finalizeTable(node) {
setAttributes(node.mathmlTree, node);
node.contentNodes.forEach((x) => {
EnrichMathml.walkTree(x);
});
node.childNodes.forEach((x) => {
EnrichMathml.walkTree(x);
});
}
}

View File

@@ -0,0 +1,9 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseLimit extends AbstractEnrichCase {
mml: Element;
static test(semantic: SemanticNode): boolean;
private static walkTree_;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,44 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticType } from '../semantic_tree/semantic_meaning.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes } from './enrich_attr.js';
export class CaseLimit extends AbstractEnrichCase {
static test(semantic) {
if (!semantic.mathmlTree || !semantic.childNodes.length) {
return false;
}
const mmlTag = DomUtil.tagName(semantic.mathmlTree);
const type = semantic.type;
return (((type === SemanticType.LIMUPPER || type === SemanticType.LIMLOWER) &&
(mmlTag === MMLTAGS.MSUBSUP || mmlTag === MMLTAGS.MUNDEROVER)) ||
(type === SemanticType.LIMBOTH &&
(mmlTag === MMLTAGS.MSUB ||
mmlTag === MMLTAGS.MUNDER ||
mmlTag === MMLTAGS.MSUP ||
mmlTag === MMLTAGS.MOVER)));
}
static walkTree_(node) {
if (node) {
EnrichMathml.walkTree(node);
}
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
getMathml() {
const children = this.semantic.childNodes;
if (this.semantic.type !== SemanticType.LIMBOTH &&
this.mml.childNodes.length >= 3) {
this.mml = EnrichMathml.introduceNewLayer([this.mml], this.semantic);
}
setAttributes(this.mml, this.semantic);
if (!children[0].mathmlTree) {
children[0].mathmlTree = this.semantic.mathmlTree;
}
children.forEach(CaseLimit.walkTree_);
return this.mml;
}
}

View File

@@ -0,0 +1,8 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseLine extends AbstractEnrichCase {
mml: Element;
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,23 @@
import { SemanticType } from '../semantic_tree/semantic_meaning.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes } from './enrich_attr.js';
export class CaseLine extends AbstractEnrichCase {
static test(semantic) {
return !!semantic.mathmlTree && semantic.type === SemanticType.LINE;
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
getMathml() {
if (this.semantic.contentNodes.length) {
EnrichMathml.walkTree(this.semantic.contentNodes[0]);
}
if (this.semantic.childNodes.length) {
EnrichMathml.walkTree(this.semantic.childNodes[0]);
}
setAttributes(this.mml, this.semantic);
return this.mml;
}
}

View File

@@ -0,0 +1,10 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { Sexp } from '../semantic_tree/semantic_skeleton.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare abstract class CaseMultiindex extends AbstractEnrichCase {
mml: Element;
static multiscriptIndex(index: SemanticNode): Sexp;
private static createNone_;
constructor(semantic: SemanticNode);
protected completeMultiscript(rightIndices: Sexp, leftIndices: Sexp): void;
}

View File

@@ -0,0 +1,60 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes, Attribute } from './enrich_attr.js';
export class CaseMultiindex extends AbstractEnrichCase {
static multiscriptIndex(index) {
if (index.type === SemanticType.PUNCTUATED &&
index.contentNodes[0].role === SemanticRole.DUMMY) {
return EnrichMathml.collapsePunctuated(index);
}
EnrichMathml.walkTree(index);
return index.id;
}
static createNone_(semantic) {
const newNode = DomUtil.createElement('none');
if (semantic) {
setAttributes(newNode, semantic);
}
newNode.setAttribute(Attribute.ADDED, 'true');
return newNode;
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
completeMultiscript(rightIndices, leftIndices) {
const children = DomUtil.toArray(this.mml.childNodes).slice(1);
let childCounter = 0;
const completeIndices = (indices) => {
for (const index of indices) {
const child = children[childCounter];
if (child && index === parseInt(child.getAttribute(Attribute.ID))) {
child.setAttribute(Attribute.PARENT, this.semantic.id.toString());
childCounter++;
}
else if (!child ||
index !==
parseInt(EnrichMathml.getInnerNode(child).getAttribute(Attribute.ID))) {
const query = this.semantic.querySelectorAll((x) => x.id === index);
this.mml.insertBefore(CaseMultiindex.createNone_(query[0]), child || null);
}
else {
EnrichMathml.getInnerNode(child).setAttribute(Attribute.PARENT, this.semantic.id.toString());
childCounter++;
}
}
};
completeIndices(rightIndices);
if (children[childCounter] &&
DomUtil.tagName(children[childCounter]) !== MMLTAGS.MPRESCRIPTS) {
this.mml.insertBefore(children[childCounter], DomUtil.createElement('mprescripts'));
}
else {
childCounter++;
}
completeIndices(leftIndices);
}
}

View File

@@ -0,0 +1,7 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { CaseMultiindex } from './case_multiindex.js';
export declare class CaseMultiscripts extends CaseMultiindex {
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,48 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { SemanticSkeleton } from '../semantic_tree/semantic_skeleton.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { CaseMultiindex } from './case_multiindex.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes, Attribute } from './enrich_attr.js';
export class CaseMultiscripts extends CaseMultiindex {
static test(semantic) {
if (!semantic.mathmlTree) {
return false;
}
const mmlTag = DomUtil.tagName(semantic.mathmlTree);
return (mmlTag === MMLTAGS.MMULTISCRIPTS &&
(semantic.type === SemanticType.SUPERSCRIPT ||
semantic.type === SemanticType.SUBSCRIPT));
}
constructor(semantic) {
super(semantic);
}
getMathml() {
setAttributes(this.mml, this.semantic);
let baseSem, rsup, rsub;
if (this.semantic.childNodes[0] &&
this.semantic.childNodes[0].role === SemanticRole.SUBSUP) {
const ignore = this.semantic.childNodes[0];
baseSem = ignore.childNodes[0];
rsup = CaseMultiindex.multiscriptIndex(this.semantic.childNodes[1]);
rsub = CaseMultiindex.multiscriptIndex(ignore.childNodes[1]);
const collapsed = [this.semantic.id, [ignore.id, baseSem.id, rsub], rsup];
EnrichMathml.addCollapsedAttribute(this.mml, collapsed);
this.mml.setAttribute(Attribute.TYPE, ignore.role);
this.completeMultiscript(SemanticSkeleton.interleaveIds(rsub, rsup), []);
}
else {
baseSem = this.semantic.childNodes[0];
rsup = CaseMultiindex.multiscriptIndex(this.semantic.childNodes[1]);
const collapsed = [this.semantic.id, baseSem.id, rsup];
EnrichMathml.addCollapsedAttribute(this.mml, collapsed);
}
const childIds = SemanticSkeleton.collapsedLeafs(rsub || [], rsup);
const base = EnrichMathml.walkTree(baseSem);
EnrichMathml.getInnerNode(base).setAttribute(Attribute.PARENT, this.semantic.id.toString());
childIds.unshift(baseSem.id);
this.mml.setAttribute(Attribute.CHILDREN, childIds.join(','));
return this.mml;
}
}

View File

@@ -0,0 +1,8 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseProof extends AbstractEnrichCase {
mml: Element;
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,33 @@
import { SemanticType } from '../semantic_tree/semantic_meaning.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes } from './enrich_attr.js';
export class CaseProof extends AbstractEnrichCase {
static test(semantic) {
return (!!semantic.mathmlTree &&
(semantic.type === SemanticType.INFERENCE ||
semantic.type === SemanticType.PREMISES));
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
getMathml() {
if (!this.semantic.childNodes.length) {
return this.mml;
}
this.semantic.contentNodes.forEach(function (x) {
EnrichMathml.walkTree(x);
setAttributes(x.mathmlTree, x);
});
this.semantic.childNodes.forEach(function (x) {
EnrichMathml.walkTree(x);
});
setAttributes(this.mml, this.semantic);
if (this.mml.getAttribute('data-semantic-id') ===
this.mml.getAttribute('data-semantic-parent')) {
this.mml.removeAttribute('data-semantic-parent');
}
return this.mml;
}
}

View File

@@ -0,0 +1,9 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseTable extends AbstractEnrichCase {
mml: Element;
inner: Element[];
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,45 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticType } from '../semantic_tree/semantic_meaning.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes } from './enrich_attr.js';
export class CaseTable extends AbstractEnrichCase {
static test(semantic) {
return (semantic.type === SemanticType.MATRIX ||
semantic.type === SemanticType.VECTOR ||
semantic.type === SemanticType.CASES);
}
constructor(semantic) {
super(semantic);
this.inner = [];
this.mml = semantic.mathmlTree;
}
getMathml() {
const lfence = EnrichMathml.cloneContentNode(this.semantic.contentNodes[0]);
const rfence = this.semantic.contentNodes[1]
? EnrichMathml.cloneContentNode(this.semantic.contentNodes[1])
: null;
this.inner = this.semantic.childNodes.map(EnrichMathml.walkTree);
if (!this.mml) {
this.mml = EnrichMathml.introduceNewLayer([lfence].concat(this.inner, [rfence]), this.semantic);
}
else if (DomUtil.tagName(this.mml) === MMLTAGS.MFENCED) {
const children = this.mml.childNodes;
this.mml.insertBefore(lfence, children[0] || null);
if (rfence) {
this.mml.appendChild(rfence);
}
this.mml = EnrichMathml.rewriteMfenced(this.mml);
}
else {
const newChildren = [lfence, this.mml];
if (rfence) {
newChildren.push(rfence);
}
this.mml = EnrichMathml.introduceNewLayer(newChildren, this.semantic);
}
setAttributes(this.mml, this.semantic);
return this.mml;
}
}

View File

@@ -0,0 +1,7 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { CaseMultiindex } from './case_multiindex.js';
export declare class CaseTensor extends CaseMultiindex {
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,35 @@
import { SemanticType } from '../semantic_tree/semantic_meaning.js';
import { SemanticSkeleton } from '../semantic_tree/semantic_skeleton.js';
import { CaseMultiindex } from './case_multiindex.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes, Attribute } from './enrich_attr.js';
export class CaseTensor extends CaseMultiindex {
static test(semantic) {
return !!semantic.mathmlTree && semantic.type === SemanticType.TENSOR;
}
constructor(semantic) {
super(semantic);
}
getMathml() {
EnrichMathml.walkTree(this.semantic.childNodes[0]);
const lsub = CaseMultiindex.multiscriptIndex(this.semantic.childNodes[1]);
const lsup = CaseMultiindex.multiscriptIndex(this.semantic.childNodes[2]);
const rsub = CaseMultiindex.multiscriptIndex(this.semantic.childNodes[3]);
const rsup = CaseMultiindex.multiscriptIndex(this.semantic.childNodes[4]);
setAttributes(this.mml, this.semantic);
const collapsed = [
this.semantic.id,
this.semantic.childNodes[0].id,
lsub,
lsup,
rsub,
rsup
];
EnrichMathml.addCollapsedAttribute(this.mml, collapsed);
const childIds = SemanticSkeleton.collapsedLeafs(lsub, lsup, rsub, rsup);
childIds.unshift(this.semantic.childNodes[0].id);
this.mml.setAttribute(Attribute.CHILDREN, childIds.join(','));
this.completeMultiscript(SemanticSkeleton.interleaveIds(rsub, rsup), SemanticSkeleton.interleaveIds(lsub, lsup));
return this.mml;
}
}

View File

@@ -0,0 +1,8 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
export declare class CaseText extends AbstractEnrichCase {
mml: Element;
static test(semantic: SemanticNode): boolean;
constructor(semantic: SemanticNode);
getMathml(): Element;
}

View File

@@ -0,0 +1,24 @@
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { AbstractEnrichCase } from './abstract_enrich_case.js';
import * as EnrichMathml from './enrich_mathml.js';
import { setAttributes, Attribute } from './enrich_attr.js';
export class CaseText extends AbstractEnrichCase {
static test(semantic) {
return (semantic.type === SemanticType.PUNCTUATED &&
(semantic.role === SemanticRole.TEXT ||
semantic.contentNodes.every((x) => x.role === SemanticRole.DUMMY)));
}
constructor(semantic) {
super(semantic);
this.mml = semantic.mathmlTree;
}
getMathml() {
const children = [];
const collapsed = EnrichMathml.collapsePunctuated(this.semantic, children);
this.mml = EnrichMathml.introduceNewLayer(children, this.semantic);
setAttributes(this.mml, this.semantic);
this.mml.removeAttribute(Attribute.CONTENT);
EnrichMathml.addCollapsedAttribute(this.mml, collapsed);
return this.mml;
}
}

View File

@@ -0,0 +1,6 @@
import './enrich_case_factory.js';
export declare function semanticMathmlNode(mml: Element): Element;
export declare function semanticMathmlSync(expr: string): Element;
export declare function semanticMathml(expr: string, callback: (p1: Element) => any): void;
export declare function testTranslation(expr: string): Element;
export declare function prepareMmlString(expr: string): string;

View File

@@ -0,0 +1,36 @@
import { Debugger } from '../common/debugger.js';
import * as DomUtil from '../common/dom_util.js';
import { EnginePromise } from '../common/engine.js';
import * as Semantic from '../semantic_tree/semantic.js';
import * as EnrichMathml from './enrich_mathml.js';
import './enrich_case_factory.js';
export function semanticMathmlNode(mml) {
const clone = DomUtil.cloneNode(mml);
const tree = Semantic.getTree(clone);
return EnrichMathml.enrich(clone, tree);
}
export function semanticMathmlSync(expr) {
const mml = DomUtil.parseInput(expr);
return semanticMathmlNode(mml);
}
export function semanticMathml(expr, callback) {
EnginePromise.getall().then(() => {
const mml = DomUtil.parseInput(expr);
callback(semanticMathmlNode(mml));
});
}
export function testTranslation(expr) {
Debugger.getInstance().init();
const mml = semanticMathmlSync(prepareMmlString(expr));
Debugger.getInstance().exit();
return mml;
}
export function prepareMmlString(expr) {
if (!expr.match(/^<math/)) {
expr = '<math>' + expr;
}
if (!expr.match(/\/math>$/)) {
expr += '</math>';
}
return expr;
}

View File

@@ -0,0 +1,30 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
export declare enum Attribute {
ADDED = "data-semantic-added",
ALTERNATIVE = "data-semantic-alternative",
CHILDREN = "data-semantic-children",
COLLAPSED = "data-semantic-collapsed",
CONTENT = "data-semantic-content",
EMBELLISHED = "data-semantic-embellished",
FENCEPOINTER = "data-semantic-fencepointer",
FONT = "data-semantic-font",
ID = "data-semantic-id",
ANNOTATION = "data-semantic-annotation",
ATTRIBUTES = "data-semantic-attributes",
OPERATOR = "data-semantic-operator",
OWNS = "data-semantic-owns",
PARENT = "data-semantic-parent",
POSTFIX = "data-semantic-postfix",
PREFIX = "data-semantic-prefix",
ROLE = "data-semantic-role",
SPEECH = "data-semantic-speech",
STRUCTURE = "data-semantic-structure",
SUMMARY = "data-semantic-summary",
TYPE = "data-semantic-type"
}
export declare const EnrichAttributes: string[];
export declare function makeIdList(nodes: SemanticNode[]): string;
export declare function setAttributes(mml: Element, semantic: SemanticNode): void;
export declare function removeAttributePrefix(mml: string): string;
export declare function addPrefix(attr: string): Attribute;
export declare function addMrow(): Element;

View File

@@ -0,0 +1,101 @@
import * as DomUtil from '../common/dom_util.js';
import { SemanticRole } from '../semantic_tree/semantic_meaning.js';
const Prefix = 'data-semantic-';
export var Attribute;
(function (Attribute) {
Attribute["ADDED"] = "data-semantic-added";
Attribute["ALTERNATIVE"] = "data-semantic-alternative";
Attribute["CHILDREN"] = "data-semantic-children";
Attribute["COLLAPSED"] = "data-semantic-collapsed";
Attribute["CONTENT"] = "data-semantic-content";
Attribute["EMBELLISHED"] = "data-semantic-embellished";
Attribute["FENCEPOINTER"] = "data-semantic-fencepointer";
Attribute["FONT"] = "data-semantic-font";
Attribute["ID"] = "data-semantic-id";
Attribute["ANNOTATION"] = "data-semantic-annotation";
Attribute["ATTRIBUTES"] = "data-semantic-attributes";
Attribute["OPERATOR"] = "data-semantic-operator";
Attribute["OWNS"] = "data-semantic-owns";
Attribute["PARENT"] = "data-semantic-parent";
Attribute["POSTFIX"] = "data-semantic-postfix";
Attribute["PREFIX"] = "data-semantic-prefix";
Attribute["ROLE"] = "data-semantic-role";
Attribute["SPEECH"] = "data-semantic-speech";
Attribute["STRUCTURE"] = "data-semantic-structure";
Attribute["SUMMARY"] = "data-semantic-summary";
Attribute["TYPE"] = "data-semantic-type";
})(Attribute || (Attribute = {}));
export const EnrichAttributes = [
Attribute.ADDED,
Attribute.ALTERNATIVE,
Attribute.CHILDREN,
Attribute.COLLAPSED,
Attribute.CONTENT,
Attribute.EMBELLISHED,
Attribute.FENCEPOINTER,
Attribute.FONT,
Attribute.ID,
Attribute.ANNOTATION,
Attribute.ATTRIBUTES,
Attribute.OPERATOR,
Attribute.OWNS,
Attribute.PARENT,
Attribute.POSTFIX,
Attribute.PREFIX,
Attribute.ROLE,
Attribute.SPEECH,
Attribute.STRUCTURE,
Attribute.SUMMARY,
Attribute.TYPE
];
export function makeIdList(nodes) {
return nodes
.map(function (node) {
return node.id;
})
.join(',');
}
export function setAttributes(mml, semantic) {
mml.setAttribute(Attribute.TYPE, semantic.type);
const attributes = semantic.allAttributes();
for (let i = 0, attr; (attr = attributes[i]); i++) {
mml.setAttribute(Prefix + attr[0].toLowerCase(), attr[1]);
}
if (semantic.childNodes.length) {
mml.setAttribute(Attribute.CHILDREN, makeIdList(semantic.childNodes));
}
if (semantic.contentNodes.length) {
mml.setAttribute(Attribute.CONTENT, makeIdList(semantic.contentNodes));
}
if (semantic.parent) {
mml.setAttribute(Attribute.PARENT, semantic.parent.id.toString());
}
const external = semantic.attributesXml();
if (external) {
mml.setAttribute(Attribute.ATTRIBUTES, external);
}
setPostfix(mml, semantic);
}
function setPostfix(mml, semantic) {
const postfix = [];
if (semantic.role === SemanticRole.MGLYPH) {
postfix.push('image');
}
if (semantic.attributes['href']) {
postfix.push('link');
}
if (postfix.length) {
mml.setAttribute(Attribute.POSTFIX, postfix.join(' '));
}
}
export function removeAttributePrefix(mml) {
return mml.toString().replace(new RegExp(Prefix, 'g'), '');
}
export function addPrefix(attr) {
return (Prefix + attr);
}
export function addMrow() {
const mrow = DomUtil.createElement('mrow');
mrow.setAttribute(Attribute.ADDED, 'true');
return mrow;
}

View File

@@ -0,0 +1,11 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
export interface EnrichCase {
getMathml(): Element;
}
interface Case {
test: (p1: SemanticNode) => boolean;
constr: (p1: SemanticNode) => EnrichCase;
}
export declare function getCase(node: SemanticNode): EnrichCase;
export declare const factory: Case[];
export {};

View File

@@ -0,0 +1,9 @@
export function getCase(node) {
for (let i = 0, enrich; (enrich = factory[i]); i++) {
if (enrich.test(node)) {
return enrich.constr(node);
}
}
return null;
}
export const factory = [];

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,52 @@
import { CaseBinomial } from './case_binomial.js';
import { CaseDoubleScript } from './case_double_script.js';
import { CaseEmbellished } from './case_embellished.js';
import { CaseEmpheq } from './case_empheq.js';
import { CaseLimit } from './case_limit.js';
import { CaseLine } from './case_line.js';
import { CaseMultiscripts } from './case_multiscripts.js';
import { CaseProof } from './case_proof.js';
import { CaseTable } from './case_table.js';
import { CaseTensor } from './case_tensor.js';
import { CaseText } from './case_text.js';
import { factory } from './enrich_case.js';
factory.push(...[
{
test: CaseLimit.test,
constr: (node) => new CaseLimit(node)
},
{
test: CaseEmbellished.test,
constr: (node) => new CaseEmbellished(node)
},
{
test: CaseDoubleScript.test,
constr: (node) => new CaseDoubleScript(node)
},
{
test: CaseTensor.test,
constr: (node) => new CaseTensor(node)
},
{
test: CaseMultiscripts.test,
constr: (node) => new CaseMultiscripts(node)
},
{ test: CaseLine.test, constr: (node) => new CaseLine(node) },
{
test: CaseBinomial.test,
constr: (node) => new CaseBinomial(node)
},
{
test: CaseProof.test,
constr: (node) => new CaseProof(node)
},
{
test: CaseEmpheq.test,
constr: (node) => new CaseEmpheq(node)
},
{
test: CaseTable.test,
constr: (node) => new CaseTable(node)
},
{ test: CaseText.test, constr: (node) => new CaseText(node) }
]);

View File

@@ -0,0 +1,13 @@
import { SemanticNode } from '../semantic_tree/semantic_node.js';
import { Sexp } from '../semantic_tree/semantic_skeleton.js';
import { SemanticTree } from '../semantic_tree/semantic_tree.js';
export declare function enrich(mml: Element, semantic: SemanticTree): Element;
export declare function walkTree(semantic: SemanticNode): Element;
export declare function introduceNewLayer(children: Element[], semantic: SemanticNode): Element;
export declare function ascendNewNode(newNode: Element): Element;
export declare function addCollapsedAttribute(node: Element, collapsed: Sexp): void;
export declare function cloneContentNode(content: SemanticNode): Element;
export declare function rewriteMfenced(mml: Element): Element;
export declare function setOperatorAttribute(semantic: SemanticNode, content: Element[]): void;
export declare function getInnerNode(node: Element): Element;
export declare function collapsePunctuated(semantic: SemanticNode, opt_children?: Element[]): Sexp;

View File

@@ -0,0 +1,543 @@
import { Debugger } from '../common/debugger.js';
import * as DomUtil from '../common/dom_util.js';
import { Engine } from '../common/engine.js';
import { NamedSymbol } from '../semantic_tree/semantic_attr.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { SemanticHeuristics } from '../semantic_tree/semantic_heuristic_factory.js';
import { SemanticSkeleton } from '../semantic_tree/semantic_skeleton.js';
import * as SemanticUtil from '../semantic_tree/semantic_util.js';
import { MMLTAGS } from '../semantic_tree/semantic_util.js';
import * as EnrichAttr from './enrich_attr.js';
import { getCase } from './enrich_case.js';
const SETTINGS = {
collapsed: true,
implicit: true,
wiki: true
};
const IDS = new Map();
export function enrich(mml, semantic) {
IDS.clear();
const oldMml = DomUtil.cloneNode(mml);
walkTree(semantic.root);
if (Engine.getInstance().structure) {
mml.setAttribute(EnrichAttr.Attribute.STRUCTURE, SemanticSkeleton.fromStructure(mml, semantic).toString());
}
Debugger.getInstance().generateOutput(() => [
formattedOutput(oldMml, 'Original MathML', SETTINGS.wiki),
formattedOutput(semantic, 'Semantic Tree', SETTINGS.wiki),
formattedOutput(mml, 'Semantically enriched MathML', SETTINGS.wiki)
]);
return mml;
}
export function walkTree(semantic) {
Debugger.getInstance().output('WALKING START: ' + semantic.toString());
const specialCase = getCase(semantic);
let newNode;
if (specialCase) {
newNode = specialCase.getMathml();
Debugger.getInstance().output('WALKING END: ' + semantic.toString());
return ascendNewNode(newNode);
}
if (semantic.mathml.length === 1) {
Debugger.getInstance().output('Walktree Case 0');
if (!semantic.childNodes.length) {
Debugger.getInstance().output('Walktree Case 0.1');
newNode = semantic.mathml[0];
EnrichAttr.setAttributes(newNode, semantic);
Debugger.getInstance().output('WALKING END: ' + semantic.toString());
return ascendNewNode(newNode);
}
const fchild = semantic.childNodes[0];
if (semantic.childNodes.length === 1 &&
fchild.type === SemanticType.EMPTY) {
Debugger.getInstance().output('Walktree Case 0.2');
newNode = semantic.mathml[0];
EnrichAttr.setAttributes(newNode, semantic);
newNode.appendChild(walkTree(fchild));
Debugger.getInstance().output('WALKING END: ' + semantic.toString());
return ascendNewNode(newNode);
}
semantic.childNodes.forEach((child) => {
if (!child.mathml.length) {
child.mathml = [createInvisibleOperator(child)];
}
});
}
const newContent = semantic.contentNodes.map(cloneContentNode);
setOperatorAttribute(semantic, newContent);
const newChildren = semantic.childNodes.map(walkTree);
const childrenList = SemanticSkeleton.combineContentChildren(semantic.type, semantic.role, newContent, newChildren);
newNode = semantic.mathmlTree;
if (newNode === null) {
Debugger.getInstance().output('Walktree Case 1');
newNode = introduceNewLayer(childrenList, semantic);
}
else {
const attached = attachedElement(childrenList);
Debugger.getInstance().output('Walktree Case 2');
if (attached) {
Debugger.getInstance().output('Walktree Case 2.1');
newNode = parentNode(attached);
}
else {
Debugger.getInstance().output('Walktree Case 2.2');
newNode = getInnerNode(newNode);
}
}
newNode = rewriteMfenced(newNode);
mergeChildren(newNode, childrenList, semantic);
if (!IDS.has(semantic.id)) {
IDS.set(semantic.id, true);
EnrichAttr.setAttributes(newNode, semantic);
}
Debugger.getInstance().output('WALKING END: ' + semantic.toString());
return ascendNewNode(newNode);
}
export function introduceNewLayer(children, semantic) {
const lca = mathmlLca(children);
let newNode = lca.node;
const info = lca.type;
if (info !== lcaType.VALID ||
!SemanticUtil.hasEmptyTag(newNode) ||
(!newNode.parentNode && semantic.parent)) {
Debugger.getInstance().output('Walktree Case 1.1');
newNode = EnrichAttr.addMrow();
if (info === lcaType.PRUNED) {
Debugger.getInstance().output('Walktree Case 1.1.0');
newNode = introduceLayerAboveLca(newNode, lca.node, children);
}
else if (children[0]) {
Debugger.getInstance().output('Walktree Case 1.1.1');
const node = attachedElement(children);
if (node) {
const oldChildren = childrenSubset(parentNode(node), children);
DomUtil.replaceNode(node, newNode);
oldChildren.forEach(function (x) {
newNode.appendChild(x);
});
}
else {
moveSemanticAttributes(newNode, children[0]);
newNode = children[0];
}
}
}
if (!semantic.mathmlTree) {
semantic.mathmlTree = newNode;
}
return newNode;
}
function introduceLayerAboveLca(mrow, lca, children) {
let innerNode = descendNode(lca);
if (SemanticUtil.hasMathTag(innerNode)) {
Debugger.getInstance().output('Walktree Case 1.1.0.0');
moveSemanticAttributes(innerNode, mrow);
DomUtil.toArray(innerNode.childNodes).forEach(function (x) {
mrow.appendChild(x);
});
const auxNode = mrow;
mrow = innerNode;
innerNode = auxNode;
}
const index = children.indexOf(lca);
children[index] = innerNode;
DomUtil.replaceNode(innerNode, mrow);
mrow.appendChild(innerNode);
children.forEach(function (x) {
mrow.appendChild(x);
});
return mrow;
}
function moveSemanticAttributes(oldNode, newNode) {
for (const attr of EnrichAttr.EnrichAttributes) {
if (oldNode.hasAttribute(attr)) {
newNode.setAttribute(attr, oldNode.getAttribute(attr));
oldNode.removeAttribute(attr);
}
}
}
function childrenSubset(node, newChildren) {
const oldChildren = DomUtil.toArray(node.childNodes);
let leftIndex = +Infinity;
let rightIndex = -Infinity;
newChildren.forEach(function (child) {
const index = oldChildren.indexOf(child);
if (index !== -1) {
leftIndex = Math.min(leftIndex, index);
rightIndex = Math.max(rightIndex, index);
}
});
return oldChildren.slice(leftIndex, rightIndex + 1);
}
function collateChildNodes(node, children, semantic) {
const oldChildren = [];
let newChildren = DomUtil.toArray(node.childNodes);
let notFirst = false;
while (newChildren.length) {
const child = newChildren.shift();
if (child.hasAttribute(EnrichAttr.Attribute.TYPE)) {
oldChildren.push(child);
continue;
}
const collect = collectChildNodes(child, children);
if (collect.length === 0) {
continue;
}
if (collect.length === 1) {
oldChildren.push(child);
continue;
}
if (notFirst) {
child.setAttribute('AuxiliaryImplicit', true);
}
else {
notFirst = true;
}
newChildren = collect.concat(newChildren);
}
const rear = [];
const semChildren = semantic.childNodes.map(function (x) {
return x.mathmlTree;
});
while (semChildren.length) {
const schild = semChildren.pop();
if (!schild) {
continue;
}
if (oldChildren.indexOf(schild) !== -1) {
break;
}
if (children.indexOf(schild) !== -1) {
rear.unshift(schild);
}
}
return oldChildren.concat(rear);
}
function collectChildNodes(node, children) {
const collect = [];
let newChildren = DomUtil.toArray(node.childNodes);
while (newChildren.length) {
const child = newChildren.shift();
if (child.nodeType !== DomUtil.NodeType.ELEMENT_NODE) {
continue;
}
if (child.hasAttribute(EnrichAttr.Attribute.TYPE) ||
children.indexOf(child) !== -1) {
collect.push(child);
continue;
}
newChildren = DomUtil.toArray(child.childNodes).concat(newChildren);
}
return collect;
}
function mergeChildren(node, newChildren, semantic) {
if (!newChildren.length)
return;
if (newChildren.length === 1 && node === newChildren[0])
return;
const oldChildren = semantic.role === SemanticRole.IMPLICIT &&
SemanticHeuristics.flags.combine_juxtaposition
? collateChildNodes(node, newChildren, semantic)
: DomUtil.toArray(node.childNodes);
if (!oldChildren.length) {
newChildren.forEach(function (x) {
node.appendChild(x);
});
return;
}
let oldCounter = 0;
while (newChildren.length) {
const newChild = newChildren[0];
if (oldChildren[oldCounter] === newChild ||
functionApplication(oldChildren[oldCounter], newChild)) {
newChildren.shift();
oldCounter++;
continue;
}
if (oldChildren[oldCounter] &&
newChildren.indexOf(oldChildren[oldCounter]) === -1) {
oldCounter++;
continue;
}
if (isDescendant(newChild, node)) {
newChildren.shift();
continue;
}
const oldChild = oldChildren[oldCounter];
if (!oldChild) {
if (newChild.parentNode) {
node = parentNode(newChild);
newChildren.shift();
continue;
}
const nextChild = newChildren[1];
if (nextChild && nextChild.parentNode) {
node = parentNode(nextChild);
node.insertBefore(newChild, nextChild);
newChildren.shift();
newChildren.shift();
continue;
}
node.insertBefore(newChild, null);
newChildren.shift();
continue;
}
insertNewChild(node, oldChild, newChild);
newChildren.shift();
}
}
function insertNewChild(node, oldChild, newChild) {
let parent = oldChild;
let next = parentNode(parent);
while (next &&
next.firstChild === parent &&
!parent.hasAttribute('AuxiliaryImplicit') &&
next !== node) {
parent = next;
next = parentNode(parent);
}
if (next) {
next.insertBefore(newChild, parent);
parent.removeAttribute('AuxiliaryImplicit');
}
}
function isDescendant(child, node) {
if (!child) {
return false;
}
do {
child = parentNode(child);
if (child === node) {
return true;
}
} while (child);
return false;
}
function functionApplication(oldNode, newNode) {
const appl = NamedSymbol.functionApplication;
if (oldNode &&
newNode &&
oldNode.textContent &&
newNode.textContent &&
oldNode.textContent === appl &&
newNode.textContent === appl &&
newNode.getAttribute(EnrichAttr.Attribute.ADDED) === 'true') {
for (let i = 0, attr; (attr = oldNode.attributes[i]); i++) {
if (!newNode.hasAttribute(attr.nodeName)) {
newNode.setAttribute(attr.nodeName, attr.nodeValue);
}
}
DomUtil.replaceNode(oldNode, newNode);
return true;
}
return false;
}
var lcaType;
(function (lcaType) {
lcaType["VALID"] = "valid";
lcaType["INVALID"] = "invalid";
lcaType["PRUNED"] = "pruned";
})(lcaType || (lcaType = {}));
function mathmlLca(children) {
const leftMost = attachedElement(children);
if (!leftMost) {
return { type: lcaType.INVALID, node: null };
}
const rightMost = attachedElement(children.slice().reverse());
if (leftMost === rightMost) {
return { type: lcaType.VALID, node: leftMost };
}
const leftPath = pathToRoot(leftMost);
const newLeftPath = prunePath(leftPath, children);
const rightPath = pathToRoot(rightMost, function (x) {
return newLeftPath.indexOf(x) !== -1;
});
const lca = rightPath[0];
const lIndex = newLeftPath.indexOf(lca);
if (lIndex === -1) {
return { type: lcaType.INVALID, node: null };
}
return {
type: newLeftPath.length !== leftPath.length
? lcaType.PRUNED
: validLca(newLeftPath[lIndex + 1], rightPath[1])
? lcaType.VALID
: lcaType.INVALID,
node: lca
};
}
function prunePath(path, children) {
let i = 0;
while (path[i] && children.indexOf(path[i]) === -1) {
i++;
}
return path.slice(0, i + 1);
}
function attachedElement(nodes) {
let count = 0;
let attached = null;
while (!attached && count < nodes.length) {
if (nodes[count].parentNode) {
attached = nodes[count];
}
count++;
}
return attached;
}
function pathToRoot(node, opt_test) {
const test = opt_test || ((_x) => false);
const path = [node];
while (!test(node) && !SemanticUtil.hasMathTag(node) && node.parentNode) {
node = parentNode(node);
path.unshift(node);
}
return path;
}
function validLca(left, right) {
return !!(left && right && !left.previousSibling && !right.nextSibling);
}
export function ascendNewNode(newNode) {
while (!SemanticUtil.hasMathTag(newNode) && unitChild(newNode)) {
newNode = parentNode(newNode);
}
return newNode;
}
function descendNode(node) {
const children = DomUtil.toArray(node.childNodes);
if (!children) {
return node;
}
const remainder = children.filter(function (child) {
return (child.nodeType === DomUtil.NodeType.ELEMENT_NODE &&
!SemanticUtil.hasIgnoreTag(child));
});
if (remainder.length === 1 &&
SemanticUtil.hasEmptyTag(remainder[0]) &&
!remainder[0].hasAttribute(EnrichAttr.Attribute.TYPE)) {
return descendNode(remainder[0]);
}
return node;
}
function unitChild(node) {
const parent = parentNode(node);
if (!parent || !SemanticUtil.hasEmptyTag(parent)) {
return false;
}
return DomUtil.toArray(parent.childNodes).every(function (child) {
return child === node || isIgnorable(child);
});
}
function isIgnorable(node) {
if (node.nodeType !== DomUtil.NodeType.ELEMENT_NODE) {
return true;
}
if (!node || SemanticUtil.hasIgnoreTag(node)) {
return true;
}
const children = DomUtil.toArray(node.childNodes);
if ((!SemanticUtil.hasEmptyTag(node) && children.length) ||
SemanticUtil.hasDisplayTag(node) ||
node.hasAttribute(EnrichAttr.Attribute.TYPE) ||
SemanticUtil.isOrphanedGlyph(node)) {
return false;
}
return DomUtil.toArray(node.childNodes).every(isIgnorable);
}
function parentNode(element) {
return element.parentNode;
}
export function addCollapsedAttribute(node, collapsed) {
const skeleton = new SemanticSkeleton(collapsed);
node.setAttribute(EnrichAttr.Attribute.COLLAPSED, skeleton.toString());
}
export function cloneContentNode(content) {
if (content.mathml.length) {
return walkTree(content);
}
const clone = SETTINGS.implicit
? createInvisibleOperator(content)
: EnrichAttr.addMrow();
content.mathml = [clone];
return clone;
}
export function rewriteMfenced(mml) {
if (DomUtil.tagName(mml) !== MMLTAGS.MFENCED) {
return mml;
}
const newNode = EnrichAttr.addMrow();
for (let i = 0, attr; (attr = mml.attributes[i]); i++) {
if (['open', 'close', 'separators'].indexOf(attr.name) === -1) {
newNode.setAttribute(attr.name, attr.value);
}
}
DomUtil.toArray(mml.childNodes).forEach(function (x) {
newNode.appendChild(x);
});
DomUtil.replaceNode(mml, newNode);
return newNode;
}
function createInvisibleOperator(operator) {
const moNode = DomUtil.createElement('mo');
const text = DomUtil.createTextNode(operator.textContent);
moNode.appendChild(text);
EnrichAttr.setAttributes(moNode, operator);
moNode.setAttribute(EnrichAttr.Attribute.ADDED, 'true');
return moNode;
}
export function setOperatorAttribute(semantic, content) {
const operator = semantic.type + (semantic.textContent ? ',' + semantic.textContent : '');
content.forEach(function (c) {
getInnerNode(c).setAttribute(EnrichAttr.Attribute.OPERATOR, operator);
});
}
export function getInnerNode(node) {
const children = DomUtil.toArray(node.childNodes);
if (!children) {
return node;
}
const remainder = children.filter(function (child) {
return !isIgnorable(child);
});
const result = [];
for (let i = 0, remain; (remain = remainder[i]); i++) {
if (SemanticUtil.hasEmptyTag(remain) &&
remain.getAttribute(EnrichAttr.Attribute.TYPE) !==
SemanticType.PUNCTUATION) {
const nextInner = getInnerNode(remain);
if (nextInner && nextInner !== remain) {
result.push(nextInner);
}
}
else {
result.push(remain);
}
}
if (result.length === 1) {
return result[0];
}
return node;
}
function formattedOutput(element, name, wiki = false) {
const output = EnrichAttr.removeAttributePrefix(DomUtil.formatXml(element.toString()));
return wiki ? name + ':\n```html\n' + output + '\n```\n' : output;
}
export function collapsePunctuated(semantic, opt_children) {
const optional = !!opt_children;
const children = opt_children || [];
const parent = semantic.parent;
const contentIds = semantic.contentNodes.map(function (x) {
return x.id;
});
contentIds.unshift('c');
const childIds = [semantic.id, contentIds];
for (let i = 0, child; (child = semantic.childNodes[i]); i++) {
const mmlChild = walkTree(child);
children.push(mmlChild);
const innerNode = getInnerNode(mmlChild);
if (parent && !optional) {
innerNode.setAttribute(EnrichAttr.Attribute.PARENT, parent.id.toString());
}
childIds.push(child.id);
}
return childIds;
}