Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"DollarMath" option to turn off markdown when using KaTex and MathJax DollarMath Quotes in markdown #693

Open
wm2015email opened this issue May 5, 2019 · 4 comments

Comments

@wm2015email
Copy link

wm2015email commented May 5, 2019

Any chance you could add an option to turnoff markdown conversion between the tags:

$ ... $

and

$$ ... $$

This is required for markdown used in combination with mathjax or KaTex… such as on math.stackexchange.com... see markdown used on this website... or typera ...or markdown in Jupiter notebooks... the use of these DollarMath tags in markdown is fairly universal these days to delimit latex embedded in markdown...

I just noticed that when I convert markdown with embedded mathjax… some of the equations get messed up by markdown tags being converted inside of math equation fence characters of "$" and "$$"

would be nice to have this as an option. example:

converter = new showdown.Converter({underline: true, mathjax:true});

one example was this:

This is the markdown of the mathjax before conversion to html by showdown:

<p>$$
\delta(n) = \begin{cases}
1 & n = 0 \\
0 & otherwise
\end{cases}
$$<\p>

after conversion it converts "&" into & amp; ... mathjax doesn't like this... and also the double underscore \\ is converted into a single underscore \...which blow up mathjax equation as well... example of bad convertion of mathjax into html by showdown:

<p>$$
\delta(n) = \begin{cases}
1 & amp; n = 0 \
0 & amp; otherwise
\end{cases}
$$</p>

When the above is rendered into mathjax it blows up the equation and doesn't work...

Another bug case was:

$$ f_s = \frac{1}{T_s} $$

This had a problem, that thankful your underline option fixed it... but without underscore option set to true... the mathjax equation gets blowup with incorrect syntax as:

$$ f<em>s = \frac{1}T<em>s $$

mathjax doesn't like underscores converted to <em> tag..

in short, its probably safer to turn off markup processing between "$" .. "$" and "$$" .. "$$" tags.... these are the tag markers that are universal used in markup to indicate that a latex math equation is contained within and that markdown conversion needs to be temporary turned off while inside of these tags... that's why I think you should add "mathjax" option to your tool to do this processing for people using your library.

however, you really shouldn't convert ampersands inside of these mathjax blocks into & amp; ... or double backslash \\ into signle backslash \... mathjax needs a raw ampersand and raw backslash... I would put that on your bug list... its quite common to use ampersands in mathjax script... you need a way to pass a raw ampersand though your script when its in the math fence block.

@wm2015email
Copy link
Author

Here's some code i was using to test your markdown engine in combination with mathjax:

<!DOCTYPE html>
<html>
  <head>
    <title>Read File (via User Input selection)</title>
	
	<!-- node.js packages required: -->
	<!--     npm install jquery -->
	<!--     npm install showdown -->
	<!--     npm install mathjax -->
	
    <script type="text/javascript" src="./node_modules/showdown/dist/jquery.js"></script>
	
    <script type="text/javascript" src="./node_modules/showdown/dist/showdown.js"></script>
	
    <script type="text/x-mathjax-config">
        MathJax.Hub.Config({
            tex2jax: {inlineMath: [["$","$"],["\\(","\\)"]]}
    });
    </script>
	
    <script type="text/javascript" 
	    src="./node_modules/mathjax/MathJax.js?config=TeX-AMS_HTML-full"></script>

	
    <script type="text/javascript">
    var reader; 

    function checkFileAPI() {
        if (window.File && window.FileReader && window.FileList && window.Blob) {
            reader = new FileReader();
            return true; 
        } else {
            alert('The File APIs are not fully supported by your browser. Fallback required.');
            return false;
        }
    }

    function readText(filePath) {
        var output = ""; //placeholder for text output
        if(filePath.files && filePath.files[0]) {           
            reader.onload = function (e) {
                output = e.target.result;
                displayContents(output);
            };//end onload()
            reader.readAsText(filePath.files[0]);
        }//end if html5 filelist support
        else if(ActiveXObject && filePath) { //fallback to IE 6-8 support via ActiveX
            try {
                reader = new ActiveXObject("Scripting.FileSystemObject");
                var file = reader.OpenTextFile(filePath, 1); //ActiveX File Object
                output = file.ReadAll(); //text contents of file
                file.Close(); //close file "input stream"
                displayContents(output);
            } catch (e) {
                if (e.number == -2146827859) {
                    alert('Unable to access local files due to browser security settings. ' + 
                     'To overcome this, go to Tools->Internet Options->Security->Custom Level. ' + 
                     'Find the setting for "Initialize and script ActiveX controls not marked as safe" and change it to "Enable" or "Prompt"'); 
                }
            }       
        }
        else { //this is where you could fallback to Java Applet, Flash or similar
            return false;
        }       
        return true;
    }   

    function displayContents(txt) {
        showdown.setOption('underline', true);
		converter = new showdown.Converter({underline: true});
        showdown.setOption('underline', true);
		html = converter.makeHtml(txt);
        var el = document.getElementById('main'); 
        el.innerHTML = html; //display output in DOM

        MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
    }   
