Spaces:
No application file
No application file
| import { getNary, getNaryTarget } from '../ooml/index.js' | |
| import { walker } from '../walker.js' | |
| import { getTextContent } from '../helpers.js' | |
| const UPPER_COMBINATION = { | |
| '\u2190': '\u20D6', // arrow left | |
| '\u27F5': '\u20D6', // arrow left, long | |
| '\u2192': '\u20D7', // arrow right | |
| '\u27F6': '\u20D7', // arrow right, long | |
| '\u00B4': '\u0301', // accute | |
| '\u02DD': '\u030B', // accute, double | |
| '\u02D8': '\u0306', // breve | |
| ˇ: '\u030C', // caron | |
| '\u00B8': '\u0312', // cedilla | |
| '\u005E': '\u0302', // circumflex accent | |
| '\u00A8': '\u0308', // diaresis | |
| '\u02D9': '\u0307', // dot above | |
| '\u0060': '\u0300', // grave accent | |
| '\u002D': '\u0305', // hyphen -> overline | |
| '\u00AF': '\u0305', // macron | |
| '\u2212': '\u0305', // minus -> overline | |
| '\u002E': '\u0307', // period -> dot above | |
| '\u007E': '\u0303', // tilde | |
| '\u02DC': '\u0303' // small tilde | |
| } | |
| function underOrOver(element, targetParent, previousSibling, nextSibling, ancestors, direction) { | |
| // Munder/Mover | |
| if (element.children.length !== 2) { | |
| // treat as mrow | |
| return targetParent | |
| } | |
| ancestors = [...ancestors] | |
| ancestors.unshift(element) | |
| const base = element.children[0] | |
| const script = element.children[1] | |
| // Munder/Mover can be translated to ooml in different ways. | |
| // First we check for m:nAry. | |
| // | |
| // m:nAry | |
| // | |
| // Conditions: | |
| // 1. base text must be nary operator | |
| // 2. no accents | |
| 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) | |
| // | |
| // m:bar | |
| // | |
| // Then we check whether it should be an m:bar. | |
| // This happens if: | |
| // 1. The script text is a single character that corresponds to | |
| // \u0332/\u005F (underbar) or \u0305/\u00AF (overbar) | |
| // 2. The type of the script element is mo. | |
| if ( | |
| (direction === 'under' && script.name === 'mo' && ['\u0332', '\u005F'].includes(scriptText)) || | |
| (direction === 'over' && script.name === 'mo' && ['\u0305', '\u00AF'].includes(scriptText)) | |
| ) { | |
| // m:bar | |
| 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 | |
| } | |
| // m:acc | |
| // | |
| // Next we try to see if it is an m:acc. This is the case if: | |
| // 1. The scriptText is 0-1 characters long. | |
| // 2. The script is an mo-element | |
| // 3. The accent is set. | |
| 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) | |
| ) { | |
| // m:acc | |
| 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 | |
| } | |
| // m:groupChr | |
| // | |
| // Now we try m:groupChr. Conditions are: | |
| // 1. Base is an 'mrow' and script is an 'mo'. | |
| // 2. Script length is 1. | |
| // 3. No accent | |
| 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 | |
| } | |
| // Fallback: m:lim | |
| 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] | |
| }) | |
| // Don't iterate over children in the usual way. | |
| } | |
| export function munder(element, targetParent, previousSibling, nextSibling, ancestors) { | |
| return underOrOver(element, targetParent, previousSibling, nextSibling, ancestors, 'under') | |
| } | |
| export function mover(element, targetParent, previousSibling, nextSibling, ancestors) { | |
| return underOrOver(element, targetParent, previousSibling, nextSibling, ancestors, 'over') | |
| } | |