| 'use strict'; |
|
|
| |
| const xmlDecodeTree = new Uint16Array( |
| |
| "\u0200aglq\t\x15\x18\x1b\u026d\x0f\0\0\x12p;\u4026os;\u4027t;\u403et;\u403cuot;\u4022" |
| .split("") |
| .map((c) => c.charCodeAt(0))); |
|
|
| |
| var _a; |
| const decodeMap = new Map([ |
| [0, 65533], |
| |
| [128, 8364], |
| [130, 8218], |
| [131, 402], |
| [132, 8222], |
| [133, 8230], |
| [134, 8224], |
| [135, 8225], |
| [136, 710], |
| [137, 8240], |
| [138, 352], |
| [139, 8249], |
| [140, 338], |
| [142, 381], |
| [145, 8216], |
| [146, 8217], |
| [147, 8220], |
| [148, 8221], |
| [149, 8226], |
| [150, 8211], |
| [151, 8212], |
| [152, 732], |
| [153, 8482], |
| [154, 353], |
| [155, 8250], |
| [156, 339], |
| [158, 382], |
| [159, 376], |
| ]); |
| |
| |
| |
| const fromCodePoint = |
| |
| (_a = String.fromCodePoint) !== null && _a !== void 0 ? _a : function (codePoint) { |
| let output = ""; |
| if (codePoint > 65535) { |
| codePoint -= 65536; |
| output += String.fromCharCode(((codePoint >>> 10) & 1023) | 55296); |
| codePoint = 56320 | (codePoint & 1023); |
| } |
| output += String.fromCharCode(codePoint); |
| return output; |
| }; |
| |
| |
| |
| |
| |
| function replaceCodePoint(codePoint) { |
| var _a; |
| if ((codePoint >= 55296 && codePoint <= 57343) || |
| codePoint > 1114111) { |
| return 65533; |
| } |
| return (_a = decodeMap.get(codePoint)) !== null && _a !== void 0 ? _a : codePoint; |
| } |
|
|
| var CharCodes; |
| (function (CharCodes) { |
| CharCodes[CharCodes["NUM"] = 35] = "NUM"; |
| CharCodes[CharCodes["SEMI"] = 59] = "SEMI"; |
| CharCodes[CharCodes["EQUALS"] = 61] = "EQUALS"; |
| CharCodes[CharCodes["ZERO"] = 48] = "ZERO"; |
| CharCodes[CharCodes["NINE"] = 57] = "NINE"; |
| CharCodes[CharCodes["LOWER_A"] = 97] = "LOWER_A"; |
| CharCodes[CharCodes["LOWER_F"] = 102] = "LOWER_F"; |
| CharCodes[CharCodes["LOWER_X"] = 120] = "LOWER_X"; |
| CharCodes[CharCodes["LOWER_Z"] = 122] = "LOWER_Z"; |
| CharCodes[CharCodes["UPPER_A"] = 65] = "UPPER_A"; |
| CharCodes[CharCodes["UPPER_F"] = 70] = "UPPER_F"; |
| CharCodes[CharCodes["UPPER_Z"] = 90] = "UPPER_Z"; |
| })(CharCodes || (CharCodes = {})); |
| |
| const TO_LOWER_BIT = 32; |
| var BinTrieFlags; |
| (function (BinTrieFlags) { |
| BinTrieFlags[BinTrieFlags["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH"; |
| BinTrieFlags[BinTrieFlags["BRANCH_LENGTH"] = 16256] = "BRANCH_LENGTH"; |
| BinTrieFlags[BinTrieFlags["JUMP_TABLE"] = 127] = "JUMP_TABLE"; |
| })(BinTrieFlags || (BinTrieFlags = {})); |
| function isNumber(code) { |
| return code >= CharCodes.ZERO && code <= CharCodes.NINE; |
| } |
| function isHexadecimalCharacter(code) { |
| return ((code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F) || |
| (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F)); |
| } |
| function isAsciiAlphaNumeric(code) { |
| return ((code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z) || |
| (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z) || |
| isNumber(code)); |
| } |
| |
| |
| |
| |
| |
| |
| function isEntityInAttributeInvalidEnd(code) { |
| return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code); |
| } |
| var EntityDecoderState; |
| (function (EntityDecoderState) { |
| EntityDecoderState[EntityDecoderState["EntityStart"] = 0] = "EntityStart"; |
| EntityDecoderState[EntityDecoderState["NumericStart"] = 1] = "NumericStart"; |
| EntityDecoderState[EntityDecoderState["NumericDecimal"] = 2] = "NumericDecimal"; |
| EntityDecoderState[EntityDecoderState["NumericHex"] = 3] = "NumericHex"; |
| EntityDecoderState[EntityDecoderState["NamedEntity"] = 4] = "NamedEntity"; |
| })(EntityDecoderState || (EntityDecoderState = {})); |
| var DecodingMode; |
| (function (DecodingMode) { |
| |
| DecodingMode[DecodingMode["Legacy"] = 0] = "Legacy"; |
| |
| DecodingMode[DecodingMode["Strict"] = 1] = "Strict"; |
| |
| DecodingMode[DecodingMode["Attribute"] = 2] = "Attribute"; |
| })(DecodingMode || (DecodingMode = {})); |
| |
| |
| |
| class EntityDecoder { |
| constructor( |
| |
| decodeTree, |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| emitCodePoint, |
| |
| errors) { |
| this.decodeTree = decodeTree; |
| this.emitCodePoint = emitCodePoint; |
| this.errors = errors; |
| |
| this.state = EntityDecoderState.EntityStart; |
| |
| this.consumed = 1; |
| |
| |
| |
| |
| |
| |
| this.result = 0; |
| |
| this.treeIndex = 0; |
| |
| this.excess = 1; |
| |
| this.decodeMode = DecodingMode.Strict; |
| } |
| |
| startEntity(decodeMode) { |
| this.decodeMode = decodeMode; |
| this.state = EntityDecoderState.EntityStart; |
| this.result = 0; |
| this.treeIndex = 0; |
| this.excess = 1; |
| this.consumed = 1; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| write(input, offset) { |
| switch (this.state) { |
| case EntityDecoderState.EntityStart: { |
| if (input.charCodeAt(offset) === CharCodes.NUM) { |
| this.state = EntityDecoderState.NumericStart; |
| this.consumed += 1; |
| return this.stateNumericStart(input, offset + 1); |
| } |
| this.state = EntityDecoderState.NamedEntity; |
| return this.stateNamedEntity(input, offset); |
| } |
| case EntityDecoderState.NumericStart: { |
| return this.stateNumericStart(input, offset); |
| } |
| case EntityDecoderState.NumericDecimal: { |
| return this.stateNumericDecimal(input, offset); |
| } |
| case EntityDecoderState.NumericHex: { |
| return this.stateNumericHex(input, offset); |
| } |
| case EntityDecoderState.NamedEntity: { |
| return this.stateNamedEntity(input, offset); |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| stateNumericStart(input, offset) { |
| if (offset >= input.length) { |
| return -1; |
| } |
| if ((input.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) { |
| this.state = EntityDecoderState.NumericHex; |
| this.consumed += 1; |
| return this.stateNumericHex(input, offset + 1); |
| } |
| this.state = EntityDecoderState.NumericDecimal; |
| return this.stateNumericDecimal(input, offset); |
| } |
| addToNumericResult(input, start, end, base) { |
| if (start !== end) { |
| const digitCount = end - start; |
| this.result = |
| this.result * Math.pow(base, digitCount) + |
| Number.parseInt(input.substr(start, digitCount), base); |
| this.consumed += digitCount; |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| stateNumericHex(input, offset) { |
| const startIndex = offset; |
| while (offset < input.length) { |
| const char = input.charCodeAt(offset); |
| if (isNumber(char) || isHexadecimalCharacter(char)) { |
| offset += 1; |
| } |
| else { |
| this.addToNumericResult(input, startIndex, offset, 16); |
| return this.emitNumericEntity(char, 3); |
| } |
| } |
| this.addToNumericResult(input, startIndex, offset, 16); |
| return -1; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| stateNumericDecimal(input, offset) { |
| const startIndex = offset; |
| while (offset < input.length) { |
| const char = input.charCodeAt(offset); |
| if (isNumber(char)) { |
| offset += 1; |
| } |
| else { |
| this.addToNumericResult(input, startIndex, offset, 10); |
| return this.emitNumericEntity(char, 2); |
| } |
| } |
| this.addToNumericResult(input, startIndex, offset, 10); |
| return -1; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| emitNumericEntity(lastCp, expectedLength) { |
| var _a; |
| |
| if (this.consumed <= expectedLength) { |
| (_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed); |
| return 0; |
| } |
| |
| if (lastCp === CharCodes.SEMI) { |
| this.consumed += 1; |
| } |
| else if (this.decodeMode === DecodingMode.Strict) { |
| return 0; |
| } |
| this.emitCodePoint(replaceCodePoint(this.result), this.consumed); |
| if (this.errors) { |
| if (lastCp !== CharCodes.SEMI) { |
| this.errors.missingSemicolonAfterCharacterReference(); |
| } |
| this.errors.validateNumericCharacterReference(this.result); |
| } |
| return this.consumed; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| stateNamedEntity(input, offset) { |
| const { decodeTree } = this; |
| let current = decodeTree[this.treeIndex]; |
| |
| let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; |
| for (; offset < input.length; offset++, this.excess++) { |
| const char = input.charCodeAt(offset); |
| this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char); |
| if (this.treeIndex < 0) { |
| return this.result === 0 || |
| |
| (this.decodeMode === DecodingMode.Attribute && |
| |
| (valueLength === 0 || |
| |
| isEntityInAttributeInvalidEnd(char))) |
| ? 0 |
| : this.emitNotTerminatedNamedEntity(); |
| } |
| current = decodeTree[this.treeIndex]; |
| valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; |
| |
| if (valueLength !== 0) { |
| |
| if (char === CharCodes.SEMI) { |
| return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess); |
| } |
| |
| if (this.decodeMode !== DecodingMode.Strict) { |
| this.result = this.treeIndex; |
| this.consumed += this.excess; |
| this.excess = 0; |
| } |
| } |
| } |
| return -1; |
| } |
| |
| |
| |
| |
| |
| emitNotTerminatedNamedEntity() { |
| var _a; |
| const { result, decodeTree } = this; |
| const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14; |
| this.emitNamedEntityData(result, valueLength, this.consumed); |
| (_a = this.errors) === null || _a === void 0 ? void 0 : _a.missingSemicolonAfterCharacterReference(); |
| return this.consumed; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| emitNamedEntityData(result, valueLength, consumed) { |
| const { decodeTree } = this; |
| this.emitCodePoint(valueLength === 1 |
| ? decodeTree[result] & ~BinTrieFlags.VALUE_LENGTH |
| : decodeTree[result + 1], consumed); |
| if (valueLength === 3) { |
| |
| this.emitCodePoint(decodeTree[result + 2], consumed); |
| } |
| return consumed; |
| } |
| |
| |
| |
| |
| |
| |
| |
| end() { |
| var _a; |
| switch (this.state) { |
| case EntityDecoderState.NamedEntity: { |
| |
| return this.result !== 0 && |
| (this.decodeMode !== DecodingMode.Attribute || |
| this.result === this.treeIndex) |
| ? this.emitNotTerminatedNamedEntity() |
| : 0; |
| } |
| |
| case EntityDecoderState.NumericDecimal: { |
| return this.emitNumericEntity(0, 2); |
| } |
| case EntityDecoderState.NumericHex: { |
| return this.emitNumericEntity(0, 3); |
| } |
| case EntityDecoderState.NumericStart: { |
| (_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed); |
| return 0; |
| } |
| case EntityDecoderState.EntityStart: { |
| |
| return 0; |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| function getDecoder(decodeTree) { |
| let returnValue = ""; |
| const decoder = new EntityDecoder(decodeTree, (data) => (returnValue += fromCodePoint(data))); |
| return function decodeWithTrie(input, decodeMode) { |
| let lastIndex = 0; |
| let offset = 0; |
| while ((offset = input.indexOf("&", offset)) >= 0) { |
| returnValue += input.slice(lastIndex, offset); |
| decoder.startEntity(decodeMode); |
| const length = decoder.write(input, |
| |
| offset + 1); |
| if (length < 0) { |
| lastIndex = offset + decoder.end(); |
| break; |
| } |
| lastIndex = offset + length; |
| |
| offset = length === 0 ? lastIndex + 1 : lastIndex; |
| } |
| const result = returnValue + input.slice(lastIndex); |
| |
| returnValue = ""; |
| return result; |
| }; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| function determineBranch(decodeTree, current, nodeIndex, char) { |
| const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7; |
| const jumpOffset = current & BinTrieFlags.JUMP_TABLE; |
| |
| if (branchCount === 0) { |
| return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1; |
| } |
| |
| if (jumpOffset) { |
| const value = char - jumpOffset; |
| return value < 0 || value >= branchCount |
| ? -1 |
| : decodeTree[nodeIndex + value] - 1; |
| } |
| |
| |
| let lo = nodeIndex; |
| let hi = lo + branchCount - 1; |
| while (lo <= hi) { |
| const mid = (lo + hi) >>> 1; |
| const midValue = decodeTree[mid]; |
| if (midValue < char) { |
| lo = mid + 1; |
| } |
| else if (midValue > char) { |
| hi = mid - 1; |
| } |
| else { |
| return decodeTree[mid + branchCount]; |
| } |
| } |
| return -1; |
| } |
| const xmlDecoder = getDecoder(xmlDecodeTree); |
| |
| |
| |
| |
| |
| |
| function decodeXML(xmlString) { |
| return xmlDecoder(xmlString, DecodingMode.Strict); |
| } |
|
|
| const attrRE = /\s([^'"/\s><]+?)[\s/>]|([^\s=]+)=\s?(".*?"|'.*?')/g; |
|
|
| function stringify$1(tag) { |
| const res = { |
| type: 'tag', |
| name: '', |
| voidElement: false, |
| attribs: {}, |
| children: [] |
| }; |
|
|
| const tagMatch = tag.match(/<\/?([^\s]+?)[/\s>]/); |
| if (tagMatch) { |
| res.name = tagMatch[1]; |
| if (tag.charAt(tag.length - 2) === '/') { |
| res.voidElement = true; |
| } |
|
|
| |
| if (res.name.startsWith('!--')) { |
| const endIndex = tag.indexOf('-->'); |
| return { |
| type: 'comment', |
| comment: endIndex !== -1 ? tag.slice(4, endIndex) : '' |
| } |
| } |
| } |
|
|
| const reg = new RegExp(attrRE); |
| let result = null; |
| for (;;) { |
| result = reg.exec(tag); |
|
|
| if (result === null) { |
| break |
| } |
|
|
| if (!result[0].trim()) { |
| continue |
| } |
|
|
| if (result[1]) { |
| const attr = result[1].trim(); |
| let arr = [attr, '']; |
|
|
| if (attr.indexOf('=') > -1) { |
| arr = attr.split('='); |
| } |
|
|
| res.attribs[arr[0]] = arr[1]; |
| reg.lastIndex--; |
| } else if (result[2]) { |
| res.attribs[result[2]] = result[3].trim().substring(1, result[3].length - 1); |
| } |
| } |
|
|
| return res |
| } |
|
|
| const tagRE = /<[a-zA-Z0-9\-!/](?:"[^"]*"|'[^']*'|[^'">])*>/g; |
| const whitespaceRE = /^\s*$/; |
|
|
| const textContainerNames = ['mtext', 'mi', 'mn', 'mo', 'ms']; |
|
|
| function parse(html, options = {}) { |
| const result = []; |
| const arr = []; |
| let current; |
| let level = -1; |
|
|
| html.replace(tagRE, (tag, index) => { |
| const isOpen = tag.charAt(1) !== '/'; |
| const isComment = tag.startsWith('<!--'); |
| const start = index + tag.length; |
| const nextChar = html.charAt(start); |
| let parent; |
|
|
| if (isComment) { |
| const comment = stringify$1(tag); |
|
|
| |
| if (level < 0) { |
| result.push(comment); |
| return result |
| } |
| parent = arr[level]; |
| parent.children.push(comment); |
| return result |
| } |
|
|
| if (isOpen) { |
| level++; |
|
|
| current = stringify$1(tag); |
| if (current.type === 'tag' && options.components?.[current.name]) { |
| current.type = 'component'; |
| } |
|
|
| if ( |
| textContainerNames.includes(current.name) && |
| !current.voidElement && |
| nextChar && |
| nextChar !== '<' |
| ) { |
| const data = html.slice(start, html.indexOf('<', start)).trim(); |
| current.children.push({ |
| type: 'text', |
| data: options.disableDecode ? data : decodeXML(data) |
| }); |
| } |
|
|
| |
| if (level === 0) { |
| result.push(current); |
| } |
|
|
| parent = arr[level - 1]; |
|
|
| if (parent) { |
| parent.children.push(current); |
| } |
|
|
| arr[level] = current; |
| } |
|
|
| if (!isOpen || current.voidElement) { |
| if (level > -1 && (current.voidElement || current.name === tag.slice(2, -1))) { |
| level--; |
| |
| current = level === -1 ? result : arr[level]; |
| } |
| if ( |
| level > -1 && |
| textContainerNames.includes[arr[level].name] && |
| nextChar !== '<' && |
| nextChar |
| ) { |
| |
| parent = arr[level].children; |
|
|
| |
| |
| const end = html.indexOf('<', start); |
| let data = html.slice(start, end === -1 ? undefined : end); |
| |
| |
| if (whitespaceRE.test(data)) { |
| data = ' '; |
| } |
| |
| |
| |
| |
| if ((end > -1 && level + parent.length >= 0) || data !== ' ') { |
| parent.push({ |
| type: 'text', |
| data: options.disableDecode ? data : decodeXML(data) |
| }); |
| } |
| } |
| } |
| }); |
|
|
| return result |
| } |
|
|
| function attrString(attribs) { |
| const buff = []; |
| for (const key in attribs) { |
| buff.push(`${key}="${attribs[key]}"`); |
| } |
| if (!buff.length) { |
| return '' |
| } |
| return ` ${buff.join(' ')}` |
| } |
|
|
| function escapeXmlText(str) { |
| return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') |
| } |
|
|
| function stringify(buff, doc) { |
| switch (doc.type) { |
| case 'text': |
| return buff + escapeXmlText(doc.data) |
| case 'tag': { |
| const voidElement = |
| doc.voidElement || (!doc.children.length && doc.attribs['xml:space'] !== 'preserve'); |
| buff += `<${doc.name}${doc.attribs ? attrString(doc.attribs) : ''}${voidElement ? '/>' : '>'}`; |
| if (voidElement) { |
| return buff |
| } |
| return `${buff + doc.children.reduce(stringify, '')}</${doc.name}>` |
| } |
| case 'comment': |
| buff += `<!--${doc.comment}-->`; |
| return buff |
| } |
| } |
|
|
| function stringifyDoc(doc) { |
| return doc.reduce((token, rootEl) => token + stringify('', rootEl), '') |
| } |
|
|
| function math(element, targetParent, previousSibling, nextSibling, ancestors) { |
| targetParent.name = 'm:oMath'; |
| targetParent.attribs = { |
| 'xmlns:m': 'http://schemas.openxmlformats.org/officeDocument/2006/math', |
| 'xmlns:w': 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' |
| }; |
| targetParent.type = 'tag'; |
| targetParent.children = []; |
| return targetParent |
| } |
|
|
| function semantics(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| return targetParent |
| } |
|
|
| function menclose(element, targetParent, previousSibling, nextSibling, ancestors) { |
| const type = element.attribs?.notation?.split(' ')[0] || 'longdiv'; |
|
|
| const targetElement = { |
| type: 'tag', |
| name: 'm:e', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| if (type === 'longdiv') { |
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:rad', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:radPr', |
| attribs: {}, |
| children: [{ type: 'tag', name: 'm:degHide', attribs: { 'm:val': 'on' }, children: [] }] |
| }, |
| { type: 'tag', name: 'm:deg', attribs: {}, children: [] }, |
| targetElement |
| ] |
| }); |
| } else { |
| const hide = { |
| t: { type: 'tag', name: 'm:hideTop', attribs: { 'm:val': 'on' }, children: [] }, |
| b: { type: 'tag', name: 'm:hideBot', attribs: { 'm:val': 'on' }, children: [] }, |
| l: { type: 'tag', name: 'm:hideLeft', attribs: { 'm:val': 'on' }, children: [] }, |
| r: { type: 'tag', name: 'm:hideRight', attribs: { 'm:val': 'on' }, children: [] } |
| }; |
| const borderBoxPr = { type: 'tag', name: 'm:borderBoxPr', attribs: {}, children: [] }; |
|
|
| const containerElement = { |
| type: 'tag', |
| name: 'm:borderBox', |
| attribs: {}, |
| children: [] |
| }; |
| switch (type) { |
| case 'actuarial': |
| case 'radical': |
| case 'box': |
| containerElement.children = [targetElement]; |
| break |
| case 'left': |
| case 'roundedbox': |
| borderBoxPr.children = [hide.t, hide.b, hide.r]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'right': |
| case 'circle': |
| borderBoxPr.children = [hide.t, hide.b, hide.l]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'top': |
| borderBoxPr.children = [hide.b, hide.l, hide.r]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'bottom': |
| borderBoxPr.children = [hide.t, hide.l, hide.r]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'updiagonalstrike': |
| borderBoxPr.children = [ |
| hide.t, |
| hide.b, |
| hide.l, |
| hide.r, |
| { type: 'tag', name: 'm:strikeBLTR', attribs: { 'm:val': 'on' }, children: [] } |
| ]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'downdiagonalstrike': |
| borderBoxPr.children = [ |
| hide.t, |
| hide.b, |
| hide.l, |
| hide.r, |
| { type: 'tag', name: 'm:strikeTLBR', attribs: { 'm:val': 'on' }, children: [] } |
| ]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'verticalstrike': |
| borderBoxPr.children = [ |
| hide.t, |
| hide.b, |
| hide.l, |
| hide.r, |
| { type: 'tag', name: 'm:strikeV', attribs: { 'm:val': 'on' }, children: [] } |
| ]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| case 'horizontalstrike': |
| borderBoxPr.children = [ |
| hide.t, |
| hide.b, |
| hide.l, |
| hide.r, |
| { type: 'tag', name: 'm:strikeH', attribs: { 'm:val': 'on' }, children: [] } |
| ]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| default: |
| borderBoxPr.children = [hide.t, hide.b, hide.l, hide.r]; |
| containerElement.children = [borderBoxPr, targetElement]; |
| break |
| } |
| targetParent.children.push(containerElement); |
| } |
| return targetElement |
| } |
|
|
| function mfrac(element, targetParent, previousSibling, nextSibling, ancestors) { |
| if (element.children.length !== 2) { |
| |
| return targetParent |
| } |
|
|
| const numerator = element.children[0]; |
| const denumerator = element.children[1]; |
| const numeratorTarget = { |
| name: 'm:num', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| const denumeratorTarget = { |
| name: 'm:den', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
| walker(numerator, numeratorTarget, false, false, ancestors); |
| walker(denumerator, denumeratorTarget, false, false, ancestors); |
| const lt = element.attribs?.linethickness; |
| const fracType = (lt === '0' || lt === '0px' || lt === '0em' || lt === '0pt') ? 'noBar' : 'bar'; |
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:f', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:fPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:type', |
| attribs: { |
| 'm:val': fracType |
| }, |
| children: [] |
| } |
| ] |
| }, |
| numeratorTarget, |
| denumeratorTarget |
| ] |
| }); |
| |
| } |
|
|
| function mglyph(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| if (element.attribs?.alt) { |
| targetParent.children.push({ |
| type: 'text', |
| data: element.attribs.alt |
| }); |
| } |
| } |
|
|
| function mmultiscripts(element, targetParent, previousSibling, nextSibling, ancestors) { |
| if (element.children.length === 0) { |
| |
| return |
| } |
|
|
| const base = element.children[0]; |
| const postSubs = []; |
| const postSupers = []; |
| const preSubs = []; |
| const preSupers = []; |
| const children = element.children.slice(1); |
| let dividerFound = false; |
| children.forEach((child, index) => { |
| if (child.name === 'mprescripts') { |
| dividerFound = true; |
| } else if (child.name !== 'none') { |
| if (index % 2) { |
| if (dividerFound) { |
| preSubs.push(child); |
| } else { |
| postSupers.push(child); |
| } |
| } else { |
| if (dividerFound) { |
| preSupers.push(child); |
| } else { |
| postSubs.push(child); |
| } |
| } |
| } |
| }); |
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
| const tempTarget = { |
| children: [] |
| }; |
| walker(base, tempTarget, false, false, ancestors); |
| let topTarget = tempTarget.children[0]; |
|
|
| if (postSubs.length || postSupers.length) { |
| const subscriptTarget = { |
| name: 'm:sub', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| postSubs.forEach((subscript) => walker(subscript, subscriptTarget, false, false, ancestors)); |
|
|
| const superscriptTarget = { |
| name: 'm:sup', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| postSupers.forEach((superscript) => |
| walker(superscript, superscriptTarget, false, false, ancestors) |
| ); |
|
|
| const topPostTarget = { |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:e', |
| attribs: {}, |
| children: [topTarget] |
| } |
| ] |
| }; |
| if (postSubs.length && postSupers.length) { |
| topPostTarget.name = 'm:sSubSup'; |
| topPostTarget.children.push(subscriptTarget); |
| topPostTarget.children.push(superscriptTarget); |
| } else if (postSubs.length) { |
| topPostTarget.name = 'm:sSub'; |
| topPostTarget.children.push(subscriptTarget); |
| } else { |
| topPostTarget.name = 'm:sSup'; |
| topPostTarget.children.push(superscriptTarget); |
| } |
| topTarget = topPostTarget; |
| } |
|
|
| if (preSubs.length || preSupers.length) { |
| const preSubscriptTarget = { |
| name: 'm:sub', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| preSubs.forEach((subscript) => walker(subscript, preSubscriptTarget, false, false, ancestors)); |
|
|
| const preSuperscriptTarget = { |
| name: 'm:sup', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| preSupers.forEach((superscript) => |
| walker(superscript, preSuperscriptTarget, false, false, ancestors) |
| ); |
| const topPreTarget = { |
| name: 'm:sPre', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [topTarget] |
| }, |
| preSubscriptTarget, |
| preSuperscriptTarget |
| ] |
| }; |
| topTarget = topPreTarget; |
| } |
| targetParent.children.push(topTarget); |
| |
| } |
|
|
| function mrow(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| |
| const children = element.children || []; |
| if (children.length >= 2) { |
| const first = children[0]; |
| const last = children[children.length - 1]; |
| if (first?.name === 'mo' && first?.attribs?.fence === 'true' && |
| last?.name === 'mo' && last?.attribs?.fence === 'true') { |
| const begChar = first.children?.[0]?.data || '('; |
| const endChar = last.children?.[0]?.data || ')'; |
| const dNode = { |
| type: 'tag', name: 'm:d', attribs: {}, children: [ |
| { type: 'tag', name: 'm:dPr', attribs: {}, children: [ |
| { type: 'tag', name: 'm:begChr', attribs: { 'm:val': begChar }, children: [] }, |
| { type: 'tag', name: 'm:endChr', attribs: { 'm:val': endChar }, children: [] }, |
| { type: 'tag', name: 'm:ctrlPr', attribs: {}, children: [] } |
| ]}, |
| { type: 'tag', name: 'm:e', attribs: {}, children: [] } |
| ] |
| }; |
| targetParent.children.push(dNode); |
| |
| first.skipInWalker = true; |
| last.skipInWalker = true; |
| |
| return dNode.children[1] |
| } |
| } |
| |
| return targetParent |
| } |
|
|
| function mspace(element, targetParent, previousSibling, nextSibling, ancestors) { |
| targetParent.children.push({ |
| name: 'm:r', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:t', |
| type: 'tag', |
| attribs: { |
| 'xml:space': 'preserve' |
| }, |
| children: [ |
| { |
| type: 'text', |
| data: ' ' |
| } |
| ] |
| } |
| ] |
| }); |
| } |
|
|
| function msqrt(element, targetParent, previousSibling, nextSibling, ancestors) { |
| const targetElement = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| targetParent.children.push({ |
| name: 'm:rad', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:radPr', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:degHide', |
| type: 'tag', |
| attribs: { |
| 'm:val': 'on' |
| }, |
| children: [] |
| } |
| ] |
| }, |
| { |
| name: 'm:deg', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }, |
| targetElement |
| ] |
| }); |
| return targetElement |
| } |
|
|
| function mstyle(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| return targetParent |
| } |
|
|
| function getTextContent(node, trim = true) { |
| let returnString = ''; |
| if (node.type === 'text') { |
| let text = node.data.replace(/[\u2062]|[\u200B]/g, ''); |
| if (trim) { |
| text = text.trim(); |
| } |
| returnString += text; |
| } else if (node.children) { |
| node.children.forEach((subNode) => { |
| returnString += getTextContent(subNode, trim); |
| }); |
| } |
| return returnString |
| } |
|
|
| const NARY_REGEXP = /^[\u220f-\u2211]|[\u2229-\u2233]|[\u22c0-\u22c3]$/; |
| const GROW_REGEXP = /^\u220f|\u2211|[\u2229-\u222b]|\u222e|\u222f|\u2232|\u2233|[\u22c0-\u22c3]$/; |
|
|
| function getNary(node) { |
| |
| const text = getTextContent(node); |
| if (NARY_REGEXP.test(text)) { |
| return text |
| } |
| return false |
| } |
|
|
| function getNaryTarget(naryChar, element, type, subHide = false, supHide = false) { |
| const stretchy = element.attribs?.stretchy; |
| const grow = |
| stretchy === 'true' ? '1' : stretchy === 'false' ? '0' : GROW_REGEXP.test(naryChar) ? '1' : '0'; |
| return { |
| type: 'tag', |
| name: 'm:nary', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:naryPr', |
| attribs: {}, |
| children: [ |
| { type: 'tag', name: 'm:chr', attribs: { 'm:val': naryChar }, children: [] }, |
| { type: 'tag', name: 'm:limLoc', attribs: { 'm:val': type }, children: [] }, |
| { type: 'tag', name: 'm:grow', attribs: { 'm:val': grow }, children: [] }, |
| { |
| type: 'tag', |
| name: 'm:subHide', |
| attribs: { 'm:val': subHide ? 'on' : 'off' }, |
| children: [] |
| }, |
| { |
| type: 'tag', |
| name: 'm:supHide', |
| attribs: { 'm:val': supHide ? 'on' : 'off' }, |
| children: [] |
| } |
| ] |
| } |
| ] |
| } |
| } |
|
|
| function addScriptlevel(target, ancestors) { |
| const scriptlevel = ancestors.find((ancestor) => ancestor.attribs?.scriptlevel)?.attribs |
| ?.scriptlevel; |
| if (['0', '1', '2'].includes(scriptlevel)) { |
| target.children.unshift({ |
| type: 'tag', |
| name: 'm:argPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:scrLvl', |
| attribs: { 'm:val': scriptlevel }, |
| children: [] |
| } |
| ] |
| }); |
| } |
| } |
|
|
| function msub(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| if (element.children.length !== 2) { |
| |
| return targetParent |
| } |
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
| const base = element.children[0]; |
| const subscript = element.children[1]; |
|
|
| let topTarget; |
| |
| |
| |
| |
| |
| |
| const naryChar = getNary(base); |
| if ( |
| naryChar && |
| element.attribs?.accent?.toLowerCase() !== 'true' && |
| element.attribs?.accentunder?.toLowerCase() !== 'true' |
| ) { |
| topTarget = getNaryTarget(naryChar, element, 'subSup', false, true); |
| element.isNary = true; |
| } else { |
| |
| const isEmptyBase = base.name === 'mrow' && (!base.children || base.children.length === 0); |
|
|
| if (isEmptyBase) { |
| topTarget = { |
| type: 'tag', |
| name: 'm:sPre', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:sPrePr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:ctrlPr', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| } |
| ] |
| }; |
| element.isPrescript = true; |
| } else { |
| const baseTarget = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| walker(base, baseTarget, false, false, ancestors); |
| topTarget = { |
| type: 'tag', |
| name: 'm:sSub', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:sSubPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:ctrlPr', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| }, |
| baseTarget |
| ] |
| }; |
| } |
| } |
|
|
| const subscriptTarget = { |
| name: 'm:sub', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| walker(subscript, subscriptTarget, false, false, ancestors); |
| topTarget.children.push(subscriptTarget); |
| if (element.isNary) { |
| topTarget.children.push({ type: 'tag', name: 'm:sup', attribs: {}, children: [] }); |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| } |
| |
| if (element.isPrescript) { |
| topTarget.children.push({ type: 'tag', name: 'm:sup', attribs: {}, children: [] }); |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| } |
| targetParent.children.push(topTarget); |
| |
| } |
|
|
| function msubsup(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| if (element.children.length !== 3) { |
| |
| return targetParent |
| } |
|
|
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
|
|
| const base = element.children[0]; |
| const subscript = element.children[1]; |
| const superscript = element.children[2]; |
|
|
| let topTarget; |
| |
| |
| |
| |
| |
| |
| const naryChar = getNary(base); |
| if ( |
| naryChar && |
| element.attribs?.accent?.toLowerCase() !== 'true' && |
| element.attribs?.accentunder?.toLowerCase() !== 'true' |
| ) { |
| topTarget = getNaryTarget(naryChar, element, 'subSup'); |
| element.isNary = true; |
| } else { |
| |
| const isEmptyBase = base.name === 'mrow' && (!base.children || base.children.length === 0); |
|
|
| if (isEmptyBase) { |
| topTarget = { |
| type: 'tag', |
| name: 'm:sPre', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:sPrePr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:ctrlPr', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| } |
| ] |
| }; |
| element.isPrescript = true; |
| } else { |
| |
| const baseTarget = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| walker(base, baseTarget, false, false, ancestors); |
| topTarget = { |
| type: 'tag', |
| name: 'm:sSubSup', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:sSubSupPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:ctrlPr', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| }, |
| baseTarget |
| ] |
| }; |
| } |
| } |
|
|
| const subscriptTarget = { |
| name: 'm:sub', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| const superscriptTarget = { |
| name: 'm:sup', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| walker(subscript, subscriptTarget, false, false, ancestors); |
| walker(superscript, superscriptTarget, false, false, ancestors); |
| topTarget.children.push(subscriptTarget); |
| topTarget.children.push(superscriptTarget); |
| if (element.isNary) { |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| } |
| if (element.isPrescript) { |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| } |
| targetParent.children.push(topTarget); |
| |
| } |
|
|
| function msup(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| if (element.children.length !== 2) { |
| |
| return targetParent |
| } |
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
| const base = element.children[0]; |
| const superscript = element.children[1]; |
|
|
| let topTarget; |
| |
| |
| |
| |
| |
| |
| const naryChar = getNary(base); |
| if ( |
| naryChar && |
| element.attribs?.accent?.toLowerCase() !== 'true' && |
| element.attribs?.accentunder?.toLowerCase() !== 'true' |
| ) { |
| topTarget = getNaryTarget(naryChar, element, 'subSup', true); |
| element.isNary = true; |
| topTarget.children.push({ type: 'tag', name: 'm:sub' }); |
| } else { |
| |
| const isEmptyBase = base.name === 'mrow' && (!base.children || base.children.length === 0); |
|
|
| if (isEmptyBase) { |
| topTarget = { |
| type: 'tag', |
| name: 'm:sPre', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:sPrePr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:ctrlPr', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| } |
| ] |
| }; |
| element.isPrescript = true; |
| } else { |
| const baseTarget = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| walker(base, baseTarget, false, false, ancestors); |
|
|
| topTarget = { |
| type: 'tag', |
| name: 'm:sSup', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:sSupPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:ctrlPr', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| }, |
| baseTarget |
| ] |
| }; |
| } |
| } |
|
|
| const superscriptTarget = { |
| name: 'm:sup', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| walker(superscript, superscriptTarget, false, false, ancestors); |
|
|
| |
| if (element.isPrescript) { |
| topTarget.children.push({ type: 'tag', name: 'm:sub', attribs: {}, children: [] }); |
| } |
| topTarget.children.push(superscriptTarget); |
| if (element.isNary) { |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| } |
| if (element.isPrescript) { |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| } |
| targetParent.children.push(topTarget); |
| |
| } |
|
|
| function mtable(element, targetParent, previousSibling, nextSibling, ancestors) { |
| const cellsPerRowCount = Math.max(...element.children.map((row) => row.children.length)); |
| const targetElement = { |
| name: 'm:m', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:mPr', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:baseJc', |
| type: 'tag', |
| attribs: { |
| 'm:val': 'center' |
| }, |
| children: [] |
| }, |
| { |
| name: 'm:plcHide', |
| type: 'tag', |
| attribs: { |
| 'm:val': 'on' |
| }, |
| children: [] |
| }, |
| { |
| name: 'm:mcs', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:mc', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:mcPr', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:count', |
| type: 'tag', |
| attribs: { |
| 'm:val': cellsPerRowCount.toString() |
| }, |
| children: [] |
| }, |
| { |
| name: 'm:mcJc', |
| type: 'tag', |
| attribs: { |
| 'm:val': 'center' |
| }, |
| children: [] |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| } |
| ] |
| }; |
| targetParent.children.push(targetElement); |
| return targetElement |
| } |
|
|
| function mtd(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| const targetElement = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| targetParent.children.push(targetElement); |
| return targetElement |
| } |
|
|
| function mtr(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| const targetElement = { |
| name: 'm:mr', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| targetParent.children.push(targetElement); |
| return targetElement |
| } |
|
|
| function munderover(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| if (element.children.length !== 3) { |
| |
| return targetParent |
| } |
|
|
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
|
|
| const base = element.children[0]; |
| const underscript = element.children[1]; |
| const overscript = element.children[2]; |
|
|
| |
| |
| |
| |
| |
| |
| const naryChar = getNary(base); |
| if ( |
| naryChar && |
| element.attributes?.accent?.toLowerCase() !== 'true' && |
| element.attributes?.accentunder?.toLowerCase() !== 'true' |
| ) { |
| const topTarget = getNaryTarget(naryChar, element, 'undOvr'); |
| element.isNary = true; |
| const subscriptTarget = { |
| name: 'm:sub', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| const superscriptTarget = { |
| name: 'm:sup', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| walker(underscript, subscriptTarget, false, false, ancestors); |
| walker(overscript, superscriptTarget, false, false, ancestors); |
| topTarget.children.push(subscriptTarget); |
| topTarget.children.push(superscriptTarget); |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| targetParent.children.push(topTarget); |
| return |
| } |
|
|
| |
|
|
| const baseTarget = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| walker(base, baseTarget, false, false, ancestors); |
|
|
| const underscriptTarget = { |
| name: 'm:lim', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| const overscriptTarget = { |
| name: 'm:lim', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| walker(underscript, underscriptTarget, false, false, ancestors); |
| walker(overscript, overscriptTarget, false, false, ancestors); |
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:limUpp', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:e', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:limLow', |
| attribs: {}, |
| children: [baseTarget, underscriptTarget] |
| } |
| ] |
| }, |
| overscriptTarget |
| ] |
| }); |
| |
| } |
|
|
| function getStyle(element, ancestors, previousStyle = {}) { |
| const elAttributes = element.attribs || {}; |
| const color = |
| elAttributes.mathcolor || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.color |
| )?.attribs.color || |
| ''; |
| |
| |
| const size = |
| elAttributes.mathsize || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.mathsize |
| )?.attribs.mathsize || |
| ''; |
| const scriptlevel = |
| elAttributes.scriptlevel || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.scriptlevel |
| )?.attribs.scriptlevel || |
| ''; |
| const background = |
| elAttributes.mathbackground || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.mathbackground |
| )?.attribs.mathbackground || |
| ''; |
| let variant = |
| elAttributes.mathvariant || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.mathvariant |
| )?.attribs.mathvariant || |
| ''; |
| if (variant === 'b-i') { |
| variant = 'bold-italic'; |
| } |
| const fontweight = |
| elAttributes.fontweight || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.fontweight |
| )?.attribs.fontweight || |
| ''; |
| if (fontweight === 'bold' && !['bold', 'bold-italic'].includes(variant)) { |
| if (variant.includes('italic')) { |
| variant = 'bold-italic'; |
| } else { |
| variant = 'bold'; |
| } |
| } else if (fontweight === 'normal' && ['bold', 'bold-italic'].includes(variant)) { |
| if (variant.includes('italic')) { |
| variant = 'italic'; |
| } else { |
| variant = ''; |
| } |
| } |
| const fontstyle = |
| elAttributes.fontstyle || |
| ancestors.find( |
| (element) => element.name === 'mstyle' && element.attribs && element.attribs.fontstyle |
| )?.attribs.fontstyle || |
| ''; |
| if (fontstyle === 'italic' && !['italic', 'bold-italic'].includes(variant)) { |
| if (variant.includes('bold')) { |
| variant = 'bold-italic'; |
| } else { |
| variant = 'italic'; |
| } |
| } else if (fontstyle === 'normal' && ['italic', 'bold-italic'].includes(variant)) { |
| if (variant.includes('bold')) { |
| variant = 'bold'; |
| } else { |
| variant = ''; |
| } |
| } |
| |
| if (!elAttributes.mathvariant) { |
| const textContent = getTextContent(element); |
| if ( |
| previousStyle.variant === '' && |
| ((element.name === 'mi' && textContent.length > 1) || |
| (element.name === 'mn' && !/^\d+\.\d+$/.test(textContent))) |
| ) { |
| variant = ''; |
| } else if ( |
| ['mi', 'mn', 'mo'].includes(element.name) && |
| ['italic', 'bold-italic'].includes(previousStyle.variant) |
| ) { |
| if (fontweight === 'bold') { |
| variant = 'bold-italic'; |
| } else { |
| variant = 'italic'; |
| } |
| } |
| } |
|
|
| return { |
| color, |
| variant, |
| size, |
| scriptlevel, |
| background, |
| fontstyle |
| } |
| } |
|
|
| const STYLES = { |
| bold: 'b', |
| italic: 'i', |
| 'bold-italic': 'bi' |
| }; |
|
|
| function textContainer(element, targetParent, previousSibling, nextSibling, ancestors, textType) { |
| |
|
|
| const hasMglyphChild = element.children?.find((element) => element.name === 'mglyph'); |
| const style = getStyle(element, ancestors, previousSibling?.style); |
| element.style = style; |
| element.hasMglyphChild = hasMglyphChild; |
| const styleSame = |
| Object.keys(style).every((key) => { |
| const previousStyle = previousSibling?.style; |
| return previousStyle && style[key] === previousStyle[key] |
| }) && previousSibling?.hasMglyphChild === hasMglyphChild; |
| const sameGroup = |
| textType === previousSibling?.name || |
| (['mi', 'mn', 'mo'].includes(textType) && ['mi', 'mn', 'mo'].includes(previousSibling?.name)); |
| let targetElement; |
| const lastChild = targetParent.children[targetParent.children.length - 1]; |
| if (sameGroup && styleSame && !hasMglyphChild && lastChild?.name === 'm:r') { |
| targetElement = lastChild.children[lastChild.children.length - 1]; |
| } else { |
| const rElement = { |
| name: 'm:r', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| if (style.variant) { |
| const wrPr = { |
| name: 'w:rPr', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| if (style.variant.includes('bold')) { |
| wrPr.children.push({ name: 'w:b', type: 'tag', attribs: {}, children: [] }); |
| } |
| if (style.variant.includes('italic')) { |
| wrPr.children.push({ name: 'w:i', type: 'tag', attribs: {}, children: [] }); |
| } |
| rElement.children.push(wrPr); |
| const mrPr = { |
| name: 'm:rPr', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:nor', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| }; |
| if (style.variant !== 'italic') { |
| mrPr.children.push({ |
| name: 'm:sty', |
| type: 'tag', |
| attribs: { |
| 'm:val': STYLES[style.variant] |
| }, |
| children: [] |
| }); |
| } |
| rElement.children.push(mrPr); |
| } else if (hasMglyphChild || textType === 'mtext') { |
| rElement.children.push({ |
| name: 'm:rPr', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:nor', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| } |
| ] |
| }); |
| } else if (style.fontstyle === 'normal' || (textType === 'ms' && style.fontstyle === '')) { |
| rElement.children.push({ |
| name: 'm:rPr', |
| type: 'tag', |
| attribs: {}, |
| children: [ |
| { |
| name: 'm:sty', |
| type: 'tag', |
| attribs: { 'm:val': 'p' }, |
| children: [] |
| } |
| ] |
| }); |
| } |
|
|
| targetElement = { |
| name: 'm:t', |
| type: 'tag', |
| attribs: { |
| 'xml:space': 'preserve' |
| }, |
| children: [] |
| }; |
| rElement.children.push(targetElement); |
| targetParent.children.push(rElement); |
| } |
| return targetElement |
| } |
|
|
| function mtext(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return textContainer(element, targetParent, previousSibling, nextSibling, ancestors, 'mtext') |
| } |
|
|
| function mi(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return textContainer(element, targetParent, previousSibling, nextSibling, ancestors, 'mi') |
| } |
|
|
| function mn(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return textContainer(element, targetParent, previousSibling, nextSibling, ancestors, 'mn') |
| } |
|
|
| function mo(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return textContainer(element, targetParent, previousSibling, nextSibling, ancestors, 'mo') |
| } |
|
|
| function ms(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return textContainer(element, targetParent, previousSibling, nextSibling, ancestors, 'ms') |
| } |
|
|
| const UPPER_COMBINATION = { |
| '\u2190': '\u20D6', |
| '\u27F5': '\u20D6', |
| '\u2192': '\u20D7', |
| '\u27F6': '\u20D7', |
| '\u00B4': '\u0301', |
| '\u02DD': '\u030B', |
| '\u02D8': '\u0306', |
| ˇ: '\u030C', |
| '\u00B8': '\u0312', |
| '\u005E': '\u0302', |
| '\u00A8': '\u0308', |
| '\u02D9': '\u0307', |
| '\u0060': '\u0300', |
| '\u002D': '\u0305', |
| '\u00AF': '\u0305', |
| '\u2212': '\u0305', |
| '\u002E': '\u0307', |
| '\u007E': '\u0303', |
| '\u02DC': '\u0303' |
| }; |
|
|
| function underOrOver(element, targetParent, previousSibling, nextSibling, ancestors, direction) { |
| |
|
|
| if (element.children.length !== 2) { |
| |
| return targetParent |
| } |
|
|
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
|
|
| const base = element.children[0]; |
| const script = element.children[1]; |
|
|
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| const naryChar = getNary(base); |
|
|
| if ( |
| naryChar && |
| element.attribs?.accent?.toLowerCase() !== 'true' && |
| element.attribs?.accentunder?.toLowerCase() !== 'true' |
| ) { |
| const topTarget = getNaryTarget( |
| naryChar, |
| element, |
| 'undOvr', |
| direction === 'over', |
| direction === 'under' |
| ); |
| element.isNary = true; |
|
|
| const subscriptTarget = { |
| name: 'm:sub', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| const superscriptTarget = { |
| name: 'm:sup', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| walker( |
| script, |
| direction === 'under' ? subscriptTarget : superscriptTarget, |
| false, |
| false, |
| ancestors |
| ); |
| topTarget.children.push(subscriptTarget); |
| topTarget.children.push(superscriptTarget); |
| topTarget.children.push({ type: 'tag', name: 'm:e', attribs: {}, children: [] }); |
| targetParent.children.push(topTarget); |
| return |
| } |
|
|
| const scriptText = getTextContent(script); |
|
|
| const baseTarget = { |
| name: 'm:e', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
| walker(base, baseTarget, false, false, ancestors); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| if ( |
| (direction === 'under' && script.name === 'mo' && ['\u0332', '\u005F'].includes(scriptText)) || |
| (direction === 'over' && script.name === 'mo' && ['\u0305', '\u00AF'].includes(scriptText)) |
| ) { |
| |
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:bar', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:barPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:pos', |
| attribs: { |
| 'm:val': direction === 'under' ? 'bot' : 'top' |
| }, |
| children: [] |
| } |
| ] |
| }, |
| { |
| type: 'tag', |
| name: 'm:e', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: direction === 'under' ? 'm:sSub' : 'm:sSup', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: direction === 'under' ? 'm:sSubPr' : 'm:sSupPr', |
| attribs: {}, |
| children: [{ type: 'tag', name: 'm:ctrlPr', attribs: {}, children: [] }] |
| }, |
| baseTarget, |
| { type: 'tag', name: 'm:sub', attribs: {}, children: [] } |
| ] |
| } |
| ] |
| } |
| ] |
| }); |
| return |
| } |
|
|
| |
| |
| |
| |
| |
| |
| if ( |
| (direction === 'under' && |
| element.attribs?.accentunder?.toLowerCase() === 'true' && |
| script.name === 'mo' && |
| scriptText.length < 2) || |
| (direction === 'over' && |
| element.attribs?.accent?.toLowerCase() === 'true' && |
| script.name === 'mo' && |
| scriptText.length < 2) |
| ) { |
| |
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:acc', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:accPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:chr', |
| attribs: { |
| 'm:val': UPPER_COMBINATION[scriptText] || scriptText |
| }, |
| children: [] |
| } |
| ] |
| }, |
| baseTarget |
| ] |
| }); |
| return |
| } |
| |
| |
| |
| |
| |
| |
| if ( |
| element.attribs?.accent?.toLowerCase() !== 'true' && |
| element.attribs?.accentunder?.toLowerCase() !== 'true' && |
| script.name === 'mo' && |
| base.name === 'mrow' && |
| scriptText.length === 1 |
| ) { |
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:groupChr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:groupChrPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:chr', |
| attribs: { |
| 'm:val': scriptText, |
| 'm:pos': direction === 'under' ? 'bot' : 'top' |
| }, |
| children: [] |
| } |
| ] |
| }, |
| baseTarget |
| ] |
| }); |
| return |
| } |
| |
|
|
| const scriptTarget = { |
| name: 'm:lim', |
| type: 'tag', |
| attribs: {}, |
| children: [] |
| }; |
|
|
| walker(script, scriptTarget, false, false, ancestors); |
| targetParent.children.push({ |
| type: 'tag', |
| name: direction === 'under' ? 'm:limLow' : 'm:limUpp', |
| attribs: {}, |
| children: [baseTarget, scriptTarget] |
| }); |
| |
| } |
|
|
| function munder(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return underOrOver(element, targetParent, previousSibling, nextSibling, ancestors, 'under') |
| } |
|
|
| function mover(element, targetParent, previousSibling, nextSibling, ancestors) { |
| return underOrOver(element, targetParent, previousSibling, nextSibling, ancestors, 'over') |
| } |
|
|
| function mroot(element, targetParent, previousSibling, nextSibling, ancestors) { |
| |
| if (element.children.length !== 2) { |
| |
| return targetParent |
| } |
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
| const base = element.children[0]; |
| const root = element.children[1]; |
|
|
| const baseTarget = { |
| type: 'tag', |
| name: 'm:e', |
| attribs: {}, |
| children: [] |
| }; |
| walker(base, baseTarget, false, false, ancestors); |
|
|
| const rootTarget = { |
| type: 'tag', |
| name: 'm:deg', |
| attribs: {}, |
| children: [] |
| }; |
| walker(root, rootTarget, false, false, ancestors); |
|
|
| const rootText = getTextContent(root); |
|
|
| targetParent.children.push({ |
| type: 'tag', |
| name: 'm:rad', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:radPr', |
| attribs: {}, |
| children: [ |
| { |
| type: 'tag', |
| name: 'm:degHide', |
| attribs: { 'm:val': rootText.length ? 'off' : 'on' }, |
| children: [] |
| } |
| ] |
| }, |
| rootTarget, |
| baseTarget |
| ] |
| }); |
| } |
|
|
| function text(element, targetParent, previousSibling, nextSibling, ancestors) { |
| let text = element.data.replace(/[\u2062]|[\u200B]/g, ''); |
| if (ancestors.find((element) => ['mi', 'mn', 'mo'].includes(element.name))) { |
| text = text.replace(/\s/g, ''); |
| } else { |
| const ms = ancestors.find((element) => element.name === 'ms'); |
| if (ms) { |
| text = (ms.attribs?.lquote || '"') + text + (ms.attribs?.rquote || '"'); |
| } |
| } |
| if (text.length) { |
| if ( |
| targetParent.children.length && |
| targetParent.children[targetParent.children.length - 1].type === 'text' |
| ) { |
| targetParent.children[targetParent.children.length - 1].data += text; |
| } else { |
| targetParent.children.push({ |
| type: 'text', |
| data: text |
| }); |
| } |
| } |
| return targetParent |
| } |
|
|
| var mathmlHandlers = Object.freeze({ |
| __proto__: null, |
| math: math, |
| menclose: menclose, |
| mfrac: mfrac, |
| mglyph: mglyph, |
| mi: mi, |
| mmultiscripts: mmultiscripts, |
| mn: mn, |
| mo: mo, |
| mover: mover, |
| mroot: mroot, |
| mrow: mrow, |
| ms: ms, |
| mspace: mspace, |
| msqrt: msqrt, |
| mstyle: mstyle, |
| msub: msub, |
| msubsup: msubsup, |
| msup: msup, |
| mtable: mtable, |
| mtd: mtd, |
| mtext: mtext, |
| mtr: mtr, |
| munder: munder, |
| munderover: munderover, |
| semantics: semantics, |
| text: text |
| }); |
|
|
| function walker( |
| element, |
| targetParent, |
| previousSibling = false, |
| nextSibling = false, |
| ancestors = [] |
| ) { |
| if ( |
| !previousSibling && |
| ['m:deg', 'm:den', 'm:e', 'm:fName', 'm:lim', 'm:num', 'm:sub', 'm:sup'].includes( |
| targetParent.name |
| ) |
| ) { |
| |
| |
| |
| |
| |
| |
| |
| addScriptlevel(targetParent, ancestors); |
| } |
| let targetElement; |
| const nameOrType = element.name || element.type; |
| if (mathmlHandlers[nameOrType]) { |
| targetElement = mathmlHandlers[nameOrType]( |
| element, |
| targetParent, |
| previousSibling, |
| nextSibling, |
| ancestors |
| ); |
| } else { |
| if (nameOrType && nameOrType !== 'root') { |
| console.warn(`Type not supported: ${nameOrType}`); |
| } |
|
|
| targetElement = targetParent; |
| } |
|
|
| if (!targetElement) { |
| |
| return |
| } |
| if (element.children?.length) { |
| ancestors = [...ancestors]; |
| ancestors.unshift(element); |
| |
| |
| |
| |
| let naryBodyTarget = null; |
| |
| |
| let prescriptTarget = null; |
| for (let i = 0; i < element.children.length; i++) { |
| const child = element.children[i]; |
| if (child.skipInWalker) continue |
|
|
| |
| |
| |
| |
| |
| if (naryBodyTarget) { |
| if (child.name === 'mo') { |
| const txt = child.children?.[0]?.data; |
| if (txt && /^[=<>≤≥≠≈≡∼≲≳≪≫∈∉⊂⊃⊆⊇⊄⊅≺≻⪯⪰∝≅≃≍≎∥⊥⊢⊣⊨⊩,;:∣]$/.test(txt)) { |
| naryBodyTarget = null; |
| } |
| } else if (child.name === 'mtext') { |
| naryBodyTarget = null; |
| } |
| } |
|
|
| const effectiveTarget = prescriptTarget || naryBodyTarget || targetElement; |
| walker( |
| child, |
| effectiveTarget, |
| element.children[i - 1], |
| element.children[i + 1], |
| ancestors |
| ); |
| if (child.isNary) { |
| |
| const naryNode = effectiveTarget.children[effectiveTarget.children.length - 1]; |
| naryBodyTarget = naryNode.children[naryNode.children.length - 1]; |
| } |
| if (child.isPrescript) { |
| |
| const preNode = effectiveTarget.children[effectiveTarget.children.length - 1]; |
| prescriptTarget = preNode.children[preNode.children.length - 1]; |
| } else if (prescriptTarget) { |
| |
| prescriptTarget = null; |
| } |
| } |
| } |
| } |
|
|
| class MML2OMML { |
| constructor(mmlString, options = {}) { |
| this.inString = mmlString; |
| this.inXML = parse(mmlString, options); |
| this.outXML = false; |
| this.outString = false; |
| } |
|
|
| run() { |
| const outXML = {}; |
| walker({ children: this.inXML, type: 'root' }, outXML); |
| this.outXML = outXML; |
| } |
|
|
| getResult() { |
| this.outString = stringifyDoc([this.outXML]); |
| return this.outString |
| } |
| } |
|
|
| const mml2omml = (mmlString, options) => { |
| const converter = new MML2OMML(mmlString, options); |
| converter.run(); |
| return converter.getResult() |
| }; |
|
|
| exports.mml2omml = mml2omml; |
| |
|
|