</script>
</head>

<body onload="checkFileAPI();">
    <div id="container">    
        <input type="file" onchange='readText(this)' />
        <br/>
        <hr/>   
		
        <h3>Contents of the Text file:</h3>
		
        <div id="main">
            ...
        </div>
    </div>
</body>

</html>

@wm2015email
Copy link
Author

wm2015email commented May 5, 2019

See below, I can use showdown perfectly with mathjax by calling the javascript function below to encode all of the "mathjax dollar quotes" with base64, then to run showdown engine on this modified markdown, then to unencode the resulting html from mathjax to remove base64 encoding of the math expression.:

that's an easy way to build "mathdollar" option into your markdown library... just base64 encode anything inbetween $ .. $ and $$ .. $$...and then base64 decode when you are done... its a little bit messy however, because i'm not 100% sure about the latex rules for preventing runaway uncloced math dollar quotes. but i think its something like this:

//npm install showdown
//npm install mathjax-node
//npm install mathjax

console.log("start")
function btoa(s) {
	s = s.replace(/[^\x00-\x7F]/g, "");
    return Buffer.from(s).toString('base64');
}

function atob(s) {
	return Buffer.from(s, 'base64').toString();
}

function isEmptyOrSpaces(str){
    return str === null || str.match(/^[\s\n\r]*$/) !== null;
}

function cwarn(linenumber, mode, msg) {
	var ln = linenumber;
	console.log(mode);
	var msg2 = ""
	msg2 += "WARNING:"
	msg2 += " mode:"; 
	msg2 += mode; 
	msg2 += " line:"; 
	msg2 += linenumber; 
    msg2 += " msg:";  
	msg2 += msg;
	msg2 += "\n";
	console.log(msg2);
}

