Skip to content

Instantly share code, notes, and snippets.

@karino2
Forked from hogeika/test.html
Created February 26, 2012 16:45
Show Gist options
  • Save karino2/1917598 to your computer and use it in GitHub Desktop.
Save karino2/1917598 to your computer and use it in GitHub Desktop.
EquationPad
<html>
<head>
<style type="text/css">
.selectedTarget { background-color: red}
</style>
</head>
<body onload="onload()">
<div id="history"></div>
<script language="javascript">
var g_VERVOSE = false;
function assertFail(msg) {
if(g_VERVOSE)
alert("fail! "+ msg);
throw msg;
}
function assertEq(exp, act) {
if(exp != act)
assertFail("not equal [" + exp + "] and [" + act + "]");
}
function assertNeq(exp, act) {
if(exp == act)
assertFail("not equal [" + exp + "] and [" + act + "]");
}
function $(id) {
return document.getElementById(id);
}
function CE(tag){
return document.createElement(tag);
}
var g_id = 1;
function IdGen() {
return g_id++;
}
function Mo(id, val) {
return "<mo id=\"" + id + "\">" + val + "</mo>";
}
function Mi(id, val) {
return "<mi id=\"" + id + "\">" + val + "</mi>";
}
function MnOrMi(term) {
if(term instanceof Number){
return "<mn id=\"" + term.objId + "\">" + term + "</mn>";
}
if(term instanceof String){
if(term[0] == '&') {
return Mi(term.objId, term);
}
return Mi(term.objId, term);
}
throw "Unknow terminal";
}
function EvalTerm(builder, term, nofence){
if(term instanceof Array){
if(!nofence) {
builder.push("<mfenced id=\"");
builder.push(term.objId);
builder.push("\"><mrow>");
}
builder.push(JsonToMathML(term));
if(!nofence)
builder.push("</mrow></mfenced>");
}else{
builder.push(MnOrMi(term));
}
}
function PushMo(builder, tagName) {
builder.push("<mo>");
builder.push(tagName);
builder.push("</mo>");
}
function IsMinus(term) {
return term instanceof Array && term[0] == "-";
}
function PushMinusTerm(builder, term) {
assertEq(true, IsMinus(term));
PushMo(builder, "-");
EvalTerm(builder,term[1]);
}
function EvalPlus(json) {
var builder = [];
var len = json.length;
if(IsMinus(json[1])){
PushMinusTerm(builder, json[1]);
}
else
EvalTerm(builder, json[1]);
for(var i = 2; i < len; i++) {
if(IsMinus(json[i])){
PushMinusTerm(builder, json[i]);
}
else {
PushMo(builder, "+");
EvalTerm(builder,json[i]);
}
}
return builder.join("");
}
function EvalTimes(json) {
var builder = [];
var len = json.length;
EvalTerm(builder, json[1]);
for(var i = 2; i < len; i++) {
if(json[i-1] instanceof Number && json[i] instanceof Number)
PushMo(builder, "&times;");
EvalTerm(builder,json[i]);
}
return builder.join("");
}
function EvalEqual(json) {
var builder = [];
EvalTerm(builder, json[1], true);
builder.push(Mo(json[0].objId, "="));
EvalTerm(builder,json[2], true);
return builder.join("");
}
function EvalPower(json) {
var builder = [];
builder.push("<msup>");
builder.push("<mrow>");
EvalTerm(builder, json[1]);
builder.push("</mrow>");
builder.push("<mrow>");
EvalTerm(builder, json[2], true);
builder.push("</mrow>");
builder.push("</msup>");
return builder.join("");
}
function EvalDotOver(json) {
var builder = [];
builder.push("<mover>");
EvalTerm(builder, json[1]);
builder.push("<mo>.</mo>");
builder.push("</mover>");
return builder.join("");
}
function EvalIntegral(json) {
var builder = [];
if(json.length == 3) {
builder.push(Mo(json[0].objId, "&Integral;"));
EvalTerm(builder, json[1], true);
// currently, json[2] does'nt support &rho;, etc.
builder.push(Mi(json[2].objId, "d" + json[2] ));
} else if(json.length == 5) {
builder.push("<msubsup>");
builder.push(Mo(json[0].objId, "&Integral;"));
EvalTerm(builder, json[3], true);
EvalTerm(builder, json[4], true);
builder.push("</msubsup>");
EvalTerm(builder, json[1], true);
// currently, json[2] does'nt support &rho;, etc.
builder.push(Mi(json[2].objId, "d" + json[2] ));
} else {
throw "unknown arg number of integral. ( " + json.length + ")";
}
return builder.join("");
}
function JsonToMathML(json) {
var tag = json[0];
if(tag == "-")
{
var builder = [];
PushMinusTerm(builder, json);
return builder.join("");
}
if(tag == "+")
{
return EvalPlus(json);
}
if(tag == "*")
{
return EvalTimes(json);
}
if(tag == "pow")
{
return EvalPower(json);
}
if(tag == "dotover")
{
return EvalDotOver(json);
}
if(tag == "int")
{
return EvalIntegral(json);
}
if(tag == "=")
{
return EvalEqual(json);
}
throw "unknown tag";
}
function dp(st) {
var con = $("console");
con.value += st + "\n";
}
function EncloseMath(str,isBlock){
var header ="<math " + (isBlock?"display=\"block\"":"") + "xmlns=\"http://www.w3.org/1998/Math/MathML\">"
var footer ="</math>"
return header + str + footer;
}
function showMath(str) {
var math = $("mathcanvas");
math.innerHTML = EncloseMath(str,true);
}
function onClear() {
var con = $("console");
con.value = '';
}
var g_hash = {};
function JsonParser(str) {
var sexp = eval(str);
var res = [];
res.objId = IdGen();
g_hash[res.objId] = res;
for(var i = 0; i < sexp.length; i++) {
var obj;
if(typeof(sexp[i]) == "number")
obj = new Number(sexp[i]);
else if(typeof(sexp[i]) == "string")
obj = new String(sexp[i]);
else if(sexp[i] instanceof Array)
obj = JsonParser(sexp[i]);
else
obj = sexp[i];
obj.objId = IdGen();
g_hash[obj.objId] = obj;
res.push(obj);
obj.parent = res;
}
assertNeq(res, sexp);
return res;
}
var g_sexp;
function clicked() {
g_sexp = JsonParser($("input").value);
var result = JsonToMathML(g_sexp);
dp(result);
showMath(result);
}
function UpdateSelection() {
if(g_selected) {
var elem = $(g_selected.obj.objId);
elem.setAttribute("class", "selectedTarget");
g_selected.elem = elem;
}
}
function InnerSexpSerializer(sexp, builder) {
if(sexp instanceof Number) {
builder.push(sexp.toString());
return;
}
if(sexp instanceof String) {
builder.push('"');
builder.push(sexp);
builder.push('"');
return;
}
if(sexp instanceof Array) {
builder.push("[");
for(var i = 0; i < sexp.length; i++) {
if(i != 0) {
builder.push(",");
}
InnerSexpSerializer(sexp[i], builder);
}
builder.push("]");
return;
}
alert(typeof(sexp));
throw "unknown atom type inside S Expression Serializer";
}
function SexpSerializer(sexp) {
var builder = [];
InnerSexpSerializer(sexp, builder);
return builder.join("");
}
function Update(sexp) {
var result = JsonToMathML(sexp);
dp(result);
showMath(result);
UpdateSelection();
$("input").value = SexpSerializer(sexp);
}
function FindIndex(arr, obj) {
for(var i = 0; i < arr.length; i++) {
if(arr[i] == obj)
return i;
}
return -1;
}
function MoveLeftTerm(sel) {
var parent = sel.obj.parent;
var i = FindIndex(parent, sel.obj);
if(i == 1)
return;
assertEq(parent[i], sel.obj);
var prev = parent[i-1];
parent[i] = prev;
parent[i-1] = sel.obj;
}
function MoveRightTerm(sel) {
var parent = sel.obj.parent;
var i = FindIndex(parent, sel.obj);
if(i == parent.length -1 )
return;
assertEq(parent[i], sel.obj);
var next = parent[i+1];
parent[i] = next;
parent[i+1] = sel.obj;
}
function Register(raw){
var history = $('history');
var history_entry = CE('div');
var form = CE('form');
var input = CE('input');
input.type = 'button';
input.value = 'revive';
input.addEventListener("click",Revive);
form.appendChild(input);
var hidden = CE('input');
hidden.type = 'hidden';
hidden.name = 'raw_exp';
hidden.value = raw;
form.appendChild(hidden);
var span = CE('span');
span.innerHTML = EncloseMath(JsonToMathML(JsonParser(raw)));
form.appendChild(span);
history_entry.appendChild(form);
history.appendChild(history_entry);
/*
<div>
<form>
<input type="button" value="revive" onclick="Revive(event);">
<input type="hidden" name="raw_exp" value="['+', 1, 2]">
<span>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>
&Laplacetrf;
</mi>
</math>
<span>
</form>
</div>
*/
}
function Revive(e){
var target = e.target;
var from = target.form;
var input = $('input');
input.value = from.elements.namedItem('raw_exp').value;
}
function Export(){
var forms = $('history').getElementsByTagName('form');
var builder = [];
builder.push('[');
for(var i = 0; i < forms.length; i++){
builder.push("'");
builder.push(encodeURI(forms.item(i).elements.namedItem('raw_exp').value));
builder.push("'");
builder.push(',');
}
builder.push(']');
prompt("Export", builder.join(""));
}
function Inport(){
var input = $('input').value;
var array = eval(input);
for(var i=0; i < array.length; i++){
Register(decodeURI(array[i]));
}
}
function assertMatch(expectPat, actual) {
if(!actual.match(expectPat)) {
assertFail("pat not match. pat[" + expectPat + "], actual [" + actual + "]");
}
}
function verify(input, expectPat, needLog) {
var sexp = JsonParser(input);
var result = JsonToMathML(sexp);
var actual_pat = expectPat;
if(needLog)
dp(result);
assertMatch(actual_pat, result);
}
function EvalTest() {
verify('["pow", "e", "a"]', /<msup><mrow><mi[^>]*>e<\/mi><\/mrow><mrow><mi[^>]*>a<\/mi><\/mrow><\/msup>/);
verify('["pow", "e", ["*", "a", "b"]]', /<msup><mrow><mi[^>]*>e<\/mi><\/mrow><mrow><mi[^>]*>a<\/mi><mi id="[0-9]*">b<\/mi><\/mrow><\/msup>/);
verify('["*", "e", ["-", "a"]]', /<mi id="[0-9]*">e<\/mi><mfenced id="[0-9]*"><mrow><mo>-<\/mo><mi id="[0-9]*">a<\/mi><\/mrow>/)
verify('["+", "&lambda;", "a"]', /<mi id="[0-9]*">&lambda;<\/mi><mo>\+<\/mo><mi id="[0-9]*">a<\/mi>/);
verify('["dotover", "x"]', /<mover><mi id="[0-9]*">x<\/mi><mo>.<\/mo><\/mover>/);
verify('["int", ["+", "x", "y"], "t"]', /<mo id="[0-9]*">&Integral;<\/mo><mi id="[0-9]*">x<\/mi><mo>\+<\/mo><mi id="[0-9]*">y<\/mi><mi id="[0-9]*">dt<\/mi>/);
verify('["int", "x", "t"]', /<mo id="[0-9]*">&Integral;<\/mo><mi id="[0-9]*">x<\/mi><mi id="[0-9]*">dt<\/mi>/);
verify('["int", "x", "t", 0, "&infin;"]', /<msubsup><mo id="[0-9]*">&Integral;<\/mo><mn id="[0-9]*">0<\/mn><mi id="[0-9]*">&infin;<\/mi><\/msubsup><mi id="[0-9]*">x<\/mi><mi id="[0-9]*">dt<\/mi>/);
verify('["=", "x", "y"]', /<mi id="[0-9]*">x<\/mi><mo id="[0-9]*">=<\/mo><mi id="[0-9]*">y<\/mi>/);
verify('["=", "x", ["+", "y", "z"]]', /<mi id="[0-9]*">x<\/mi><mo id="[0-9]*">=<\/mo><mi id="[0-9]*">y<\/mi><mo>\+<\/mo><mi id="[0-9]*">z<\/mi>/);
}
function verifySerializer(input, expect) {
var actual = SexpSerializer(JsonParser(input));
assertEq(expect, actual);
}
function SerializerTest() {
verifySerializer(["a", 1, 2],'["a",1,2]');
verifySerializer(["a", [1, 2]],'["a",[1,2]]');
}
function UnitTest() {
try {
EvalTest();
SerializerTest();
dp("test sucess!");
}catch(e) {
dp("test fail!");
dp(e);
}
}
var KEY_ENTER = 13;
var KEY_LEFT = 37;
var KEY_RIGHT = 39;
var g_selected = undefined;
function onload() {
UnitTest();
var canv = $("mathcanvas");
canv.addEventListener("click", function(evt) {
if(g_selected) {
g_selected.elem.setAttribute("class", "");
}
var id = evt.target.getAttribute("id");
var obj = g_hash[id];
if(!obj) {
g_selected = undefined;
return;
}
if(obj.parent && obj.parent[0] == "-")
obj = obj.parent;
g_selected = {obj: obj, elem: evt.target};
g_selected.elem.setAttribute("class", "selectedTarget");
// alert(g_selected.elem);
});
document.body.addEventListener("keypress", function(evt) {
if(!g_selected)
return;
if(evt.keyCode == KEY_LEFT) {
MoveLeftTerm(g_selected);
Update(g_sexp);
} else if (evt.keyCode == KEY_RIGHT) {
MoveRightTerm(g_selected);
Update(g_sexp);
} else if (evt.keyCode == KEY_ENTER){
Register($("input").value);
}
// alert(evt.keyCode);
});
// alert(document.getElementById("hoge"));
//document.getElementById("hoge").addEventListener("click", function() {
// alert("clicked!"); } );
/*
document.getElementById("x").addEventListener("click", function() {
alert("x!"); } );
document.getElementById("d").addEventListener("click", function() {
alert("d!"); } );
*/
}
</script>
<br>
<textarea id="input" rows="2" cols="100" ></textarea><br>
<input value="Set" type="button" onclick="clicked();">
<input value="Export" type="button" onclick="Export();">
<input value="Import" type="button" onclick="Inport();">
<br>
<div id="mathcanvas"></div>
<hr>
<input value="Clear" type="button" onclick="onClear();"><br>
<textarea id="console" rows="10" cols="100" ></textarea><br>
以下は適当な例。<br>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mrow class="selectedTarget">
<mi>a</mi>
<mi>b</mi>
<mi>c</mi>
</mrow>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mi>
&Laplacetrf;
</mi>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<msubsup>
<mo>&Integral;</mo>
<mi>a</mi>
<mi>&infin;</mi>
</msubsup>
<mi>x</mi>
<mi>dt</mi>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mo>&Integral;</mo>
<mi>x</mi>
<mi>dt</mi>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mover>
<mi>r</mi>
<mo>.</mo>
</mover>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mo>&lambda;</mo>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<msup>
<mrow>
<msub>
<mrow>
<mi> e </mi>
</mrow>
<mrow>
<mi>a</mi>
</mrow>
</msub>
</mrow>
<mrow>
<mo> - </mo>
<mi> &#x03C1;<!--greek small letter rho--> </mi>
<mi> x </mi>
</mrow>
</msup>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<msubsup>
<mrow>
<mi> e </mi>
</mrow>
<mrow>
<mi>a</mi>
</mrow>
<mrow>
<mo> - </mo>
<mi> &#x03C1;<!--greek small letter rho--> </mi>
<mi> x </mi>
</mrow>
</msubsup>
</math>
<br>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mn> 3 </mn>
<mo> + </mo>
<mn> 4 </mn>
</math>
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mrow id="hoge">
<munderover>
<mo>&int;</mo>
<mn>0</mn>
<mn>1</mn>
</munderover>
<mrow>
<msup>
<mi>e</mi>
<mi id="x">x</mi>
</msup>
<mi id="d">d</mi>
<mi>x</mi>
</mrow>
</mrow>
</math>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mfenced>
<mrow>
<mi> a </mi>
<mo> + </mo>
<mi> b </mi>
</mrow>
</mfenced>
</math>
以下はダメ
<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
<cn type="complex-cartesian"> 12.3 <sep/> 5 </cn>
<bind id="outer"><csymbol cd="fns1">lambda</csymbol>
<bvar><ci>x</ci></bvar>
<apply><ci>f</ci>
<bind id="inner"><csymbol cd="fns1">lambda</csymbol>
<bvar><ci>x</ci></bvar>
<share id="copy" href="#orig"/>
</bind>
<apply id="orig"><ci>g</ci><ci>x</ci></apply>
</apply>
</bind>
</mrow>
</math>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment