Annotation of loncom/html/adm/LC_math_editor/src/enode.js, revision 1.2

1.1       damieng     1: /*
                      2: 
                      3: Copyright (C) 2014  Michigan State University Board of Trustees
                      4: 
                      5: The JavaScript code in this page is free software: you can
                      6: redistribute it and/or modify it under the terms of the GNU
                      7: General Public License (GNU GPL) as published by the Free Software
                      8: Foundation, either version 3 of the License, or (at your option)
                      9: any later version.  The code is distributed WITHOUT ANY WARRANTY;
                     10: without even the implied warranty of MERCHANTABILITY or FITNESS
                     11: FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
                     12: 
                     13: As additional permission under GNU GPL version 3 section 7, you
                     14: may distribute non-source (e.g., minimized or compacted) forms of
                     15: that code without the copy of the GNU GPL normally required by
                     16: section 4, provided you include this license notice and a URL
                     17: through which recipients can access the Corresponding Source.
                     18: 
                     19: */
                     20: 
1.2     ! damieng    21: "use strict";
        !            22: 
1.1       damieng    23: /**
1.2     ! damieng    24:  * Parsed tree node. ENode.toMathML() contains the code for the transformation into MathML.
1.1       damieng    25:  * @constructor
1.2     ! damieng    26:  * @param {number} type - ENode.UNKNOWN | NAME | NUMBER | OPERATOR | FUNCTION | VECTOR | INTERVAL | SET | SUBSCRIPT
1.1       damieng    27:  * @param {Operator} op - The operator
                     28:  * @param {string} value - Node value as a string, null for type VECTOR
1.2     ! damieng    29:  * @param {Array.<ENode>} children - The children nodes, only for types OPERATOR, FUNCTION, VECTOR, INTERVAL, SET, SUBSCRIPT
        !            30:  * @param {number} [interval_type] - ENode.NOT_AN_INTERVAL | OPEN_OPEN | OPEN_CLOSED | CLOSED_OPEN | CLOSED_CLOSED
1.1       damieng    31:  */
1.2     ! damieng    32: function ENode(type, op, value, children, interval_type) {
1.1       damieng    33:     this.type = type;
                     34:     this.op = op;
                     35:     this.value = value;
                     36:     this.children = children;
1.2     ! damieng    37:     if (typeof interval_type == "undefined")
        !            38:         this.interval_type = ENode.NOT_AN_INTERVAL;
        !            39:     else
        !            40:         this.interval_type = interval_type;
1.1       damieng    41: }
                     42: 
                     43: ENode.UNKNOWN = 0;
                     44: ENode.NAME = 1;
                     45: ENode.NUMBER = 2;
                     46: ENode.OPERATOR = 3;
                     47: ENode.FUNCTION = 4;
                     48: ENode.VECTOR = 5;
1.2     ! damieng    49: ENode.INTERVAL = 6;
        !            50: ENode.SET = 7;
        !            51: ENode.SUBSCRIPT = 8;
1.1       damieng    52: ENode.COLORS = ["#E01010", "#0010FF", "#009000", "#FF00FF", "#00B0B0", "#F09000", 
                     53:                 "#800080", "#F080A0", "#6090F0", "#902000", "#70A050", "#A07060",
                     54:                 "#5000FF", "#E06050", "#008080", "#808000"];
                     55: 
1.2     ! damieng    56: ENode.NOT_AN_INTERVAL = 0;
        !            57: ENode.OPEN_OPEN = 1;
        !            58: ENode.OPEN_CLOSED = 2;
        !            59: ENode.CLOSED_OPEN = 3;
        !            60: ENode.CLOSED_CLOSED = 4;
        !            61: 
1.1       damieng    62: /**
                     63:  * Returns the node as a string, for debug
                     64:  * @returns {string}
                     65:  */
                     66: ENode.prototype.toString = function() {
                     67:     var s = '(';
                     68:     switch (this.type) {
                     69:         case ENode.UNKNOWN:
                     70:             s += 'UNKNOWN';
                     71:             break;
                     72:         case ENode.NAME:
                     73:             s += 'NAME';
                     74:             break;
                     75:         case ENode.NUMBER:
                     76:             s += 'NUMBER';
                     77:             break;
                     78:         case ENode.OPERATOR:
                     79:             s += 'OPERATOR';
                     80:             break;
                     81:         case ENode.FUNCTION:
                     82:             s += 'FUNCTION';
                     83:             break;
                     84:         case ENode.VECTOR:
                     85:             s += 'VECTOR';
                     86:             break;
1.2     ! damieng    87:         case ENode.INTERVAL:
        !            88:             s += 'INTERVAL';
        !            89:             break;
        !            90:         case ENode.SET:
        !            91:             s += 'SET';
        !            92:             break;
1.1       damieng    93:         case ENode.SUBSCRIPT:
                     94:             s += 'SUBSCRIPT';
                     95:             break;
                     96:     }
                     97:     if (this.op)
                     98:         s += " '" + this.op.id + "'";
                     99:     if (this.value)
                    100:         s += " '" + this.value + "'";
                    101:     if (this.children) {
                    102:         s += ' [';
                    103:         for (var i = 0; i < this.children.length; i++) {
                    104:             s += this.children[i].toString();
                    105:             if (i != this.children.length - 1)
                    106:                 s += ',';
                    107:         }
                    108:         s += ']';
                    109:     }
1.2     ! damieng   110:     if (this.interval_type) {
        !           111:         s += " " + this.interval_type;
        !           112:     }
1.1       damieng   113:     s+= ')';
                    114:     return s;
                    115: };
                    116: 
                    117: /**
                    118:  * Returns the color for an identifier.
                    119:  * @param {string} name
                    120:  * @param {Object.<string, string>} hcolors - hash identifier->color
                    121:  * @returns {string}
                    122:  */
