execonvert 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,744 @@
1
+
2
+ import Marcheur from 'marcheur';
3
+ import nodal from 'marcheur/nodal.js';
4
+ import qname from 'marcheur/qname.js';
5
+ import Matcher from 'marcheur/matcher.js';
6
+ import xpath from 'xpath';
7
+ import dom from './dom.js';
8
+ import { oprx } from './operators.js';
9
+
10
+ let MATH_NS = 'http://www.w3.org/1998/Math/MathML'
11
+ , nsMap = {
12
+ m: 'http://schemas.openxmlformats.org/officeDocument/2006/math',
13
+ }
14
+ , select = xpath.useNamespaces(nsMap)
15
+ , selectAttr = (path, attr, ctx, onlyDef = false) => {
16
+ let el = select(path, ctx)[0];
17
+ if (!el) return onlyDef ? undefined : '';
18
+ let atn = qname(attr, nsMap);
19
+ if (atn.ns) return el.getAttributeNS(atn.ns, atn.ln);
20
+ return el.getAttribute(atn.qn);
21
+ }
22
+ , el
23
+ ;
24
+
25
+ export default function omml2mathml (omml) {
26
+ let m = new Matcher(nsMap)
27
+ , walker = new Marcheur()
28
+ ;
29
+ return walker
30
+ .match(m.document(), setup)
31
+ .match(
32
+ m.el('m:oMathPara'),
33
+ (src, out, w) => {
34
+ setup(src, out, w);
35
+ w.res.setAttribute('display', 'block');
36
+ w.walk(out);
37
+ }
38
+ )
39
+ .match(
40
+ m.el('m:oMath'),
41
+ (src, out, w) => {
42
+ setup(src, out, w);
43
+ let p = src.parentNode;
44
+ if (p && p.namespaceURI === nsMap.m && p.localName === 'oMathPara') {
45
+ w.res.setAttribute('display', 'block');
46
+ }
47
+ w.walk(out);
48
+ }
49
+ )
50
+ .match(
51
+ m.el('m:f'),
52
+ (src, out, w) => {
53
+ let type = (selectAttr('./m:fPr[last()]/m:type', 'm:val', src) || '').toLowerCase()
54
+ , outer = (type === 'lin')
55
+ ? el('mrow', {}, out)
56
+ : el('mfrac', fracProp(type), out)
57
+ ;
58
+ let numRow = el('mrow', {}, outer);
59
+ w.walk(numRow, select('m:num[1]', src));
60
+ if (type === 'lin') {
61
+ let mo = el('mo', {}, outer);
62
+ mo.textContent = '/';
63
+ }
64
+ let denRow = el('mrow', {}, outer);
65
+ w.walk(denRow, select('m:den[1]', src));
66
+ }
67
+ )
68
+ .match(
69
+ m.el('m:r'),
70
+ (src, out) => {
71
+ let nor = selectAttr('m:rPr[last()]/m:nor', 'm:val', src) || false;
72
+ if (nor) nor = forceFalse(nor);
73
+ if (nor) {
74
+ let mtext = el('mtext', {}, out);
75
+ mtext.textContent = nbsp(select('.//m:t', src)
76
+ .map(mt => mt.textContent)
77
+ .join(''))
78
+ ;
79
+ }
80
+ else {
81
+ select('.//m:t', src)
82
+ .forEach(mt => {
83
+ parseMT(src, out, {
84
+ toParse: select('./text()', mt).map(t => t.data).join(''),
85
+ scr: selectAttr('../m:rPr[last()]/m:scr', 'm:val', mt),
86
+ sty: selectAttr('../m:rPr[last()]/m:sty', 'm:val', mt),
87
+ nor: false,
88
+ });
89
+ })
90
+ ;
91
+ }
92
+ }
93
+ )
94
+ .match(
95
+ m.el('m:limLow'),
96
+ (src, out, w) => {
97
+ let outer = el('munder', {}, out)
98
+ , row1 = el('mrow', {}, outer)
99
+ , row2 = el('mrow', {}, outer)
100
+ ;
101
+ w.walk(row1, select('m:e[1]', src));
102
+ w.walk(row2, select('m:lim[1]', src));
103
+ }
104
+ )
105
+ .match(
106
+ m.el('m:limUpp'),
107
+ (src, out, w) => {
108
+ let outer = el('mover', {}, out)
109
+ , row1 = el('mrow', {}, outer)
110
+ , row2 = el('mrow', {}, outer)
111
+ ;
112
+ w.walk(row1, select('m:e[1]', src));
113
+ w.walk(row2, select('m:lim[1]', src));
114
+ }
115
+ )
116
+ .match(
117
+ m.el('m:sSub'),
118
+ (src, out, w) => {
119
+ let outer = el('msub', {}, out)
120
+ , row1 = el('mrow', {}, outer)
121
+ , row2 = el('mrow', {}, outer)
122
+ ;
123
+ w.walk(row1, select('m:e[1]', src));
124
+ w.walk(row2, select('m:sub[1]', src));
125
+ }
126
+ )
127
+ .match(
128
+ m.el('m:sSup'),
129
+ (src, out, w) => {
130
+ let outer = el('msup', {}, out)
131
+ , row1 = el('mrow', {}, outer)
132
+ , row2 = el('mrow', {}, outer)
133
+ ;
134
+ w.walk(row1, select('m:e[1]', src));
135
+ w.walk(row2, select('m:sup[1]', src));
136
+ }
137
+ )
138
+ .match(
139
+ m.el('m:sSubSup'),
140
+ (src, out, w) => {
141
+ let outer = el('msubsup', {}, out)
142
+ , row1 = el('mrow', {}, outer)
143
+ , row2 = el('mrow', {}, outer)
144
+ , row3 = el('mrow', {}, outer)
145
+ ;
146
+ w.walk(row1, select('m:e[1]', src));
147
+ w.walk(row2, select('m:sub[1]', src));
148
+ w.walk(row3, select('m:sup[1]', src));
149
+ }
150
+ )
151
+ .match(
152
+ m.el('m:sPre'),
153
+ (src, out, w) => {
154
+ let outer = el('mmultiscripts', {}, out)
155
+ , row = el('mrow', {}, outer)
156
+ ;
157
+ w.walk(row, select('m:e[1]', src));
158
+ el('mprescripts', {}, outer);
159
+ outputScript(w, outer, select('m:sub[1]', src));
160
+ outputScript(w, outer, select('m:sup[1]', src));
161
+ }
162
+ )
163
+ .match(
164
+ m.el('m:m'),
165
+ (src, out, w) => {
166
+ let mcjc = selectAttr('m:mPr[last()]/m:mcs/m:mc/m:mcPr[last()]/m:mcJc', 'm:val', src)
167
+ .toLowerCase()
168
+ , outer = el('mtable', (mcjc && mcjc !== 'center') ? { columnalign: mcjc } : {}, out)
169
+ ;
170
+ select('m:mr', src)
171
+ .forEach(mr => {
172
+ let mtr = el('mtr', {}, outer);
173
+ select('m:e', mr)
174
+ .forEach(me => {
175
+ let mtd = el('mtd', {}, mtr);
176
+ w.walk(mtd, me);
177
+ })
178
+ ;
179
+ })
180
+ ;
181
+ }
182
+ )
183
+ .match(
184
+ m.el('m:rad'),
185
+ (src, out, w) => {
186
+ let degHide = selectAttr('m:radPr[last()]/m:degHide', 'm:val', src) || false;
187
+ if (degHide) degHide = forceFalse(degHide);
188
+ if (degHide) {
189
+ let msqrt = el('msqrt', {}, out);
190
+ w.walk(msqrt, select('m:e[1]', src));
191
+ }
192
+ else {
193
+ let outer = el('mroot', {}, out)
194
+ , row1 = el('mrow', {}, outer)
195
+ , row2 = el('mrow', {}, outer)
196
+ ;
197
+ w.walk(row1, select('m:e[1]', src));
198
+ w.walk(row2, select('m:deg[1]', src));
199
+ }
200
+ }
201
+ )
202
+ .match(
203
+ m.el('m:nary'),
204
+ (src, out, w) => {
205
+ let subHide = selectAttr('m:naryPr[last()]/m:subHide', 'm:val', src) || false;
206
+ if (subHide) subHide = forceFalse(subHide);
207
+ let supHide = selectAttr('m:naryPr[last()]/m:supHide', 'm:val', src) || false;
208
+ if (supHide) supHide = forceFalse(supHide);
209
+ let limLocSubSup = selectAttr('m:naryPr[last()]/m:limLoc', 'm:val', src).toLowerCase();
210
+ limLocSubSup = (limLocSubSup === '' || limLocSubSup === 'subsup');
211
+ let grow = selectAttr('m:naryPr[last()]/m:grow', 'm:val', src) || false;
212
+ if (grow) grow = forceFalse(grow);
213
+
214
+ let mrow = el('mrow', {}, out);
215
+ if (supHide && subHide) {
216
+ outputNAryMO(src, mrow, src, grow);
217
+ }
218
+ else if (subHide) {
219
+ let outer = el(limLocSubSup ? 'msup' : 'mover', {}, mrow);
220
+ outputNAryMO(src, outer, src, grow);
221
+ let subrow = el('mrow', {}, outer);
222
+ w.walk(subrow, select('m:sup[1]', src));
223
+ }
224
+ else if (supHide) {
225
+ let outer = el(limLocSubSup ? 'msub' : 'munder', {}, mrow);
226
+ outputNAryMO(src, outer, src, grow);
227
+ let subrow = el('mrow', {}, outer);
228
+ w.walk(subrow, select('m:sub[1]', src));
229
+ }
230
+ else {
231
+ let outer = el(limLocSubSup ? 'msubsup' : 'munderover', {}, mrow);
232
+ outputNAryMO(src, outer, src, grow);
233
+ let subrow1 = el('mrow', {}, outer)
234
+ , subrow2 = el('mrow', {}, outer)
235
+ ;
236
+ w.walk(subrow1, select('m:sub[1]', src));
237
+ w.walk(subrow2, select('m:sup[1]', src));
238
+ }
239
+ let erow = el('mrow', {}, mrow);
240
+ w.walk(erow, select('m:e[1]', src));
241
+ }
242
+ )
243
+ .match(
244
+ m.el('m:d'),
245
+ (src, out, w) => {
246
+ let attr = {}
247
+ , begChr = selectAttr('m:dPr[1]/m:begChr', 'm:val', src, true)
248
+ , endChr = selectAttr('m:dPr[1]/m:endChr', 'm:val', src, true)
249
+ , sepChr = selectAttr('m:dPr[1]/m:sepChr', 'm:val', src) || '|'
250
+ ;
251
+ if (typeof begChr !== 'undefined' && begChr !== '(') attr.open = begChr;
252
+ if (typeof endChr !== 'undefined' && endChr !== ')') attr.close = endChr;
253
+ if (sepChr !== ',') attr.separators = sepChr;
254
+ let mfenced = el('mfenced', attr, out);
255
+ select('m:e', src).forEach(me => {
256
+ let row = el('mrow', {}, mfenced);
257
+ w.walk(row, me);
258
+ });
259
+ }
260
+ )
261
+ .match(
262
+ m.el('m:eqArr'),
263
+ (src, out, w) => {
264
+ let mtable = el('mtable', {}, out);
265
+ select('m:e', src)
266
+ .forEach(me => {
267
+ let mtr = el('mtr', {}, mtable)
268
+ , mtd = el('mtd', {}, mtr)
269
+ , scrLvl = selectAttr('m:argPr[last()]/m:scrLvl', 'm:val', me)
270
+ , outer
271
+ ;
272
+ if (scrLvl !== '0' || !scrLvl) outer = el('mrow', {}, mtd);
273
+ else outer = el('mstyle', { scriptlevel: scrLvl }, mtd);
274
+ el('maligngroup', {}, outer);
275
+ createEqArrRow(w, src, outer, 1, select('*[1]', me)[0]);
276
+ })
277
+ ;
278
+ }
279
+ )
280
+ .match(
281
+ m.el('m:func'),
282
+ (src, out, w) => {
283
+ let outer = el('mrow', {}, out)
284
+ , row1 = el('mrow', {}, outer)
285
+ ;
286
+ select('m:fName', src).forEach(fn => w.walk(row1, fn));
287
+ let mo = el('mo', {}, outer);
288
+ mo.textContent = '\u2061';
289
+ let row2 = el('mrow', {}, outer);
290
+ w.walk(row2, select('m:e', src));
291
+ }
292
+ )
293
+ .match(
294
+ m.el('m:acc'),
295
+ (src, out, w) => {
296
+ let mover = el('mover', { accent: 'true' }, out)
297
+ , row = el('mrow', {}, mover)
298
+ , acc = selectAttr('m:accPr/m:chr', 'm:val', src).substr(0, 1) || '\u0302'
299
+ , nonComb = toNonCombining(acc)
300
+ ;
301
+ w.walk(row, select('m:e[1]', src));
302
+ if (acc.length === 0) {
303
+ el('mo', {}, mover);
304
+ }
305
+ else {
306
+ let nor = selectAttr('m:rPr[last()]/m:nor', 'm:val', src) || false;
307
+ if (nor) nor = forceFalse(nor);
308
+ parseMT(src, mover, {
309
+ toParse: nonComb,
310
+ scr: selectAttr('m:e[1]/*/m:rPr[last()]/m:scr', 'm:val', src),
311
+ sty: selectAttr('m:e[1]/*/m:rPr[last()]/m:sty', 'm:val', src),
312
+ nor,
313
+ });
314
+ }
315
+ }
316
+ )
317
+ .match(
318
+ m.el('m:groupChr'),
319
+ (src, out, w) => {
320
+ let lastGroupChrPr = select('m:groupChrPr[last()]', src)[0]
321
+ , pos = selectAttr('m:pos', 'm:val', lastGroupChrPr).toLowerCase()
322
+ , vertJc = selectAttr('m:vertJc', 'm:val', lastGroupChrPr).toLowerCase()
323
+ , lastChrVal = selectAttr('m:chr', 'm:val', lastGroupChrPr)
324
+ , chr = lastChrVal ? lastChrVal.substr(0, 1) : '\u23DF'
325
+ , mkMrow = (parent) => {
326
+ let mrow = el('mrow', {}, parent);
327
+ w.walk(mrow, select('m:e[1]', src));
328
+ }
329
+ , mkMo = (parent) => {
330
+ let mo = el('mo', {}, parent);
331
+ mo.textContent = chr;
332
+ }
333
+ ;
334
+ if (pos === 'top') {
335
+ if (vertJc === 'bot') {
336
+ let outer = el('mover', { accent: 'false' }, out);
337
+ mkMrow(outer);
338
+ mkMo(outer);
339
+ }
340
+ else {
341
+ let outer = el('munder', { accentunder: 'false' }, out);
342
+ mkMo(outer);
343
+ mkMrow(outer);
344
+ }
345
+ }
346
+ else {
347
+ if (vertJc === 'bot') {
348
+ let outer = el('mover', { accent: 'false' }, out);
349
+ mkMo(outer);
350
+ mkMrow(outer);
351
+ }
352
+ else {
353
+ let outer = el('munder', { accentunder: 'false' }, out);
354
+ mkMrow(outer);
355
+ mkMo(outer);
356
+ }
357
+ }
358
+ }
359
+ )
360
+ .match(
361
+ m.el('m:borderBox'),
362
+ (src, out, w) => {
363
+ let hideTop = forceTrue(selectAttr('m:borderBoxPr[last()]/m:hideTop[last()]', 'm:val', src)
364
+ .toLowerCase())
365
+ , hideBot = forceTrue(selectAttr('m:borderBoxPr[last()]/m:hideBot[last()]', 'm:val', src)
366
+ .toLowerCase())
367
+ , hideLeft = forceTrue(selectAttr('m:borderBoxPr[last()]/m:hideLeft[last()]',
368
+ 'm:val', src).toLowerCase())
369
+ , hideRight = forceTrue(selectAttr('m:borderBoxPr[last()]/m:hideRight[last()]',
370
+ 'm:val', src).toLowerCase())
371
+ , strikeH = forceTrue(selectAttr('m:borderBoxPr[last()]/m:strikeH[last()]', 'm:val', src)
372
+ .toLowerCase())
373
+ , strikeV = forceTrue(selectAttr('m:borderBoxPr[last()]/m:strikeV[last()]', 'm:val', src)
374
+ .toLowerCase())
375
+ , strikeBLTR = forceTrue(selectAttr('m:borderBoxPr[last()]/m:strikeBLTR[last()]',
376
+ 'm:val', src).toLowerCase())
377
+ , strikeTLBR = forceTrue(selectAttr('m:borderBoxPr[last()]/m:strikeTLBR[last()]',
378
+ 'm:val', src).toLowerCase())
379
+ , outer
380
+ ;
381
+ if (hideTop && hideBot && hideLeft && hideRight &&
382
+ !strikeH && !strikeV && !strikeBLTR && !strikeTLBR) {
383
+ outer = el('mrow', {}, out);
384
+ }
385
+ else {
386
+ outer = el('menclose', createMEnclodeNotation({
387
+ hideTop, hideBot, hideLeft, hideRight, strikeH, strikeV, strikeBLTR, strikeTLBR,
388
+ }), out);
389
+ }
390
+ w.walk(outer, select('m:e[1]', src));
391
+ }
392
+ )
393
+ .match(
394
+ m.el('m:bar'),
395
+ (src, out, w) => {
396
+ let pos = selectAttr('m:barPr/m:pos', 'm:val', src).toLowerCase();
397
+ if (pos === 'top') {
398
+ let outer = el('mover', { accent: 'false' }, out)
399
+ , row = el('mrow', {}, outer)
400
+ , mo = el('mo', {}, outer)
401
+ ;
402
+ w.walk(row, select('m:e[1]', src));
403
+ mo.textContent = '\u00af';
404
+ }
405
+ else {
406
+ let outer = el('munder', { underaccent: 'false' }, out)
407
+ , row = el('mrow', {}, outer)
408
+ , mo = el('mo', {}, outer)
409
+ ;
410
+ w.walk(row, select('m:e[1]', src));
411
+ mo.textContent = '\u005f';
412
+ }
413
+ }
414
+ )
415
+ .match(
416
+ [m.el('m:e'), m.el('m:den'), m.el('m:num'), m.el('m:lim'), m.el('m:sup'), m.el('m:sub')],
417
+ (src, out, w) => {
418
+ let scriptlevel = selectAttr('m:argPr[last()]/m:scrLvl', 'm:val', src);
419
+ if (!scriptlevel) {
420
+ w.walk(out);
421
+ }
422
+ else {
423
+ let style = el('mstyle', { scriptlevel }, out);
424
+ w.walk(style);
425
+ }
426
+ }
427
+ )
428
+ .match(
429
+ m.el('m:phant'),
430
+ (src, out, w) => {
431
+ let zeroWid = selectAttr('m:phantPr[last()]/m:zeroWid[last()]', 'm:val', src)
432
+ .toLowerCase() || false;
433
+ if (zeroWid) zeroWid = forceFalse(zeroWid);
434
+ let zeroAsc = selectAttr('m:phantPr[last()]/m:zeroAsc[last()]', 'm:val', src)
435
+ .toLowerCase() || false;
436
+ if (zeroAsc) zeroAsc = forceFalse(zeroAsc);
437
+ let zeroDesc = selectAttr('m:phantPr[last()]/m:zeroDesc[last()]', 'm:val', src)
438
+ .toLowerCase() || false;
439
+ if (zeroDesc) zeroDesc = forceFalse(zeroDesc);
440
+ let showVal = forceFalse(selectAttr('m:phantPr[last()]/m:show[last()]', 'm:val', src)
441
+ .toLowerCase());
442
+ let parent;
443
+ if (showVal) {
444
+ parent = el('mpadded', createMPaddedAttr({ zeroWid, zeroAsc, zeroDesc }), out);
445
+ }
446
+ else if (!zeroWid && !zeroAsc && !zeroDesc) {
447
+ parent = el('mphantom', {}, out);
448
+ }
449
+ else {
450
+ let phant = el('mphantom', {}, out);
451
+ parent = el('mpadded', createMPaddedAttr({ zeroWid, zeroAsc, zeroDesc }), phant);
452
+ }
453
+ let row = el('mrow', {}, parent);
454
+ w.walk(row, select('m:e', src));
455
+ }
456
+ )
457
+ .run(omml)
458
+ ;
459
+ };
460
+
461
+ function fracProp (type) {
462
+ if (type === 'skw' || type === 'lin') return { bevelled: 'true' };
463
+ if (type === 'nobar') return { linethickness: '0pt' };
464
+ return {};
465
+ // TODO: the original XSLT had traces of trying to set `numalign` on both numerator and
466
+ // denominator, but the variables were never properly defined and could absolutely not match
467
+ }
468
+
469
+ function nbsp (str) {
470
+ if (!str) return;
471
+ return str.replace(/\s/g, '\u00a0');
472
+ }
473
+
474
+ function tf (str) {
475
+ if (str == null) return;
476
+ str = str.toLowerCase();
477
+ if (str === 'on' || str === '1' || str === 'true') return true;
478
+ if (str === 'off' || str === '0' || str === 'false') return false;
479
+ }
480
+
481
+ function forceFalse (str) {
482
+ let res = tf(str);
483
+ if (res === false) return false;
484
+ return true;
485
+ }
486
+
487
+ function forceTrue (str) {
488
+ return tf(str) || false;
489
+ }
490
+
491
+ function parseMT (ctx, out, { toParse = '', scr, sty, nor }) {
492
+ if (!toParse.length) return;
493
+ let firstOper = rxIndexOf(toParse, oprx)
494
+ , firstNum = rxIndexOf(toParse, /\d/)
495
+ , startsWithOper = (firstOper === 1)
496
+ , startsWithNum = (firstNum === 1)
497
+ ;
498
+ if (!startsWithOper && !startsWithNum) {
499
+ let charToPrint;
500
+ if (select('ancestor::m:fName', ctx)[0]) {
501
+ if (!firstOper && !firstNum) charToPrint = toParse.length;
502
+ else charToPrint = Math.min(firstOper || Number.MAX_VALUE, firstNum || Number.MAX_VALUE) - 1;
503
+ }
504
+ else charToPrint = 1;
505
+ let mi = el('mi', tokenAttributes({ scr, sty, nor, charToPrint, tokenType: 'mi' }), out);
506
+ mi.textContent = nbsp(toParse.substr(0, charToPrint));
507
+ parseMT(ctx, out, { toParse: toParse.substr(charToPrint), scr, sty, nor });
508
+ }
509
+ else if (startsWithOper) {
510
+ let mo = el('mo', tokenAttributes({ nor, tokenType: 'mo' }), out);
511
+ mo.textContent = toParse.substr(0, 1);
512
+ parseMT(ctx, out, { toParse: toParse.substr(1), scr, sty, nor });
513
+ }
514
+ else {
515
+ let num = numStart(toParse)
516
+ , mn = el('mn', tokenAttributes({ scr, sty: 'p', nor, tokenType: 'mn' }), out)
517
+ ;
518
+ mn.textContent = num;
519
+ parseMT(ctx, out, { toParse: toParse.substr(num.length), scr, sty, nor });
520
+ }
521
+ }
522
+
523
+ function parseEqArrMr (ctx, out, { toParse = '', scr, sty, nor, align }) {
524
+ if (!toParse.length) return;
525
+ if (toParse[0] === '&') {
526
+ el(align ? 'malignmark' : 'maligngroup', {}, out);
527
+ parseEqArrMr(ctx, out, {
528
+ toParse: toParse.substr(1),
529
+ align: !align,
530
+ scr, sty, nor,
531
+ });
532
+ }
533
+ else {
534
+ let firstOper = rxIndexOf(toParse, oprx)
535
+ , firstNum = rxIndexOf(toParse, /\d/)
536
+ , startsWithOper = (firstOper === 1)
537
+ , startsWithNum = (firstNum === 1)
538
+ ;
539
+ if (!startsWithOper && !startsWithNum) {
540
+ if (!nor) {
541
+ let mi = el('mi', tokenAttributes({ scr, sty, nor, charToPrint: 1, tokenType: 'mi' }), out);
542
+ mi.textContent = nbsp(toParse.substr(0, 1));
543
+ }
544
+ else {
545
+ let mt = el('mtext', {}, out);
546
+ mt.textContent = nbsp(toParse.substr(0, 1));
547
+ }
548
+ parseEqArrMr(ctx, out, { toParse: toParse.substr(1), scr, sty, nor, align });
549
+ }
550
+ else if (startsWithOper) {
551
+ if (!nor) {
552
+ let mo = el('mo', tokenAttributes({ nor, charToPrint: 1, tokenType: 'mo' }), out);
553
+ mo.textContent = toParse.substr(0, 1);
554
+ }
555
+ else {
556
+ let mt = el('mtext', {}, out);
557
+ mt.textContent = toParse.substr(0, 1);
558
+ }
559
+ parseEqArrMr(ctx, out, { toParse: toParse.substr(1), scr, sty, nor, align });
560
+ }
561
+ else {
562
+ let num = numStart(toParse);
563
+ if (!nor) {
564
+ let mn = el('mn', tokenAttributes({ sty: 'p', nor, charToPrint: 1, tokenType: 'mn' }), out);
565
+ mn.textContent = toParse.substr(0, num.length);
566
+ }
567
+ else {
568
+ let mt = el('mtext', {}, out);
569
+ mt.textContent = toParse.substr(0, num.length);
570
+ }
571
+ parseEqArrMr(ctx, out, { toParse: toParse.substr(num.length), scr, sty, nor, align });
572
+ }
573
+ }
574
+ }
575
+
576
+ function rxIndexOf (str, rx) {
577
+ let re = rx.exec(str);
578
+ if (!re) return 0;
579
+ return re.index + 1;
580
+ }
581
+
582
+ function tokenAttributes ({ scr, sty, nor, charToPrint = 0, tokenType }) {
583
+ let attr = {};
584
+ if (nor) attr.mathvariant = 'normal';
585
+ else {
586
+ let mathvariant
587
+ , fontweight = (sty === 'b' || sty === 'bi') ? 'bold' : 'normal'
588
+ , fontstyle = (sty === 'b' || sty === 'p') ? 'normal' : 'italic'
589
+ ;
590
+ if (tokenType !== 'mn') {
591
+ if (scr === 'monospace') mathvariant = 'monospace';
592
+ else if (scr === 'sans-serif' && sty === 'i') mathvariant = 'sans-serif-italic';
593
+ else if (scr === 'sans-serif' && sty === 'b') mathvariant = 'bold-sans-serif';
594
+ else if (scr === 'sans-serif' && sty === 'bi') mathvariant = 'sans-serif-bold-italic';
595
+ else if (scr === 'sans-serif') mathvariant = 'sans-serif';
596
+ else if (scr === 'fraktur' && (sty === 'b' || sty === 'i')) mathvariant = 'bold-fraktur';
597
+ else if (scr === 'fraktur') mathvariant = 'fraktur';
598
+ else if (scr === 'double-struck') mathvariant = 'double-struck';
599
+ else if (scr === 'script' && (sty === 'b' || sty === 'i')) mathvariant = 'bold-script';
600
+ else if (scr === 'script') mathvariant = 'script';
601
+ else if (scr === 'roman' || !scr) {
602
+ if (sty === 'b') mathvariant = 'bold';
603
+ else if (sty === 'i') mathvariant = 'italic';
604
+ else if (sty === 'p') mathvariant = 'normal';
605
+ else if (sty === 'bi') mathvariant = 'bold-italic';
606
+ }
607
+ }
608
+ if (tokenType === 'mo' && mathvariant !== 'normal') return attr;
609
+ if (tokenType === 'mi' && charToPrint === 1 && (mathvariant === 'italic' || !mathvariant)) {
610
+ return attr;
611
+ }
612
+ if (tokenType === 'mi' && charToPrint > 1 && (mathvariant === 'italic' || !mathvariant)) {
613
+ attr.mathvariant = 'italic';
614
+ }
615
+ else if (mathvariant && mathvariant !== 'italic') {
616
+ attr.mathvariant = mathvariant;
617
+ }
618
+ else {
619
+ if (fontstyle === 'italic' && !(tokenType === 'mi' && charToPrint === 1)) {
620
+ attr.fontstyle = 'italic';
621
+ }
622
+ if (fontweight === 'bold') attr.fontweight = 'bold';
623
+ }
624
+ }
625
+ return attr;
626
+ }
627
+
628
+ function numStart (str) {
629
+ if (!str) return '';
630
+ let ret = '';
631
+ str.replace(/^(\d+)/, (_, m) => {
632
+ ret = m;
633
+ });
634
+ return ret;
635
+ }
636
+
637
+ function outputScript (w, out, cur) {
638
+ if (cur && cur.length) {
639
+ let row = el('mrow', {}, out);
640
+ w.walk(row, cur);
641
+ }
642
+ else el('none', {}, out);
643
+ }
644
+
645
+ function outputNAryMO (src, out, cur, grow = false) {
646
+ let mo = el('mo', { stretchy: grow ? 'true' : 'false' }, out)
647
+ , val = selectAttr('./m:naryPr[last()]/m:chr', 'm:val', src)
648
+ ;
649
+ mo.textContent = val || '\u222b';
650
+ }
651
+
652
+ function createEqArrRow (w, src, out, align, cur) {
653
+ let allMt = select('m:t', cur).map(mt => mt.textContent).join('');
654
+ if (select('self::m:r', cur)[0]) {
655
+ let nor = selectAttr('m:rPr[last()]/m:nor', 'm:val', src) || false;
656
+ if (nor) nor = forceFalse(nor);
657
+ parseEqArrMr(src, out, {
658
+ toParse: allMt,
659
+ scr: selectAttr('../m:rPr[last()]/m:scr', 'm:val', src),
660
+ sty: selectAttr('../m:rPr[last()]/m:sty', 'm:val', src),
661
+ nor,
662
+ align,
663
+ });
664
+ }
665
+ else {
666
+ w.walk(out, cur);
667
+ }
668
+ if (select('following-sibling::*', cur).length) {
669
+ let amp = countAmp(allMt);
670
+ createEqArrRow(w, src, out, (align + (amp % 2)) % 2, select('following-sibling::*', cur)[0]);
671
+ }
672
+ }
673
+
674
+ function countAmp (allMt) {
675
+ return ((allMt || '').match(/&/g) || []).length;
676
+ }
677
+
678
+ let combiMap = {
679
+ '\u0306': '\u02D8',
680
+ '\u032e': '\u02D8',
681
+ '\u0312': '\u00B8',
682
+ '\u0327': '\u00B8',
683
+ '\u0300': '\u0060',
684
+ '\u0316': '\u0060',
685
+ '\u0305': '\u002D',
686
+ '\u0332': '\u002D',
687
+ '\u0323': '\u002E',
688
+ '\u0307': '\u02D9',
689
+ '\u030B': '\u02DD',
690
+ '\u0317': '\u00B4',
691
+ '\u0301': '\u00B4',
692
+ '\u0330': '\u007E',
693
+ '\u0303': '\u007E',
694
+ '\u0324': '\u00A8',
695
+ '\u0308': '\u00A8',
696
+ '\u032C': '\u02C7',
697
+ '\u030C': '\u02C7',
698
+ '\u0302': '\u005E',
699
+ '\u032D': '\u005E',
700
+ '\u20D7': '\u2192',
701
+ '\u20EF': '\u2192',
702
+ '\u20D6': '\u2190',
703
+ '\u20EE': '\u2190',
704
+ };
705
+ function toNonCombining (ch) {
706
+ return combiMap[ch] || ch;
707
+ }
708
+
709
+ function createMEnclodeNotation ({ hideTop, hideBot, hideLeft, hideRight, strikeH, strikeV,
710
+ strikeBLTR, strikeTLBR }) {
711
+ let notation = [];
712
+ if (!hideTop && !hideBot && !hideLeft && !hideRight) notation.push('box');
713
+ else {
714
+ if (!hideTop) notation.push('top');
715
+ if (!hideBot) notation.push('bottom');
716
+ if (!hideLeft) notation.push('left');
717
+ if (!hideRight) notation.push('right');
718
+ }
719
+ if (strikeH) notation.push('horizontalstrike');
720
+ if (strikeV) notation.push('verticalstrike');
721
+ if (strikeBLTR) notation.push('updiagonalstrike');
722
+ if (strikeTLBR) notation.push('downdiagonalstrike');
723
+ return { notation: notation.join(' ') };
724
+ }
725
+
726
+ function createMPaddedAttr ({ zeroWid, zeroAsc, zeroDesc }) {
727
+ let attr = {};
728
+ if (zeroWid) attr.width = '0in';
729
+ if (zeroAsc) attr.height = '0in';
730
+ if (zeroDesc) attr.depth = '0in';
731
+ return attr;
732
+ }
733
+
734
+ function setup (src, out, w) {
735
+ if (w.res) return;
736
+ let doc = dom.implementation().createHTMLDocument('')
737
+ , nod = nodal(doc, {}, nsMap)
738
+ , math = doc.createElementNS(MATH_NS, 'math')
739
+ ;
740
+ math.setAttribute('display', 'inline');
741
+ el = nod.el;
742
+ w.result(math);
743
+ w.walk(math);
744
+ }