--- loncom/html/adm/LC_math_editor/src/enode.js 2014/09/24 18:14:39 1.1 +++ loncom/html/adm/LC_math_editor/src/enode.js 2015/02/24 15:20:44 1.2 @@ -18,19 +18,26 @@ through which recipients can access the */ +"use strict"; + /** - * Parsed tree node. ENode.toMathML(hcolors) contains the code for the transformation into MathML. + * Parsed tree node. ENode.toMathML() contains the code for the transformation into MathML. * @constructor - * @param {number} type - ENode.UNKNOWN | NAME | NUMBER | OPERATOR | FUNCTION | VECTOR + * @param {number} type - ENode.UNKNOWN | NAME | NUMBER | OPERATOR | FUNCTION | VECTOR | INTERVAL | SET | SUBSCRIPT * @param {Operator} op - The operator * @param {string} value - Node value as a string, null for type VECTOR - * @param {Array.} children - The children nodes, only for types OPERATOR, FUNCTION, VECTOR, SUBSCRIPT + * @param {Array.} children - The children nodes, only for types OPERATOR, FUNCTION, VECTOR, INTERVAL, SET, SUBSCRIPT + * @param {number} [interval_type] - ENode.NOT_AN_INTERVAL | OPEN_OPEN | OPEN_CLOSED | CLOSED_OPEN | CLOSED_CLOSED */ -function ENode(type, op, value, children) { +function ENode(type, op, value, children, interval_type) { this.type = type; this.op = op; this.value = value; this.children = children; + if (typeof interval_type == "undefined") + this.interval_type = ENode.NOT_AN_INTERVAL; + else + this.interval_type = interval_type; } ENode.UNKNOWN = 0; @@ -39,11 +46,19 @@ ENode.NUMBER = 2; ENode.OPERATOR = 3; ENode.FUNCTION = 4; ENode.VECTOR = 5; -ENode.SUBSCRIPT = 6; +ENode.INTERVAL = 6; +ENode.SET = 7; +ENode.SUBSCRIPT = 8; ENode.COLORS = ["#E01010", "#0010FF", "#009000", "#FF00FF", "#00B0B0", "#F09000", "#800080", "#F080A0", "#6090F0", "#902000", "#70A050", "#A07060", "#5000FF", "#E06050", "#008080", "#808000"]; +ENode.NOT_AN_INTERVAL = 0; +ENode.OPEN_OPEN = 1; +ENode.OPEN_CLOSED = 2; +ENode.CLOSED_OPEN = 3; +ENode.CLOSED_CLOSED = 4; + /** * Returns the node as a string, for debug * @returns {string} @@ -69,6 +84,12 @@ ENode.prototype.toString = function() { case ENode.VECTOR: s += 'VECTOR'; break; + case ENode.INTERVAL: + s += 'INTERVAL'; + break; + case ENode.SET: + s += 'SET'; + break; case ENode.SUBSCRIPT: s += 'SUBSCRIPT'; break; @@ -86,6 +107,9 @@ ENode.prototype.toString = function() { } s += ']'; } + if (this.interval_type) { + s += " " + this.interval_type; + } s+= ')'; return s; }; @@ -96,26 +120,40 @@ ENode.prototype.toString = function() { * @param {Object.} hcolors - hash identifier->color * @returns {string} */ -ENode.prototype.getColorForIdentifier = function(name, hcolors) { - var res = hcolors[name]; +ENode.prototype.getColorForIdentifier = function(name, context) { + var res = context.hcolors[name]; if (!res) { - res = ENode.COLORS[Object.keys(hcolors).length % ENode.COLORS.length]; - hcolors[name] = res; + var colors; + if (context.colors) + colors = context.colors; + else + colors = ENode.COLORS; + res = colors[Object.keys(context.hcolors).length % colors.length]; + context.hcolors[name] = res; } return res; } /** * Transforms this ENode into a MathML HTML DOM element. - * @param {Object} [context] - display context (not needed for the root element) + * @param {Array.} [colors] - optional override for ENode.COLORS + * @returns {Element} + */ +ENode.prototype.toMathML = function(colors) { + var context = { hcolors: {}, depth: 0 , colors: colors}; + return(this._toMathML(context)); +} + +/** + * Transforms this ENode into a MathML HTML DOM element (internal function). + * @param {Object} context - display context * @param {Object.} context.hcolors - hash identifier->color * @param {number} context.depth - Depth in parenthesis, used for coloring + * @param {Array.} context.colors - optional override for ENode.COLORS * @returns {Element} */ -ENode.prototype.toMathML = function(context) { - var c0, c1, c2, c3, c4, i, j, el, par, mrow, mo, mtable, mfrac, msub, msup; - if (typeof context == "undefined") - context = { hcolors: {}, depth: 0 }; +ENode.prototype._toMathML = function(context) { + var c0, c1, c2, c3, c4, i, j, el, par, mrow, mo, mtable, mfrac, msub, msup, mrow2; if (this.children != null && this.children.length > 0) c0 = this.children[0]; else @@ -153,7 +191,9 @@ ENode.prototype.toMathML = function(cont } else { el = this.mi(this.value) } - el.setAttribute("mathcolor", this.getColorForIdentifier(this.value, context.hcolors)); + el.setAttribute("mathcolor", this.getColorForIdentifier(this.value, context)); + if (this.value.indexOf('$') === 0) + el.setAttribute("fontfamily", "monospace"); return(el); case ENode.NUMBER: @@ -175,8 +215,8 @@ ENode.prototype.toMathML = function(cont case ENode.OPERATOR: if (this.value == "/") { mfrac = document.createElement('mfrac'); - mfrac.appendChild(c0.toMathML(context)); - mfrac.appendChild(c1.toMathML(context)); + mfrac.appendChild(c0._toMathML(context)); + mfrac.appendChild(c1._toMathML(context)); el = mfrac; } else if (this.value == "^") { if (c0.type == ENode.FUNCTION) { @@ -193,14 +233,14 @@ ENode.prototype.toMathML = function(cont if (par) el.appendChild(this.addP(c0, context)); else - el.appendChild(c0.toMathML(context)); - el.appendChild(c1.toMathML(context)); + el.appendChild(c0._toMathML(context)); + el.appendChild(c1._toMathML(context)); } else if (this.value == "*") { mrow = document.createElement('mrow'); if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-")) mrow.appendChild(this.addP(c0, context)); else - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); // should the x operator be visible ? We need to check if there is a number to the left of c1 var firstinc1 = c1; while (firstinc1.type == ENode.OPERATOR) { @@ -213,23 +253,27 @@ ENode.prototype.toMathML = function(cont mrow.appendChild(this.mo("*")); else if (firstinc1.type == ENode.NUMBER) mrow.appendChild(this.mo("\u22C5")); + else if (context.units) + mrow.appendChild(this.mo("\u22C5")); + else + mrow.appendChild(this.mo("\u2062")); // invisible multiplication if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (this.value == "-") { mrow = document.createElement('mrow'); if (this.children.length == 1) { mrow.appendChild(this.mo("-")); - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); } else { - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); mrow.appendChild(this.mo("-")); if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); } el = mrow; } else if (this.value == "!") { @@ -238,13 +282,13 @@ ENode.prototype.toMathML = function(cont if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-")) mrow.appendChild(this.addP(c0, context)); else - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); mrow.appendChild(mo); el = mrow; } else if (this.value == "+") { mrow = document.createElement('mrow'); mo = this.mo(this.value); - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); mrow.appendChild(mo); // should we add parenthesis ? We need to check if there is a '-' to the left of c1 par = false; @@ -262,42 +306,50 @@ ENode.prototype.toMathML = function(cont if (par) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (this.value == ".") { mrow = document.createElement('mrow'); if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-")) mrow.appendChild(this.addP(c0, context)); else - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); mrow.appendChild(this.mo("\u22C5")); if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (this.value == "`") { mrow = document.createElement('mrow'); if (c0.type == ENode.OPERATOR && (c0.value == "+" || c0.value == "-")) mrow.appendChild(this.addP(c0, context)); else - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); // the units should not be in italics + // unit multiplication should not be displayed with an invisible operator var mstyle = document.createElement("mstyle"); mstyle.setAttribute("fontstyle", "normal"); + var units_context = {}; + for (var prop in context) { + if (context.hasOwnProperty(prop)) { + units_context[prop] = context[prop]; + } + } + units_context.units = true; if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) - mstyle.appendChild(this.addP(c1, context)); + mstyle.appendChild(this.addP(c1, units_context)); else - mstyle.appendChild(c1.toMathML(context)); + mstyle.appendChild(c1._toMathML(units_context)); mrow.appendChild(mstyle); el = mrow; } else { // relational operators mrow = document.createElement('mrow'); mo = this.mo(this.value); - mrow.appendChild(c0.toMathML(context)); + mrow.appendChild(c0._toMathML(context)); mrow.appendChild(mo); - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } return(el); @@ -306,24 +358,24 @@ ENode.prototype.toMathML = function(cont // c0 contains the function name if (c0.value == "sqrt" && c1 != null) { el = document.createElement('msqrt'); - el.appendChild(c1.toMathML(context)); + el.appendChild(c1._toMathML(context)); } else if (c0.value == "abs" && c1 != null) { mrow = document.createElement('mrow'); mrow.appendChild(this.mo("|")); - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); mrow.appendChild(this.mo("|")); el = mrow; } else if (c0.value == "exp" && c1 != null) { el = document.createElement('msup'); el.appendChild(this.mi("e")); - el.appendChild(c1.toMathML(context)); + el.appendChild(c1._toMathML(context)); } else if (c0.value == "factorial") { mrow = document.createElement('mrow'); mo = this.mo("!"); if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); mrow.appendChild(mo); el = mrow; } else if (c0.value == "diff" && this.children != null && this.children.length == 3) { @@ -338,27 +390,27 @@ ENode.prototype.toMathML = function(cont if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (c0.value == "diff" && this.children != null && this.children.length == 4) { mrow = document.createElement('mrow'); mfrac = document.createElement('mfrac'); msup = document.createElement('msup'); msup.appendChild(this.mi("d")); - msup.appendChild(c3.toMathML(context)); + msup.appendChild(c3._toMathML(context)); mfrac.appendChild(msup); var f2 = document.createElement('mrow'); f2.appendChild(this.mi("d")); msup = document.createElement('msup'); - msup.appendChild(c2.toMathML(context)); - msup.appendChild(c3.toMathML(context)); + msup.appendChild(c2._toMathML(context)); + msup.appendChild(c3._toMathML(context)); f2.appendChild(msup); mfrac.appendChild(f2); mrow.appendChild(mfrac); if (c1.type == ENode.OPERATOR && (c1.value == "+" || c1.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (c0.value == "integrate" && this.children != null && this.children.length == 3) { mrow = document.createElement('mrow'); @@ -368,9 +420,9 @@ ENode.prototype.toMathML = function(cont if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); mrow.appendChild(this.mi("d")); - mrow.appendChild(c2.toMathML(context)); + mrow.appendChild(c2._toMathML(context)); el = mrow; } else if (c0.value == "integrate" && this.children != null && this.children.length == 5) { mrow = document.createElement('mrow'); @@ -378,15 +430,15 @@ ENode.prototype.toMathML = function(cont var mo = this.mo("\u222B"); mo.setAttribute("stretchy", "true"); // doesn't work with MathJax msubsup.appendChild(mo); - msubsup.appendChild(c3.toMathML(context)); - msubsup.appendChild(c4.toMathML(context)); + msubsup.appendChild(c3._toMathML(context)); + msubsup.appendChild(c4._toMathML(context)); mrow.appendChild(msubsup); if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); mrow.appendChild(this.mi("d")); - mrow.appendChild(c2.toMathML(context)); + mrow.appendChild(c2._toMathML(context)); el = mrow; } else if (c0.value == "sum" && this.children != null && this.children.length == 5) { mrow = document.createElement('mrow'); @@ -394,17 +446,17 @@ ENode.prototype.toMathML = function(cont var mo = this.mo("\u2211"); mo.setAttribute("stretchy", "true"); // doesn't work with MathJax munderover.appendChild(mo); - var mrow2 = document.createElement('mrow'); - mrow2.appendChild(c2.toMathML(context)); + mrow2 = document.createElement('mrow'); + mrow2.appendChild(c2._toMathML(context)); mrow2.appendChild(this.mo("=")); - mrow2.appendChild(c3.toMathML(context)); + mrow2.appendChild(c3._toMathML(context)); munderover.appendChild(mrow2); - munderover.appendChild(c4.toMathML(context)); + munderover.appendChild(c4._toMathML(context)); mrow.appendChild(munderover); if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (c0.value == "product" && this.children != null && this.children.length == 5) { mrow = document.createElement('mrow'); @@ -412,17 +464,17 @@ ENode.prototype.toMathML = function(cont var mo = this.mo("\u220F"); mo.setAttribute("stretchy", "true"); // doesn't work with MathJax munderover.appendChild(mo); - var mrow2 = document.createElement('mrow'); - mrow2.appendChild(c2.toMathML(context)); + mrow2 = document.createElement('mrow'); + mrow2.appendChild(c2._toMathML(context)); mrow2.appendChild(this.mo("=")); - mrow2.appendChild(c3.toMathML(context)); + mrow2.appendChild(c3._toMathML(context)); munderover.appendChild(mrow2); - munderover.appendChild(c4.toMathML(context)); + munderover.appendChild(c4._toMathML(context)); mrow.appendChild(munderover); if (c2.type == ENode.OPERATOR && (c2.value == "+" || c2.value == "-")) mrow.appendChild(this.addP(c1, context)); else - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (c0.value == "limit") { mrow = document.createElement('mrow'); @@ -432,9 +484,9 @@ ENode.prototype.toMathML = function(cont var munder = document.createElement('munder'); munder.appendChild(this.mo("lim")); var mrowunder = document.createElement('mrow'); - mrowunder.appendChild(c2.toMathML(context)); + mrowunder.appendChild(c2._toMathML(context)); mrowunder.appendChild(this.mo("\u2192")); - mrowunder.appendChild(c3.toMathML(context)); + mrowunder.appendChild(c3._toMathML(context)); if (c4 != null) { if (c4.value == "plus") mrowunder.appendChild(this.mo("+")); @@ -444,7 +496,7 @@ ENode.prototype.toMathML = function(cont munder.appendChild(mrowunder); mrow.appendChild(munder); } - mrow.appendChild(c1.toMathML(context)); + mrow.appendChild(c1._toMathML(context)); el = mrow; } else if (c0.value == "binomial") { // displayed like a vector @@ -453,7 +505,7 @@ ENode.prototype.toMathML = function(cont mtable = document.createElement('mtable'); for (i=1; i 0) + mrow2.appendChild(this.mo(";")); + mrow2.appendChild(this.children[i]._toMathML(context)); + } + mrow.appendChild(mrow2); + mrow.appendChild(this.mo("}")); + return(mrow); + case ENode.SUBSCRIPT: msub = document.createElement('msub'); - msub.appendChild(c0.toMathML(context)); + msub.appendChild(c0._toMathML(context)); if (this.children.length > 2) { mrow = document.createElement('mrow'); for (i=1; i