1.2     ! damieng   123: ENode.prototype.getColorForIdentifier = function(name, context) {
        !           124:     var res = context.hcolors[name];
1.1       damieng   125:     if (!res) {
1.2     ! damieng   126:         var colors;
        !           127:         if (context.colors)
        !           128:             colors = context.colors;
        !           129:         else
        !           130:             colors = ENode.COLORS;
        !           131:         res = colors[Object.keys(context.hcolors).length % colors.length];
        !           132:         context.hcolors[name] = res;
1.1       damieng   133:     }
                    134:     return res;
                    135: }
                    136: 
                    137: /**
                    138:  * Transforms this ENode into a MathML HTML DOM element.
1.2     ! damieng   139:  * @param {Array.<string>} [colors] - optional override for ENode.COLORS
        !           140:  * @returns {Element}
        !           141:  */
        !           142: ENode.prototype.toMathML = function(colors) {
        !           143:     var context = { hcolors: {}, depth: 0 , colors: colors};
        !           144:     return(this._toMathML(context));
        !           145: }
        !           146: 
        !           147: /**
        !           148:  * Transforms this ENode into a MathML HTML DOM element (internal function).
        !           149:  * @param {Object} context - display context
1.1       damieng   150:  * @param {Object.<string, string>} context.hcolors - hash identifier->color
                    151:  * @param {number} context.depth - Depth in parenthesis, used for coloring
1.2     ! damieng   152:  * @param {Array.<string>} context.colors - optional override for ENode.COLORS
1.1       damieng   153:  * @returns {Element}
                    154:  */
