/*
 * jsTeX
 * 
 * Renders (some of) TeX inline formatting rules and entities into HTML
 * directly upon loading the document in the browser. 
 * 
 * Inspired and meant to be a companion to the outstanding jsMath package that
 * does the same for TeX's math mode. 
 * 
 * Author: Stefano Mazzocchi <stefanom@mit.edu>
 * Copyright: Massachusetts Institute of Technology
 * License: BSD
 * Encoding: UTF-8
 */

(function() {
    if (typeof window.jsTeX == "undefined") {

        var log = function(msg) {
            if (jsTeX.log && console && console.log) {
                console.log(msg);
            }
        }
        
        var chars = {
            "\"": {
                "A" : "Ä",
                "E" : "Ë",
                "H" : "Ḧ",
                "I" : "Ï",
                "O" : "Ö",
                "U" : "Ü",
                "W" : "Ẅ",
                "X" : "Ẍ",
                "Y" : "Ÿ",
                
                "a" : "ä",
                "e" : "ë",
                "h" : "ḧ",
                "i" : "ï",
                "o" : "ö",
                "t" : "ẗ",
                "u" : "ü",
                "w" : "ẅ",
                "x" : "ẍ",
                "y" : "ÿ"
            },
            "'" : {
                "A" : "Á",
                "C" : "Ć",
                "E" : "É",
                "G" : "Ǵ",
                "I" : "Í",
                "K" : "Ḱ",
                "L" : "Ĺ",
                "M" : "Ḿ",
                "N" : "Ń",
                "O" : "Ó",
                "P" : "Ṕ",
                "R" : "Ŕ",
                "S" : "Ś",
                "U" : "Ú",
                "W" : "Ẃ",
                "Y" : "Ý",
                "Z" : "Ź",

                "a" : "á",
                "c" : "ć",
                "e" : "é",
                "g" : "ǵ",
                "i" : "í",
                "k" : "ḱ",
                "l" : "ĺ",
                "m" : "ḿ",
                "n" : "ń",
                "o" : "ó",
                "p" : "ṕ",
                "r" : "ŕ",
                "s" : "ś",
                "u" : "ú",
                "w" : "ẃ",
                "y" : "ý",
                "z" : "ź"
            },
            "." : {
                "A" : "Ȧ",
                "B" : "Ḃ",
                "C" : "Ċ",
                "D" : "Ḋ",
                "E" : "Ė",
                "F" : "Ḟ",
                "G" : "Ġ",
                "H" : "Ḣ",
                "I" : "İ",
                "M" : "Ṁ",
                "N" : "Ṅ",
                "O" : "Ȯ",
                "P" : "Ṗ",
                "R" : "Ṙ",
                "S" : "Ṡ",
                "T" : "Ṫ",
                "W" : "Ẇ",
                "X" : "Ẋ",
                "Y" : "Ẏ",
                "Z" : "Ż",
                
                "a" : "ȧ",
                "b" : "ḃ",
                "c" : "ċ",
                "d" : "ḋ",
                "e" : "ė",
                "f" : "ḟ",
                "g" : "ġ",
                "h" : "ḣ",
                "m" : "ṁ",
                "n" : "ṅ",
                "o" : "ȯ",
                "p" : "ṗ",
                "r" : "ṙ",
                "s" : "ṡ",
                "t" : "ṫ",
                "w" : "ẇ",
                "x" : "ẋ",
                "y" : "ẏ",
                "z" : "ż"
            },
            "=" : {
                "A" : "Ā",
                "E" : "Ē",
                "G" : "Ḡ",
                "I" : "Ī",
                "O" : "Ō",
                "U" : "Ū",
                "Y" : "Ȳ",
                
                "a" : "ā",
                "e" : "ē",
                "g" : "ḡ",
                "i" : "ī",
                "o" : "ō",
                "u" : "ū",
                "y" : "ȳ"
            },
            "^": {
                "A" : "Â",
                "C" : "Ĉ",
                "E" : "Ê",
                "G" : "Ĝ",
                "H" : "Ĥ",
                "I" : "Î",
                "J" : "Ĵ",
                "O" : "Ô",
                "S" : "Ŝ",
                "U" : "Û",
                "W" : "Ŵ",
                "Y" : "Ŷ",
                "Z" : "Ẑ",
                
                "a" : "â",
                "c" : "ĉ",
                "e" : "ê",
                "g" : "ĝ",
                "h" : "ĥ",
                "i" : "î",
                "j" : "ĵ",
                "o" : "ô",
                "s" : "ŝ",
                "u" : "û",
                "w" : "ŵ",
                "y" : "ŷ",
                "z" : "ẑ"
            },
            "`" : {
                "A" : "À",
                "E" : "È",
                "I" : "Ì",
                "N" : "Ǹ",
                "O" : "Ò",
                "U" : "Ù",
                "W" : "Ẁ",
                "Y" : "Ỳ",
                
                "a" : "à",
                "e" : "è",
                "i" : "ì",
                "n" : "ǹ",
                "o" : "ò",
                "u" : "ù",
                "w" : "ẁ",
                "y" : "ỳ"
            },
            "~": {
                "A" : "Ã",
                "E" : "Ẽ",
                "I" : "Ĩ",
                "N" : "Ñ",
                "O" : "Õ",
                "U" : "Ũ",
                "V" : "Ṽ",
                "Y" : "Ỹ",
                
                "a" : "ã",
                "e" : "ẽ",
                "i" : "ĩ",
                "n" : "ñ",
                "o" : "õ",
                "u" : "ũ",
                "v" : "ṽ",
                "y" : "ỹ"
            },
            "b" : {
                "B" : "Ḇ",
                "D" : "Ḏ",
                "K" : "Ḵ",
                "L" : "Ḻ",
                "N" : "Ṉ",
                "R" : "Ṟ",
                "T" : "Ṯ",
                "Z" : "Ẕ",
                
                "b" : "ḇ",
                "d" : "ḏ",
                "h" : "ẖ",
                "k" : "ḵ",
                "l" : "ḻ",
                "n" : "ṉ",
                "r" : "ṟ",
                "t" : "ṯ",
                "z" : "ẕ"
            },
            "c" : {
                "C" : "Ç",
                "D" : "Ḑ",
                "E" : "Ȩ",
                "G" : "Ģ",
                "H" : "Ḩ",
                "K" : "Ķ",
                "L" : "Ļ",
                "N" : "Ņ",
                "R" : "Ŗ",
                "S" : "Ş",
                "T" : "Ţ",
                
                "c" : "ç",
                "d" : "ḑ",
                "e" : "ȩ",
                "h" : "ḩ",
                "k" : "ķ",
                "l" : "ļ",
                "n" : "ņ",
                "r" : "ŗ",
                "s" : "ş",
                "t" : "ţ"
            },
            "d" : {
                "A" : "Ạ",
                "B" : "Ḅ",
                "D" : "Ḍ",
                "E" : "Ẹ",
                "H" : "Ḥ",
                "I" : "Ị",
                "K" : "Ḳ",
                "L" : "Ḷ",
                "M" : "Ṃ",
                "N" : "Ṇ",
                "O" : "Ọ",
                "R" : "Ṛ",
                "S" : "Ṣ",
                "T" : "Ṭ",
                "U" : "Ụ",
                "V" : "Ṿ",
                "W" : "Ẉ",
                "Y" : "Ỵ",
                "Z" : "Ẓ",
                
                "a" : "ạ",
                "b" : "ḅ",
                "d" : "ḍ",
                "e" : "ẹ",
                "h" : "ḥ",
                "i" : "ị",
                "k" : "ḳ",
                "l" : "ḷ",
                "m" : "ṃ",
                "n" : "ṇ",
                "o" : "ọ",
                "r" : "ṛ",
                "s" : "ṣ",
                "t" : "ṭ",
                "u" : "ụ",
                "v" : "ṿ",
                "w" : "ẉ",
                "y" : "ỵ",
                "z" : "ẓ"
            },
            "r" : {
                "A" : "Å",
                "U" : "Ů",
                
                "a" : "å",
                "u" : "ů",
                "w" : "ẘ",
                "y" : "ẙ"
            },
            "v" : {
                "A" : "Ǎ",
                "C" : "Č",
                "D" : "Ď",
                "E" : "Ě",
                "G" : "Ǧ",
                "H" : "Ȟ",
                "I" : "Ǐ",
                "K" : "Ǩ",
                "N" : "Ň",
                "O" : "Ǒ",
                "R" : "Ř",
                "S" : "Š",
                "T" : "Ť",
                "U" : "Ǔ",
                "Z" : "Ž",
                
                "a" : "ǎ",
                "c" : "č",
                "e" : "ě",
                "g" : "ǧ",
                "h" : "ȟ",
                "i" : "ǐ",
                "k" : "ǩ",
                "n" : "ň",
                "o" : "ǒ",
                "r" : "ř",
                "s" : "š",
                "u" : "ǔ",
                "z" : "ž"
            },
            "u" : {
                "A" : "Ă",
                "E" : "Ĕ",
                "G" : "Ğ",
                "I" : "Ĭ",
                "O" : "Ŏ",
                "U" : "Ŭ",
                
                "a" : "ă",
                "e" : "ĕ",
                "g" : "ğ",
                "i" : "ĭ",
                "o" : "ŏ",
                "u" : "ŭ"
            },

            "$": "$",
            "%": "%",
            "{": "{",
            "}": "}",
            "&": "&",
            "_": "_",
            "/": " ",    // is this correct?
            
            "o": "ø",
            "O": "Ø",
            "l": "ł",
            "L": " Ł",
            
            "P": "¶",
            "S": "§"
        }
        
        var strings = {
            "copyright": "©",
            "dag": "†",
            "ddag": "‡",
            "dots": "…",
            "pounds": "£",
            "AA": "Å",
            "aa": "å",
            "AE": "Æ",
            "ae": "æ",
            "OE": "Œ",
            "oe": "œ",
            "ss": "ß",
            "SS": "SS"
        }

        var commands = {
            "em" : "i",
            "emph" : "i",
            "bf" : "b"
        }   
             
        var processText = function(text) {
            var value = text.nodeValue;

            if (jsTeX.log) log("process: '" + value + "'");
            
            // first of all adjust quotes
            var startIndex = value.indexOf("``");
            if (startIndex > -1) {
                var endIndex = value.indexOf("''");
                if (endIndex > -1) {
                    text.nodeValue = value = value.replace(/``/,"“").replace(/''/,"”");
                }
            }

            var mode = 0;  // 0 = regular mode
            var state = 0; // 0 = regular state
            var scopes = [];
            var i = 0;
            var start = 0;
            var nested = 0;
            var command = "";
            var params = "";

            var processCommand = function() {
                if (jsTeX.log) log("command '" + command + "'('" + params + "')");
                
                var n = commands[command];
                if (n) {
                    if (jsTeX.log) log("tag: " + command + " -> " + n);
                    var e = document.createElement(n);
                    var t = document.createTextNode(params);
                    e.appendChild(t);
                    text.parentNode.replaceChild(e,text);
                    text = t;
                    processElement(e);
                } else {
                    var n = strings[command];
                    if (n) {
                        if (jsTeX.log) log("string: " + command + " -> " + n);
                        var value = text.nodeValue;
                        var index1 = value.indexOf(command);
                        var index2 = value.indexOf(params,index1);
                        value = value.substring(0,index-1) + n + params + value.substring(index2 + params.length + 1);
                    } else {
                        var c = command.charAt(0);
                        var ch = command.charAt(1);
                        command = command.substring(0,2);
                        var table = chars[c];
                        if (table) {
                            if (typeof table == "object") {
                                if (ch) {
                                    n = table[ch];
                                } else if (params) {
                                    n = table[params];
                                }
                            } else if (typeof table == "string") {
                                n = table;
                                command = command.substring(0,1);
                            }

                            if (!n) {
                                if (ch) {
                                    n = ch;
                                } else if (params) {
                                    n = params;
                                }
                            }
                            
                            if (jsTeX.log) log("char " + command + " -> " + n);
                            var value = text.nodeValue;
                            var index = value.indexOf(command);
                            if (ch) {
                                value = value.substring(0,index-1) + n + params + value.substring(index + command.length);
                            } else if (params) {
                                if (typeof table == "string") {
                                    value = value.substring(0,index-1) + n + params + value.substring(index + command.length + 2 + params.length);
                                } else {
                                    value = value.substring(0,index-1) + n + value.substring(index + command.length + 2 + params.length);
                                }
                            } else {
                                value = value.substring(0,index-1) + n + value.substring(index + command.length);
                            }
                        } else {
                            value = (params) ? params : "";
                        }
                    }
                    text.nodeValue = value;
                }
                
                command = "";
                params = "";
                
                return text;
            }
            
            var processMath = function() {
                if (jsTeX.log) log("math: " + start + " " + i + " " + value.length);
                var e = document.createElement("SPAN");
                e.className = "math";
                text.parentNode.replaceChild(e,text);
                e.appendChild(text);
                if (typeof window.jsMath != "undefined") {
                    jsMath.ProcessBeforeShowing(e.parentNode);
                } else {
                    jsTeX.log("can't process TeX math since jsMath was not found");
                }
            }
            
            var split = function() {
                if (jsTeX.log) log("split: " + start + " " + i + " " + value.length);
                if (i < value.length) text.splitText(i);
                if (start > 1) text.splitText(start - 1);
                if (start === 1 && i == value.length) {
                    if (value.charAt(0) === "\\") {
                        text = processCommand();
                    } else if (value.charAt(0) === "$") {
                        value = text.nodeValue;
                        text.nodeValue = value.substring(1,value.length - 1);
                        processMath();
                        return;
                    } else {
                        value = text.nodeValue;
                        text.nodeValue = value.substring(1,value.length - 1);
                    }
                }

                processText(text);

                // NOTE: the processElement method will continue 
                // with the processing of the new sibling text nodes
                // created with splitText() so we need to return right after
                // invoking this method
            }

            while (i < value.length) {
                var ch = value.charAt(i++);
                if (jsTeX.log) log("ch: " + ch + " " + mode + " " + state + " " + nested);
                switch(mode) {
                    case 1: // math mode
                        switch(ch) {
                            case "$":
                                split();
                                return;
                            default:
                                // ignore the rest in math mode (since jsMath does this job)
                        }
                        break;

                    case 2: // inline command mode
                        if (state === 1) { // collecting command parameters
                            switch(ch) {
                                case "{":
                                    if (value.charAt(i - 2) != '\\') { // avoid counting escaped ones
                                        nested++;
                                    }
                                    params += ch;
                                    break;
                                case "}":
                                    if (value.charAt(i - 2) != '\\') { // avoid counting escaped ones
                                        nested--;
                                    }
                                    if (nested === 0) {
                                        split();
                                        return;
                                    } else {
                                        params += ch;
                                    }
                                    break;
                                default:
                                    params += ch;
                            }
                        } else {
                            if (i == start + 1) { // needed for \{ and \} escapes
                                command += ch;
                            } else {
                                switch(ch) {
                                    case "{":
                                        state = 1; // collecting command parameters
                                        nested++;
                                        break;
                                    case " ":
                                        var tag = commands[command];
                                        if (jsTeX.log) log("command: " + command + " -> " + tag);
                                        if (nested == 0 && typeof tag == "undefined") {
                                            i--;
                                            split();
                                            return;
                                        } else {
                                            nested++; // fake an open brace
                                            state = 1;  // collecting command parameter
                                            break;
                                        }
                                    case "}":
                                        split();
                                        return;
                                    default:
                                        command += ch;
                                }
                            }
                        }
                        break;

                    case 3: // scope mode
                        switch(ch) {
                            case "{":
                                if (value.charAt(i - 2) != '\\') { // avoid counting escaped ones
                                    nested++;
                                }
                                break;
                            case "}":
                                if (value.charAt(i - 2) != '\\') { // avoid counting escaped ones
                                    nested--;
                                }
                                if (nested === 0) {
                                    split();
                                    return;
                                }
                            default:
                        }
                        break;

                    default: // in all other modes
                        switch(ch) {
                            case "$":
                                mode = 1; // enter math mode
                                start = i;
                                break;
                            case "\\":
                                mode = 2; // command mode
                                start = i;
                                break;
                            case "~":
                                value = value.substr(0, i - 1) + " " + value.substr(i);
                                text.nodeValue = value;
                                break;
                            case "{":
                                mode = 3 // scope mode
                                nested++;
                                start = i;
                                break;
                            default:
                        }
                }
            }
            
            if (command != '') {
                processCommand();
            }
        }
        
        var processElement = function(elmt) {
            if (jsTeX.log) log("processElement: " + elmt);            
            var children = elmt.childNodes;
            if (children) {
                for (var i = 0; i < children.length; i++) {
                    var e = children[i];
                    if (e.nodeType == 3) {
                        processText(e);
                    } else {
                        processElement(e);
                    }
                }
            }
        }

        window.jsTeX = {
          
            version: "1.0",
              
            log: false,
            
            process: function(elmt) {
                if (elmt.normalize) elmt.normalize();
                processElement(elmt);
            }
            
        };
        
    }
 
})();

