File:  [LON-CAPA] / loncom / html / adm / LC_math_editor / src / ui.js
Revision 1.1: download - view: text, annotated - select for diffs
Wed Sep 24 18:14:39 2014 UTC (9 years, 11 months ago) by damieng
Branches: MAIN
CVS tags: HEAD
added new LON-CAPA math editor for mathresponse and formularesponse to replace the DragMath applet

/*

Copyright (C) 2014  Michigan State University Board of Trustees

The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version.  The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.

As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.

*/

"use strict";

var handleChange = function(math_object) {
    // math_object has 3 fields: ta, output_node, oldtxt
    // we need to pass this object instead of the values because oldtxt will change
    var ta, output_node, txt, parser, output, root, test1, test2;
    ta = math_object.ta;
    output_node = math_object.output_node;
    txt = ta.value;
    
    // automatically add brackets to something like "1;2;3", for LON-CAPA:
    // NOTE: this is ugly and sometimes adds brackets to error messages
    test1 = '';
    test2 = txt;
    while (test2 != test1) {
      test1 = test2;
      test2 = test1.replace(/\[[^\[\]]*\]/g, '');
    }
    if (test2.split("[").length == test2.split("]").length) {
      test1 = '';
      while (test2 != test1) {
        test1 = test2;
        test2 = test1.replace(/\([^\(\)]*\)/g, '');
      }
      if (test2.split("(").length == test2.split(")").length) {
        if (test2.indexOf(Definitions.ARG_SEPARATOR) != -1) {
          txt = '['+txt+']';
        }
      }
    }
    
    if (txt != math_object.oldtxt) {
        math_object.oldtxt = txt;
        while (output_node.firstChild != null)
            output_node.removeChild(output_node.firstChild);
        output_node.removeAttribute("title");
        if (txt != "") {
            parser = math_object.parser;
            try {
                root = parser.parse(txt);
                if (root != null) {
                    var math = document.createElement("math");
                    math.setAttribute("display", "block");
                    math.appendChild(root.toMathML());
                    output_node.appendChild(math);
                    MathJax.Hub.Queue(["Typeset", MathJax.Hub, output_node]);
                }
            } catch (e) {
                output = "error: " + e;
                output_node.setAttribute("title", output);
                if (e instanceof ParseException) {
                    output_node.appendChild(document.createTextNode(txt.substring(0, e.from)));
                    var span = document.createElement('span');
                    span.appendChild(document.createTextNode(txt.substring(e.from, e.to + 1)));
                    span.className = 'math-error';
                    output_node.appendChild(span);
                    if (e.to < txt.length - 1) {
                        output_node.appendChild(document.createTextNode(txt.substring(e.to + 1)));
                    }
                } else {
                    var tn = document.createTextNode(output);
                    output_node.appendChild(tn);
                }
            }
        }
    }
}

var init_done = false;

/*
  Looks for elements with the "math" class, and
  adds a preview div afterward which is updated automatically.
*/
var initEditors = function() {
    if (init_done)
        return;
    init_done = true;
    var math_objects = [];
    var math_inputs = document.getElementsByClassName('math');
    for (var i=0; i<math_inputs.length; i++) {
        var ta = math_inputs[i];
        if (ta.nodeName == "TEXTAREA" || ta.nodeName == "INPUT") {
            var output_node = document.createElement("span");
            output_node.setAttribute("style", "display:none");
            if (ta.nextSibling)
                ta.parentNode.insertBefore(output_node, ta.nextSibling);
            else
                ta.parentNode.appendChild(output_node);
            var hideNode = function(node) {
                return function(e) { node.setAttribute("style", "display:none"); };
            };
            var showNode = function(node) {
                return function(e) { node.setAttribute("style", "display: inline-block; background-color: #FFFFE0"); };
            };
            ta.addEventListener("blur", hideNode(output_node), false);
            ta.addEventListener("focus", showNode(output_node), false);
            var implicit_operators = (ta.getAttribute("data-implicit_operators") === "true");
            var unit_mode = (ta.getAttribute("data-unit_mode") === "true");
            var constants = ta.getAttribute("data-constants");
            if (constants)
                constants = constants.split(/[\s,]+/);
            var oldtxt = "";
            math_objects[i] = {
                "ta": ta,
                "output_node": output_node,
                "oldtxt": oldtxt,
                "parser": new Parser(implicit_operators, unit_mode, constants)
            };
            var changeObjectN = function(n) {
                return function(e) {
                  var obj = math_objects[n];
                  if (document.activeElement == obj.ta) {
                    // this is useful if there is data in the field with the page default focus
                    // (there might not be a focus event for the active element)
                    obj.output_node.setAttribute("style", "display: inline-block; background-color: #FFFFE0");
                  }
                  handleChange(obj);
                };
            };
            var startChange = changeObjectN(i);
            if (ta.value != oldtxt)
                startChange(); // process non-empty fields even though they are not visible yet
            ta.addEventListener('change', startChange, false);
            ta.addEventListener('keyup', startChange, false);
        }
    }
}


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