1.2     ! damieng   155: ENode.prototype._toMathML = function(context) {
        !           156:     var c0, c1, c2, c3, c4, i, j, el, par, mrow, mo, mtable, mfrac, msub, msup, mrow2;
1.1       damieng   157:     if (this.children != null && this.children.length > 0)
                    158:         c0 = this.children[0];
                    159:     else
                    160:         c0 = null;
                    161:     if (this.children != null && this.children.length > 1)
                    162:         c1 = this.children[1];
                    163:     else
                    164:         c1 = null;
                    165:     if (this.children != null && this.children.length > 2)
                    166:         c2 = this.children[2];
                    167:     else
                    168:         c2 = null;
                    169:     if (this.children != null && this.children.length > 3)
                    170:         c3 = this.children[3];
                    171:     else
                    172:         c3 = null;
                    173:     if (this.children != null && this.children.length > 4)
                    174:         c4 = this.children[4];
                    175:     else
                    176:         c4 = null;
                    177:     
                    178:     switch (this.type) {
                    179:         case ENode.UNKNOWN:
                    180:             el = document.createElement('mtext');
                    181:             el.appendChild(document.createTextNode("???"));
                    182:             return(el);
                    183:         
                    184:         case ENode.NAME:
                    185:             if (this.value.search(/^[a-zA-Z]+[0-9]+$/) >= 0) {
                    186:                 var ind = this.value.search(/[0-9]/);
                    187:                 msub = document.createElement('msub');
                    188:                 msub.appendChild(this.mi(this.value.substring(0,ind)));
                    189:                 msub.appendChild(this.mn(this.value.substring(ind)));
                    190:                 el = msub;
                    191:             } else {
                    192:                 el = this.mi(this.value)
                    193:             }
1.2     ! damieng   194:             el.setAttribute("mathcolor", this.getColorForIdentifier(this.value, context));
        !           195:             if (this.value.indexOf('$') === 0)
        !           196:                 el.setAttribute("fontfamily", "monospace");
1.1       damieng   197:             return(el);
                    198:         
                    199:         case ENode.NUMBER:
                    200:             if (this.value.indexOf('e') != -1 || this.value.indexOf('E') != -1) {
                    201:                 var index = this.value.indexOf('e');
                    202:                 if (index == -1)
                    203:                     index = this.value.indexOf('E');
                    204:                 mrow = document.createElement('mrow');
                    205:                 mrow.appendChild(this.mn(this.value.substring(0, index)));
                    206:                 mrow.appendChild(this.mo("\u22C5"));
                    207:                 msup = document.createElement('msup');
                    208:                 msup.appendChild(this.mn(10));
                    209:                 msup.appendChild(this.mn(this.value.substring(index + 1)));
                    210:                 mrow.appendChild(msup);
                    211:                 return(mrow);
                    212:             }
                    213:             return(this.mn(this.value));
                    214:         
                    215:         case ENode.OPERATOR:
                    216:             if (this.value == "/") {
                    217:                 mfrac = document.createElement('mfrac');
1.2     ! damieng   218:                 mfrac.appendChild(c0._toMathML(context));
        !           219:                 mfrac.appendChild(c1._toMathML(context));
1.1       damieng   220:                 el = mfrac;
                    221:             } else if (this.value == "^") {
                    222:                 if (c0.type == ENode.FUNCTION) {
                    223:                     if (c0.value == "sqrt" || c0.value == "abs" || c0.value == "matrix" ||
                    224:                             c0.value == "diff")
                    225:                         par = false;
                    226:                     else
                    227:                         par = true;
                    228:                 } else if (c0.type == ENode.OPERATOR) {
                    229:                     par = true;
                    230:                 } else
                    231:                     par = false;
                    232:                 el = document.createElement('msup');
                    233:                 if (par)
                    234:                     el.appendChild(this.addP(c0, context));
                    235:                 else
1.2     ! damieng   236:                     el.appendChild(c0._toMathML(context));
        !           237:                 el.appendChild(c1._toMathML(context));
1.1       damieng   238:             } else if (this.value == "*") {
                    239:                 mrow = document.createElement('mrow');
                    240:                 if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-"))
                    241:                     mrow.appendChild(this.addP(c0, context));
                    242:                 else
1.2     ! damieng   243:                     mrow.appendChild(c0._toMathML(context));
1.1       damieng   244:                 // should the x operator be visible ? We need to check if there is a number to the left of c1
                    245:                 var firstinc1 = c1;
                    246:                 while (firstinc1.type == ENode.OPERATOR) {
                    247:                     firstinc1 = firstinc1.children[0];
                    248:                 }
                    249:                 // ... and if it's an operation between vectors/matrices, the * operator should be displayed
                    250:                 // (it is ambiguous otherwise)
                    251:                 // note: this will not work if the matrix is calculated, for instance with 2[1;2]*[3;4]
                    252:                 if (c0.type == ENode.VECTOR && c1.type == ENode.VECTOR)
                    253:                     mrow.appendChild(this.mo("*"));
                    254:                 else if (firstinc1.type == ENode.NUMBER)
                    255:                     mrow.appendChild(this.mo("\u22C5"));
1.2     ! damieng   256:                 else if (context.units)
        !           257:                     mrow.appendChild(this.mo("\u22C5"));
        !           258:                 else
        !           259:                     mrow.appendChild(this.mo("\u2062")); // invisible multiplication
1.1       damieng   260:                 if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
                    261:                     mrow.appendChild(this.addP(c1, context));
                    262:                 else
1.2     ! damieng   263:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   264:                 el = mrow;
                    265:             } else if (this.value == "-") {
                    266:                 mrow = document.createElement('mrow');
                    267:                 if (this.children.length == 1) {
                    268:                     mrow.appendChild(this.mo("-"));
1.2     ! damieng   269:                     mrow.appendChild(c0._toMathML(context));
1.1       damieng   270:                 } else {
1.2     ! damieng   271:                     mrow.appendChild(c0._toMathML(context));
1.1       damieng   272:                     mrow.appendChild(this.mo("-"));
                    273:                     if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
                    274:                         mrow.appendChild(this.addP(c1, context));
                    275:                     else
1.2     ! damieng   276:                         mrow.appendChild(c1._toMathML(context));
1.1       damieng   277:                 }
                    278:                 el = mrow;
                    279:             } else if (this.value == "!") {
                    280:                 mrow = document.createElement('mrow');
                    281:                 mo = this.mo(this.value);
                    282:                 if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-"))
                    283:                     mrow.appendChild(this.addP(c0, context));
                    284:                 else
1.2     ! damieng   285:                     mrow.appendChild(c0._toMathML(context));
1.1       damieng   286:                 mrow.appendChild(mo);
                    287:                 el = mrow;
                    288:             } else if (this.value == "+") {
                    289:                 mrow = document.createElement('mrow');
                    290:                 mo = this.mo(this.value);
1.2     ! damieng   291:                 mrow.appendChild(c0._toMathML(context));
1.1       damieng   292:                 mrow.appendChild(mo);
                    293:                 // should we add parenthesis ? We need to check if there is a '-' to the left of c1
                    294:                 par = false;
                    295:                 var first = c1;
                    296:                 while (first.type == ENode.OPERATOR) {
                    297:                     if (first.value == "-" && first.children.length == 1) {
                    298:                         par = true;
                    299:                         break;
                    300:                     } else if (first.value == "+" || first.value == "-" || first.value == "*") {
                    301:                         first = first.children[0];
                    302:                     } else {
                    303:                         break;
                    304:                     }
                    305:                 }
                    306:                 if (par)
                    307:                     mrow.appendChild(this.addP(c1, context));
                    308:                 else
1.2     ! damieng   309:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   310:                 el = mrow;
                    311:             } else if (this.value == ".") {
                    312:                 mrow = document.createElement('mrow');
                    313:                 if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-"))
                    314:                     mrow.appendChild(this.addP(c0, context));
                    315:                 else
1.2     ! damieng   316:                     mrow.appendChild(c0._toMathML(context));
1.1       damieng   317:                 mrow.appendChild(this.mo("\u22C5"));
                    318:                 if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
                    319:                     mrow.appendChild(this.addP(c1, context));
                    320:                 else
1.2     ! damieng   321:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   322:                 el = mrow;
                    323:             } else if (this.value == "`") {
                    324:                 mrow = document.createElement('mrow');
                    325:                 if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-"))
                    326:                     mrow.appendChild(this.addP(c0, context));
                    327:                 else
1.2     ! damieng   328:                     mrow.appendChild(c0._toMathML(context));
1.1       damieng   329:                 // the units should not be in italics
1.2     ! damieng   330:                 // unit multiplication should not be displayed with an invisible operator
1.1       damieng   331:                 var mstyle = document.createElement("mstyle");
                    332:                 mstyle.setAttribute("fontstyle", "normal");
1.2     ! damieng   333:                 var units_context = {};
        !           334:                 for (var prop in context) {
        !           335:                     if (context.hasOwnProperty(prop)) {
        !           336:                         units_context[prop] = context[prop];
        !           337:                     }
        !           338:                 }
        !           339:                 units_context.units = true;
1.1       damieng   340:                 if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
1.2     ! damieng   341:                     mstyle.appendChild(this.addP(c1, units_context));
1.1       damieng   342:                 else
1.2     ! damieng   343:                     mstyle.appendChild(c1._toMathML(units_context));
1.1       damieng   344:                 mrow.appendChild(mstyle);
                    345:                 el = mrow;
                    346:             } else {
                    347:                 // relational operators
                    348:                 mrow = document.createElement('mrow');
                    349:                 mo = this.mo(this.value);
1.2     ! damieng   350:                 mrow.appendChild(c0._toMathML(context));
1.1       damieng   351:                 mrow.appendChild(mo);
1.2     ! damieng   352:                 mrow.appendChild(c1._toMathML(context));
1.1       damieng   353:                 el = mrow;
                    354:             }
                    355:             return(el);
                    356:         
                    357:         case ENode.FUNCTION: /* TODO: throw exceptions if wrong nb of args ? */
                    358:             // c0 contains the function name
                    359:             if (c0.value == "sqrt" && c1 != null) {
                    360:                 el = document.createElement('msqrt');
1.2     ! damieng   361:                 el.appendChild(c1._toMathML(context));
1.1       damieng   362:             } else if (c0.value == "abs" && c1 != null) {
                    363:                 mrow = document.createElement('mrow');
                    364:                 mrow.appendChild(this.mo("|"));
1.2     ! damieng   365:                 mrow.appendChild(c1._toMathML(context));
1.1       damieng   366:                 mrow.appendChild(this.mo("|"));
                    367:                 el = mrow;
                    368:             } else if (c0.value == "exp" && c1 != null) {
                    369:                 el = document.createElement('msup');
                    370:                 el.appendChild(this.mi("e"));
1.2     ! damieng   371:                 el.appendChild(c1._toMathML(context));
1.1       damieng   372:             } else if (c0.value == "factorial") {
                    373:                 mrow = document.createElement('mrow');
                    374:                 mo = this.mo("!");
                    375:                 if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
                    376:                     mrow.appendChild(this.addP(c1, context));
                    377:                 else
1.2     ! damieng   378:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   379:                 mrow.appendChild(mo);
                    380:                 el = mrow;
                    381:             } else if (c0.value == "diff" && this.children != null && this.children.length == 3) {
                    382:                 mrow = document.createElement('mrow');
                    383:                 mfrac = document.createElement('mfrac');
                    384:                 mfrac.appendChild(this.mi("d"));
                    385:                 var f2 = document.createElement('mrow');
                    386:                 f2.appendChild(this.mi("d"));
                    387:                 f2.appendChild(this.mi(c2.value));
                    388:                 mfrac.appendChild(f2);
                    389:                 mrow.appendChild(mfrac);
                    390:                 if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
                    391:                     mrow.appendChild(this.addP(c1, context));
                    392:                 else
1.2     ! damieng   393:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   394:                 el = mrow;
                    395:             } else if (c0.value == "diff" && this.children != null && this.children.length == 4) {
                    396:                 mrow = document.createElement('mrow');
                    397:                 mfrac = document.createElement('mfrac');
                    398:                 msup = document.createElement('msup');
                    399:                 msup.appendChild(this.mi("d"));
1.2     ! damieng   400:                 msup.appendChild(c3._toMathML(context));
1.1       damieng   401:                 mfrac.appendChild(msup);
                    402:                 var f2 = document.createElement('mrow');
                    403:                 f2.appendChild(this.mi("d"));
                    404:                 msup = document.createElement('msup');
1.2     ! damieng   405:                 msup.appendChild(c2._toMathML(context));
        !           406:                 msup.appendChild(c3._toMathML(context));
1.1       damieng   407:                 f2.appendChild(msup);
                    408:                 mfrac.appendChild(f2);
                    409:                 mrow.appendChild(mfrac);
                    410:                 if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-"))
                    411:                     mrow.appendChild(this.addP(c1, context));
                    412:                 else
1.2     ! damieng   413:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   414:                 el = mrow;
                    415:             } else if (c0.value == "integrate" && this.children != null && this.children.length == 3) {
                    416:                 mrow = document.createElement('mrow');
                    417:                 var mo = this.mo("\u222B");
                    418:                 mo.setAttribute("stretchy", "true"); // doesn't work with MathJax
                    419:                 mrow.appendChild(mo);
                    420:                 if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-"))
                    421:                     mrow.appendChild(this.addP(c1, context));
                    422:                 else
1.2     ! damieng   423:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   424:                 mrow.appendChild(this.mi("d"));
1.2     ! damieng   425:                 mrow.appendChild(c2._toMathML(context));
1.1       damieng   426:                 el = mrow;
                    427:             } else if (c0.value == "integrate" && this.children != null && this.children.length == 5) {
                    428:                 mrow = document.createElement('mrow');
                    429:                 var msubsup = document.createElement('msubsup');
                    430:                 var mo = this.mo("\u222B");
                    431:                 mo.setAttribute("stretchy", "true"); // doesn't work with MathJax
                    432:                 msubsup.appendChild(mo);
1.2     ! damieng   433:                 msubsup.appendChild(c3._toMathML(context));
        !           434:                 msubsup.appendChild(c4._toMathML(context));
1.1       damieng   435:                 mrow.appendChild(msubsup);
                    436:                 if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-"))
                    437:                     mrow.appendChild(this.addP(c1, context));
                    438:                 else
1.2     ! damieng   439:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   440:                 mrow.appendChild(this.mi("d"));
1.2     ! damieng   441:                 mrow.appendChild(c2._toMathML(context));
1.1       damieng   442:                 el = mrow;
                    443:             } else if (c0.value == "sum" && this.children != null && this.children.length == 5) {
                    444:                 mrow = document.createElement('mrow');
                    445:                 var munderover = document.createElement('munderover');
                    446:                 var mo = this.mo("\u2211");
                    447:                 mo.setAttribute("stretchy", "true"); // doesn't work with MathJax
                    448:                 munderover.appendChild(mo);
1.2     ! damieng   449:                 mrow2 = document.createElement('mrow');
        !           450:                 mrow2.appendChild(c2._toMathML(context));
1.1       damieng   451:                 mrow2.appendChild(this.mo("="));
1.2     ! damieng   452:                 mrow2.appendChild(c3._toMathML(context));
1.1       damieng   453:                 munderover.appendChild(mrow2);
1.2     ! damieng   454:                 munderover.appendChild(c4._toMathML(context));
1.1       damieng   455:                 mrow.appendChild(munderover);
                    456:                 if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-"))
                    457:                     mrow.appendChild(this.addP(c1, context));
                    458:                 else
1.2     ! damieng   459:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   460:                 el = mrow;
                    461:             } else if (c0.value == "product" && this.children != null && this.children.length == 5) {
                    462:                 mrow = document.createElement('mrow');
                    463:                 var munderover = document.createElement('munderover');
                    464:                 var mo = this.mo("\u220F");
                    465:                 mo.setAttribute("stretchy", "true"); // doesn't work with MathJax
                    466:                 munderover.appendChild(mo);
1.2     ! damieng   467:                 mrow2 = document.createElement('mrow');
        !           468:                 mrow2.appendChild(c2._toMathML(context));
1.1       damieng   469:                 mrow2.appendChild(this.mo("="));
1.2     ! damieng   470:                 mrow2.appendChild(c3._toMathML(context));
1.1       damieng   471:                 munderover.appendChild(mrow2);
1.2     ! damieng   472:                 munderover.appendChild(c4._toMathML(context));
1.1       damieng   473:                 mrow.appendChild(munderover);
                    474:                 if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-"))
                    475:                     mrow.appendChild(this.addP(c1, context));
                    476:                 else
1.2     ! damieng   477:                     mrow.appendChild(c1._toMathML(context));
1.1       damieng   478:                 el = mrow;
                    479:             } else if (c0.value == "limit") {
                    480:                 mrow = document.createElement('mrow');
                    481:                 if (this.children.length < 4) {
                    482:                     mrow.appendChild(this.mo("lim"));
                    483:                 } else {
                    484:                     var munder = document.createElement('munder');
                    485:                     munder.appendChild(this.mo("lim"));
                    486:                     var mrowunder = document.createElement('mrow');
1.2     ! damieng   487:                     mrowunder.appendChild(c2._toMathML(context));
1.1       damieng   488:                     mrowunder.appendChild(this.mo("\u2192"));
1.2     ! damieng   489:                     mrowunder.appendChild(c3._toMathML(context));
1.1       damieng   490:                     if (c4 != null) {
                    491:                         if (c4.value == "plus")
                    492:                             mrowunder.appendChild(this.mo("+"));
                    493:                         else if (c4.value == "minus")
                    494:                             mrowunder.appendChild(this.mo("-"));
                    495:                     }
                    496:                     munder.appendChild(mrowunder);
                    497:                     mrow.appendChild(munder);
                    498:                 }
1.2     ! damieng   499:                 mrow.appendChild(c1._toMathML(context));
1.1       damieng   500:                 el = mrow;
                    501:             } else if (c0.value == "binomial") {
                    502:                 // displayed like a vector
                    503:                 mrow = document.createElement('mrow');
                    504:                 mrow.appendChild(this.mo("("));
                    505:                 mtable = document.createElement('mtable');
                    506:                 for (i=1; i<this.children.length; i++) {
                    507:                     var mtr = document.createElement('mtr');
1.2     ! damieng   508:                     mtr.appendChild(this.children[i]._toMathML(context));
1.1       damieng   509:                     mtable.appendChild(mtr);
                    510:                 }
                    511:                 mrow.appendChild(mtable);
                    512:                 mrow.appendChild(this.mo(")"));
                    513:                 el = mrow;
                    514:             } else if (c0.value == "matrix") {
                    515:                 for (i=1; i<this.children.length; i++) {
                    516:                     // check that all children are vectors
                    517:                     if (this.children[i].type !== ENode.VECTOR) {
                    518:                         el = document.createElement('mtext');
                    519:                         el.appendChild(document.createTextNode("???")); // could throw here
                    520:                         return(el);
                    521:                     }
                    522:                 }
                    523:                 mrow = document.createElement('mrow');
                    524:                 mrow.appendChild(this.mo("("));
                    525:                 mtable = document.createElement('mtable');
                    526:                 for (i=1; i<this.children.length; i++) {
                    527:                     var mtr = document.createElement('mtr');
                    528:                     for (j=0; j<this.children[i].children.length; j++) {
1.2     ! damieng   529:                         mtr.appendChild(this.children[i].children[j]._toMathML(context));
1.1       damieng   530:                     }
                    531:                     mtable.appendChild(mtr);
                    532:                 }
                    533:                 mrow.appendChild(mtable);
                    534:                 mrow.appendChild(this.mo(")"));
                    535:                 el = mrow;
1.2     ! damieng   536:             } else if (c0.value == "union" && this.children.length == 3) {
        !           537:                 for (i=1; i<this.children.length; i++) {
        !           538:                     // check that all children are intervals or sets
        !           539:                     if (this.children[i].type !== ENode.INTERVAL && this.children[i].type !== ENode.SET) {
        !           540:                         el = document.createElement('mtext');
        !           541:                         el.appendChild(document.createTextNode("???"));
        !           542:                         return(el);
        !           543:                     }
        !           544:                 }
        !           545:                 mrow = document.createElement('mrow');
        !           546:                 mrow.appendChild(c1._toMathML(context));
        !           547:                 mrow.appendChild(this.mo("\u222A"));
        !           548:                 mrow.appendChild(c2._toMathML(context));
        !           549:                 el = mrow;
        !           550:             } else if (c0.value == "intersection" && this.children.length == 3) {
        !           551:                 for (i=1; i<this.children.length; i++) {
        !           552:                     // check that all children are intervals or sets
        !           553:                     if (this.children[i].type !== ENode.INTERVAL && this.children[i].type !== ENode.SET) {
        !           554:                         el = document.createElement('mtext');
        !           555:                         el.appendChild(document.createTextNode("???"));
        !           556:                         return(el);
        !           557:                     }
        !           558:                 }
        !           559:                 mrow = document.createElement('mrow');
        !           560:                 mrow.appendChild(c1._toMathML(context));
        !           561:                 mrow.appendChild(this.mo("\u2229"));
        !           562:                 mrow.appendChild(c2._toMathML(context));
        !           563:                 el = mrow;
1.1       damieng   564:             } else {
                    565:                 // default display for a function
                    566:                 mrow = document.createElement('mrow');
1.2     ! damieng   567:                 mrow.appendChild(c0._toMathML(context));
1.1       damieng   568:                 mrow.appendChild(this.mo("("));
                    569:                 for (i=1; i<this.children.length; i++) {
1.2     ! damieng   570:                     mrow.appendChild(this.children[i]._toMathML(context));
1.1       damieng   571:                     if (i < this.children.length - 1)
                    572:                         mrow.appendChild(this.mo(Definitions.ARG_SEPARATOR));
                    573:                 }
                    574:                 mrow.appendChild(this.mo(")"));
                    575:                 el = mrow;
                    576:             }
                    577:             return(el);
                    578:         
                    579:         case ENode.VECTOR:
                    580:             var is_matrix = true;
                    581:             for (i=0; i<this.children.length; i++) {
                    582:                 if (this.children[i].type !== ENode.VECTOR)
                    583:                     is_matrix = false;
                    584:             }
                    585:             mrow = document.createElement('mrow');
                    586:             mrow.appendChild(this.mo("("));
                    587:             mtable = document.createElement('mtable');
                    588:             for (i=0; i<this.children.length; i++) {
                    589:                 var mtr = document.createElement('mtr');
                    590:                 if (is_matrix) {
                    591:                     for (j=0; j<this.children[i].children.length; j++) {
1.2     ! damieng   592:                         mtr.appendChild(this.children[i].children[j]._toMathML(context));
1.1       damieng   593:                     }
                    594:                 } else {
1.2     ! damieng   595:                     mtr.appendChild(this.children[i]._toMathML(context));
1.1       damieng   596:                 }
                    597:                 mtable.appendChild(mtr);
                    598:             }
                    599:             mrow.appendChild(mtable);
                    600:             mrow.appendChild(this.mo(")"));
                    601:             return(mrow);
1.2     ! damieng   602:         
        !           603:         case ENode.INTERVAL:
        !           604:             mrow = document.createElement('mrow');
        !           605:             if (this.interval_type == ENode.OPEN_OPEN || this.interval_type == ENode.OPEN_CLOSED) {
        !           606:                 mo = this.mo("(");
        !           607:             } else {
        !           608:                 mo = this.mo("[");
        !           609:             }
        !           610:             mrow.appendChild(mo);
        !           611:             mrow2 = document.createElement('mrow');
        !           612:             mrow2.appendChild(this.children[0]._toMathML(context));
        !           613:             mrow2.appendChild(this.mo(":"));
        !           614:             mrow2.appendChild(this.children[1]._toMathML(context));
        !           615:             mrow.appendChild(mrow2);
        !           616:             if (this.interval_type == ENode.OPEN_OPEN || this.interval_type == ENode.CLOSED_OPEN) {
        !           617:                 mo = this.mo(")");
        !           618:             } else {
        !           619:                 mo = this.mo("]");
        !           620:             }
        !           621:             mrow.appendChild(mo);
        !           622:             return(mrow);
1.1       damieng   623:             
1.2     ! damieng   624:         case ENode.SET:
        !           625:             mrow = document.createElement('mrow');
        !           626:             mrow.appendChild(this.mo("{"));
        !           627:             mrow2 = document.createElement('mrow');
        !           628:             for (i=0; i<this.children.length; i++) {
        !           629:                 if (i > 0)
        !           630:                     mrow2.appendChild(this.mo(";"));
        !           631:                 mrow2.appendChild(this.children[i]._toMathML(context));
        !           632:             }
        !           633:             mrow.appendChild(mrow2);
        !           634:             mrow.appendChild(this.mo("}"));
        !           635:             return(mrow);
        !           636:         
1.1       damieng   637:         case ENode.SUBSCRIPT:
                    638:             msub = document.createElement('msub');
1.2     ! damieng   639:             msub.appendChild(c0._toMathML(context));
1.1       damieng   640:             if (this.children.length > 2) {
                    641:                 mrow = document.createElement('mrow');
                    642:                 for (i=1; i<this.children.length; i++) {
1.2     ! damieng   643:                     mrow.appendChild(this.children[i]._toMathML(context));
1.1       damieng   644:                     if (i < this.children.length - 1)
                    645:                         mrow.appendChild(this.mo(Definitions.ARG_SEPARATOR));
                    646:                 }
                    647:                 msub.appendChild(mrow);
                    648:             } else {
1.2     ! damieng   649:                 msub.appendChild(c1._toMathML(context));
1.1       damieng   650:             }
                    651:             return(msub);
1.2     ! damieng   652:             
1.1       damieng   653:     }
                    654: };
                    655: 
                    656: /**
                    657:  * Creates a MathML mi element with the given name
                    658:  * @param {string} name
                    659:  * @returns {Element}
                    660:  */
                    661: ENode.prototype.mi = function(name) {
                    662:     var mi = document.createElement('mi');
                    663:     if (ENode.symbols[name])
                    664:         name = ENode.symbols[name];
                    665:     mi.appendChild(document.createTextNode(name));
                    666:     return mi;
                    667: };
                    668: 
                    669: /**
                    670:  * Creates a MathML mn element with the given number or string
                    671:  * @param {string} n
                    672:  * @returns {Element}
                    673:  */
                    674: ENode.prototype.mn = function(n) {
                    675:     var mn = document.createElement('mn');
                    676:     mn.appendChild(document.createTextNode(n));
                    677:     return mn;
                    678: };
                    679: 
                    680: /**
                    681:  * Creates a MathML mo element with the given name
                    682:  * @param {string} name
                    683:  * @returns {Element}
                    684:  */
                    685: ENode.prototype.mo = function(name) {
                    686:     var mo = document.createElement('mo');
                    687:     if (ENode.symbols[name])
                    688:         name = ENode.symbols[name];
                    689:     mo.appendChild(document.createTextNode(name));
                    690:     return mo;
                    691: };
                    692: 
                    693: /**
                    694:  * Add parenthesis and returns a MathML element
                    695:  * @param {ENode} en
                    696:  * @param {Object} [context] - display context (not needed for the root element)
                    697:  * @param {Object.<string, string>} context.hcolors - hash identifier->color
                    698:  * @param {number} context.depth - Depth in parenthesis, used for coloring
                    699:  * @returns {Element}
                    700:  */
                    701: ENode.prototype.addP = function(en, context) {
                    702:     var mrow, mo;
                    703:     mrow = document.createElement('mrow');
                    704:     mo = this.mo("(");
1.2     ! damieng   705:     var colors;
        !           706:     if (context.colors)
        !           707:         colors = context.colors;
        !           708:     else
        !           709:         colors = ENode.COLORS;
        !           710:     mo.setAttribute("mathcolor", colors[context.depth % colors.length]);
1.1       damieng   711:     mrow.appendChild(mo);
                    712:     context.depth++;
1.2     ! damieng   713:     mrow.appendChild(en._toMathML(context));
1.1       damieng   714:     context.depth--;
                    715:     mo = this.mo(")");
1.2     ! damieng   716:     mo.setAttribute("mathcolor", colors[context.depth % colors.length]);
1.1       damieng   717:     mrow.appendChild(mo);
                    718:     return mrow;
                    719: };
                    720: 
                    721: ENode.symbols = {
                    722:     /* lowercase greek */
                    723:     "alpha": "\u03B1", "beta": "\u03B2", "gamma": "\u03B3",
                    724:     "delta": "\u03B4", "epsilon": "\u03B5", "zeta": "\u03B6",
                    725:     "eta": "\u03B7", "theta": "\u03B8", "iota": "\u03B9",
                    726:     "kappa": "\u03BA", "lambda": "\u03BB", "mu": "\u03BC",
                    727:     "nu": "\u03BD", "xi": "\u03BE", "omicron": "\u03BF",
                    728:     "pi": "\u03C0", "rho": "\u03C1", "sigma": "\u03C3",
                    729:     "tau": "\u03C4", "upsilon": "\u03C5", "phi": "\u03C6",
                    730:     "chi": "\u03C7", "psi": "\u03C8", "omega": "\u03C9",
                    731:     /* uppercase greek */
                    732:     "Alpha": "\u0391", "Beta": "\u0392", "Gamma": "\u0393",
                    733:     "Delta": "\u0394", "Epsilon": "\u0395", "Zeta": "\u0396",
                    734:     "Eta": "\u0397", "Theta": "\u0398", "Iota": "\u0399",
                    735:     "Kappa": "\u039A", "Lambda": "\u039B", "Mu": "\u039C",
                    736:     "Nu": "\u039D", "Xi": "\u039E", "Omicron": "\u039F",
                    737:     "Pi": "\u03A0", "Rho": "\u03A1", "Sigma": "\u03A3",
                    738:     "Tau": "\u03A4", "Upsilon": "\u03A5", "Phi": "\u03A6",
                    739:     "Chi": "\u03A7", "Psi": "\u03A8", "Omega": "\u03A9",
                    740:     
                    741:     /* operators */
                    742:     "#":  "\u2260",
                    743:     ">=": "\u2265",
                    744:     "<=": "\u2264",
                    745:     
                    746:     /* other */
                    747:     "inf":  "\u221E",
                    748:     "minf": "-\u221E",
                    749:     "hbar": "\u210F",
                    750:     "G":    "\uD835\uDCA2" // 1D4A2
                    751: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>