function mathjax_coder(mode, s) {
    //console.log("mathjax_encode: mode:"  + mode);
    //console.log("length = "  + s.length);
    	
	linenumber = 1;
	
    if (!(mode == 'e' || mode == 'd')) {
	   throw "invalid mode, required: e=encode, d=decode";
	}
	
    var ostr  = "";
	var enc   = "";
	var state = 0;
	var line  = "";
	var dcount = 0; // math inlines per line
	var ddcount = 0;
    for(i=0; i < s.length; i++) {
	    c = s.charAt(i);
	    line += c;
			
		// blank line terminates open math dollar
		if (c == '\n' && isEmptyOrSpaces(line) && state != 0) {
            cwarn(linenumber, mode, "math dolar not terminated");
			state = 0;		    		
		}
		
		
	    // run away inline
	    if ((c == '\n') && (state != 0) && type == 1) {
            cwarn(linenumber, mode, "runaway inline math dollar not balanced");
			state = 0;
		}
		
	    // $$ pair + another $ pair not allowed on the same line
	    if ((c == '\n') && (ddcount > 1)) {
            cwarn(linenumber, mode, "only one \$\$ pair allowed per line");
			state = 0;		    
		}
		
		
	    if (c == '\n') {
		    linenumber++;
			line = "";
			dcount=0;
			ddcount=0;
		}
		
  	    //console.log(i + " " + c + "\n");
        switch(state) {
	   
	    // non-encoding
	    case 0:
		    // start marker1
            if (c == '$') {
	            state = 1;
				type  = 1;
		        enc   = "";
			    ostr +=  c; 
				dcount++;
		    }
			
			// non-encoded
			else {
	            state = 0;
   		        enc   = "";
			    ostr +=  c;
			}
	        break
		  
		// marker1
		case 1:
		    // start marker2
		    if (s.charAt(i) == '$') {
			    ostr +=  c; 
			    state = 3;
				type = 2;
				ddcount++;
			}
			// start encoding
			else {
			    type   = 1;
			    enc   += c;
			    state  = 3;
			}
			break;
	   
        //encoding
        case 3:
	        // stop marker1
		    if (s.charAt(i) == '$') {
				   if (type == 1) {
			  //       console.log("ENC1: " + enc)
					   state = 0;
			           ostr += (mode == 'e') ? btoa(enc) : atob(enc);
					   ostr += "$";
				   }
				   else {
					   state = 4;
				   }
			}
			else {
			    state  = 3;
			    enc   += c;
			}
		    
            break;		

	    // stop marker2
 	    case 4: 
	        if (s.charAt(i) != '$') {
			    console.log("WARNING: (" + mode + ") closing math dollar not balanced at line:" + linenumber );
				console.log("LINE:" + line);
				console.log("HERE:(" + enc + ")");
				//process.exit(1);
		    }
			state = 0;
	       //onsole.log("ENC2: " + enc)
	 		ostr += (mode == 'e') ? btoa(enc) : atob(enc);
			ostr += "$";
			ostr += "$";
            break;
			

		
        } //switch	
		
        //if ((linenumber > 2086)) {
		//	line2 = line.replace('\n', '<lf>');
		//	line2 = line2.replace('\r', '<cr>');
		//    console.log("!>>" + line2 + "<<" + state + "type:" + type);
		//}
        //if ((linenumber ==  2090)) {
		//    process.exit(1);
		//}
		
    } // for loop
	
	//console.log("OSTR:" + ostr)
	return ostr;

}


function MathJaxWrapper(html) {
    h = "\n";
    h += "<!DOCTYPE html>\n";
    h += "<html>\n";
    h += "<head>\n";
    h += "   <title>Markdown TexDollar Reader</title>\n";
    h += "   <script type='text/x-mathjax-config'>\n";
    h += "        MathJax.Hub.Config({\n";
    h += "          tex2jax: {inlineMath: [['$','$']]}\n";
    h += "    });\n";
    h += "    </script>\n";
    h += "    <script type='text/javascript'\n"; 
    h += "	    src='./node_modules/mathjax/MathJax.js?config=TeX-AMS_HTML-full'></script>\n";
    h += "</head>\n";
    h += "\n";
    h += "<body>\n";  
    h += html;
    h += "</body>\n";
    h += "</html>\n";   
	return h;
}


var file = 'test1.md';

var filename  = "out2.html";

var fs        = require('fs');
var showdown  = require('showdown'); 
var mark1     = fs.readFileSync(file, 'utf8');
var mark2     = mathjax_coder('e', mark1);

fs.writeFileSync("mark2.md", mark2);

var converter = new showdown.Converter();
var h1    = converter.makeHtml(mark2);
fs.writeFileSync("h1.md", h1);

var h2    = mathjax_coder('d', h1);
var JaxHtml   = MathJaxWrapper(h2);

console.log("Write File: " + filename);
fs.writeFileSync(filename, JaxHtml);

console.log("exit!");
process.exit();

@obedm503
Copy link

obedm503 commented May 5, 2019

if you don't specifically need mathjax, you could use katex with the showdown-katex extension and won't have to wait for a change in showdown itself. katex does basically everything mathjax does but faster

@wm2015email
Copy link
Author

thanks. that's a good tip.

@wm2015email wm2015email changed the title "mathjax" option needed to turn off markdown when text surround by "$" "$" "DollarMath" option to turn off markdown when using KaTex and MathJax DollarMath Quotes in markdown May 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants