Subversion Repositories wimsdev

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /* ASCIIsvg.js
  2. ==============
  3. JavaScript routines to dynamically generate Scalable Vector Graphics
  4. using a mathematical xy-coordinate system (y increases upwards) and
  5. very intuitive JavaScript commands (no programming experience required).
  6. ASCIIsvg.js is good for learning math and illustrating online math texts.
  7. Works with Internet Explorer+Adobe SVGviewer and SVG enabled Mozilla/Firefox.
  8.  
  9. Ver 1.2.7 Oct 13, 2005 (c) Peter Jipsen http://www.chapman.edu/~jipsen
  10. Latest version at http://www.chapman.edu/~jipsen/svg/ASCIIsvg.js
  11. If you use it on a webpage, please send the URL to jipsen@chapman.edu
  12.  
  13. This program is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 2 of the License, or (at
  16. your option) any later version.
  17.  
  18. This program is distributed in the hope that it will be useful,
  19. but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  21. General Public License (at http://www.gnu.org/copyleft/gpl.html)
  22. for more details.*/
  23.  
  24. var checkIfSVGavailable = true;
  25. var notifyIfNoSVG = true;
  26. var alertIfNoSVG = false;
  27. var xunitlength = 20;  // pixels
  28. var yunitlength = 20;  // pixels
  29. var origin = [0,0];   // in pixels (default is bottom left corner)
  30. var defaultwidth = 300; defaultheight = 200; defaultborder = 0;
  31. var border = defaultborder;
  32. var strokewidth, strokedasharray, stroke, fill;
  33. var fontstyle, fontfamily, fontsize, fontweight, fontstroke, fontfill;
  34. var markerstrokewidth = "1";
  35. var markerstroke = "black";
  36. var markerfill = "yellow";
  37. var marker = "none";
  38. var arrowfill = stroke;
  39. var dotradius = 4;
  40. var ticklength = 4;
  41. var axesstroke = "black";
  42. var gridstroke = "grey";
  43. var pointerpos = null;
  44. var coordinates = null;
  45. var above = "above";
  46. var below = "below";
  47. var left = "left";
  48. var right = "right";
  49. var aboveleft = "aboveleft";
  50. var aboveright = "aboveright";
  51. var belowleft = "belowleft";
  52. var belowright = "belowright";
  53. var cpi = "\u03C0", ctheta = "\u03B8";
  54. var pi = Math.PI, ln = Math.log, e = Math.E;
  55. var arcsin = Math.asin, arccos = Math.acos, arctan = Math.atan;
  56. var sec = function(x) { return 1/Math.cos(x) };
  57. var csc = function(x) { return 1/Math.sin(x) };
  58. var cot = function(x) { return 1/Math.tan(x) };
  59. var xmin, xmax, ymin, ymax, xscl, yscl,
  60.     xgrid, ygrid, xtick, ytick, initialized;
  61. var isIE = document.createElementNS==null;
  62. var picture, svgpicture, doc, width, height, a, b, c, d, i, n, p, t, x, y;
  63. var arcsec = function(x) { return arccos(1/x) };
  64. var arccsc = function(x) { return arcsin(1/x) };
  65. var arccot = function(x) { return arctan(1/x) };
  66. var sinh = function(x) { return (Math.exp(x)-Math.exp(-x))/2 };
  67. var cosh = function(x) { return (Math.exp(x)+Math.exp(-x))/2 };
  68. var tanh =
  69.   function(x) { return (Math.exp(x)-Math.exp(-x))/(Math.exp(x)+Math.exp(-x)) };
  70. var sech = function(x) { return 1/cosh(x) };
  71. var csch = function(x) { return 1/sinh(x) };
  72. var coth = function(x) { return 1/tanh(x) };
  73. var arcsinh = function(x) { return ln(x+Math.sqrt(x*x+1)) };
  74. var arccosh = function(x) { return ln(x+Math.sqrt(x*x-1)) };
  75. var arctanh = function(x) { return ln((1+x)/(1-x))/2 };
  76. var sech = function(x) { return 1/cosh(x) };
  77. var csch = function(x) { return 1/sinh(x) };
  78. var coth = function(x) { return 1/tanh(x) };
  79. var arcsech = function(x) { return arccosh(1/x) };
  80. var arccsch = function(x) { return arcsinh(1/x) };
  81. var arccoth = function(x) { return arctanh(1/x) };
  82. var sign = function(x) { return (x==0?0:(x<0?-1:1)) };
  83.  
  84. function factorial(x,n) {
  85.   if (n==null) n=1;
  86.   for (var i=x-n; i>0; i-=n) x*=i;
  87.   return (x<0?NaN:(x==0?1:x));
  88. }
  89.  
  90. function C(x,k) {
  91.   var res=1;
  92.   for (var i=0; i<k; i++) res*=(x-i)/(k-i);
  93.   return res;
  94. }
  95.  
  96. function chop(x,n) {
  97.   if (n==null) n=0;
  98.   return Math.floor(x*Math.pow(10,n))/Math.pow(10,n);
  99. }
  100.  
  101. function ran(a,b,n) {
  102.   if (n==null) n=0;
  103.   return chop((b+Math.pow(10,-n)-a)*Math.random()+a,n);
  104. }
  105.  
  106. function myCreateElementXHTML(t) {
  107.   if (isIE) return document.createElement(t);
  108.   else return document.createElementNS("http://www.w3.org/1999/xhtml",t);
  109. }
  110.  
  111. function isSVGavailable() {
  112.   var nd = myCreateElementXHTML("center");
  113.   nd.appendChild(document.createTextNode("To view the "));
  114.   var an = myCreateElementXHTML("a");
  115.   an.appendChild(document.createTextNode("ASCIIsvg"));
  116.   an.setAttribute("href","http://www.chapman.edu/~jipsen/asciisvg.html");
  117.   nd.appendChild(an);
  118.   nd.appendChild(document.createTextNode(" images use Internet Explorer 6+"));
  119.   an = myCreateElementXHTML("a");
  120.   an.appendChild(document.createTextNode("Adobe SVGviewer 3.02"));
  121.   an.setAttribute("href","http://www.adobe.com/svg");
  122.   nd.appendChild(an);
  123.   nd.appendChild(document.createTextNode(" or "));
  124.   an = myCreateElementXHTML("a");
  125.   an.appendChild(document.createTextNode("SVG enabled Mozilla/Firefox"));
  126.   an.setAttribute("href",
  127.     "http://www.chapman.edu/~jipsen/svg/svgenabledmozillafirefox.html");
  128.   nd.appendChild(an);
  129.   if (navigator.appName.slice(0,8)=="Netscape")
  130.     if (window['SVGElement']) return null;
  131.     else return nd;
  132.   else if (navigator.appName.slice(0,9)=="Microsoft")
  133.     try {
  134.       var oSVG=eval("new ActiveXObject('Adobe.SVGCtl.3');");
  135.         return null;
  136.     } catch (e) {
  137.         return nd;
  138.     }
  139.   else return nd;
  140. }
  141.  
  142.  
  143. function less(x,y) { return x < y }  // used for scripts in XML files
  144.                                      // since IE does not handle CDATA well
  145. function setText(st,id) {
  146.   var node = document.getElementById(id);
  147.   if (node!=null)
  148.     if (node.childNodes.length!=0) node.childNodes[0].nodeValue = st;
  149.     else node.appendChild(document.createTextNode(st));
  150. }
  151.  
  152. function myCreateElementSVG(t) {
  153.   if (isIE) return doc.createElement(t);
  154.   else return doc.createElementNS("http://www.w3.org/2000/svg",t);
  155. }
  156.  
  157. function getX() {
  158.   return (doc.getElementById("pointerpos").getAttribute("cx")-origin[0])/xunitlength;
  159. }
  160.  
  161. function getY() {
  162.   return (height-origin[1]-doc.getElementById("pointerpos").getAttribute("cy"))/yunitlength;
  163. }
  164.  
  165. function mousemove_listener(evt) {
  166.   if (svgpicture.getAttribute("xbase")!=null)
  167.     pointerpos.cx.baseVal.value = evt.clientX-svgpicture.getAttribute("xbase");
  168.   if (svgpicture.getAttribute("ybase")!=null)
  169.     pointerpos.cy.baseVal.value = evt.clientY-svgpicture.getAttribute("ybase");
  170. }
  171.  
  172. function top_listener(evt) {
  173.   svgpicture.setAttribute("ybase",evt.clientY);
  174. }
  175.  
  176. function bottom_listener(evt) {
  177.   svgpicture.setAttribute("ybase",evt.clientY-height+1);
  178. }
  179.  
  180. function left_listener(evt) {
  181.   svgpicture.setAttribute("xbase",evt.clientX);
  182. }
  183.  
  184. function right_listener(evt) {
  185.   svgpicture.setAttribute("xbase",evt.clientX-width+1);
  186. }
  187.  
  188. function drawPictures() { // main routine; called after webpage has loaded
  189.   var src, id, dsvg, onmov, nd, node, ht, index;
  190.   var pictures = document.getElementsByTagName("textarea");
  191.   for (index = 0; index<pictures.length; index++)
  192.     if (pictures[index].className=="ASCIIsvg"){
  193.       pictures[index].style.display="none";  // hide the textarea
  194.     }
  195.   pictures = document.getElementsByTagName("embed");
  196.   var len = pictures.length;
  197.  if (checkIfSVGavailable) {
  198.   nd = isSVGavailable();
  199.   if (nd != null && notifyIfNoSVG && len>0)
  200.     if (alertIfNoSVG)
  201.       alert("To view the SVG pictures in Internet Explorer\n\
  202. download the free Adobe SVGviewer from www.adobe.com/svg or\n\
  203. use Firefox 1.5 preview (called Deerpark)");
  204.     else {
  205.     var ASbody = document.getElementsByTagName("body")[0];
  206.     ASbody.insertBefore(nd,ASbody.childNodes[0]);
  207.   }
  208.  }
  209.  if (nd == null) {
  210.   for (index = 0; index < len; index++) {
  211.     xmin = null; xmax = null; ymin = null; ymax = null;
  212.     xscl = null; xgrid = null; yscl = null; ygrid = null;
  213.     initialized = false;
  214.     picture = (isIE ? pictures[index] : pictures[0]);
  215.     src = picture.getAttribute("script");
  216.     if (src==null) src = "";
  217.     ht = picture.getAttribute("height");
  218.     if (ht==null) ht ="";
  219.    if (ht!="") defaultborder = 25;
  220.    if (ht=="" || src=="")
  221.     if (document.getElementById("picture"+(index+1)+"input")==null) {
  222.       if (isIE && src.indexOf("nobutton()")==-1)
  223.         picture.parentNode.insertBefore(myCreateElementXHTML("br"),picture);
  224.       node = myCreateElementXHTML("textarea");
  225.       node.setAttribute("rows","10");
  226.       node.setAttribute("cols","60");
  227. //      node.setAttribute("style","display:block");
  228.       if (isIE) src = src.replace(/([^\r])\n/g,"$1\r").slice(1);
  229.       node.appendChild(document.createTextNode(src));
  230.       if (src.indexOf("showcode()")==-1) node.style.display = "none";
  231.       node.setAttribute("id","picture"+(index+1)+"input");
  232.       picture.parentNode.insertBefore(node,picture);
  233.  
  234.       if (src.indexOf("nobutton()")==-1) {
  235.         picture.parentNode.insertBefore(myCreateElementXHTML("br"),picture);
  236.  
  237.         node = myCreateElementXHTML("button");
  238.         if (isIE) node.onclick = function() { showHideCode(this) };
  239.         else node.setAttribute("onclick","showHideCode(this)");
  240.         node.appendChild(document.createTextNode("Show/Hide"));
  241.         picture.parentNode.insertBefore(node,picture);
  242.  
  243.         node = myCreateElementXHTML("button");
  244.         if (isIE) node.onclick = ASfn[index];
  245.         else node.setAttribute("onclick","updatePicture("+index+")");
  246.         node.appendChild(document.createTextNode("Update"));
  247.         if (src.indexOf("showCode()")==-1) node.style.display = "none";
  248.         picture.parentNode.insertBefore(node,picture);
  249.  
  250. /*      node = myCreateElementXHTML("span");
  251. //      node.setAttribute("id","AScoord"+index);
  252.         node.appendChild(document.createTextNode("(x,y)"));
  253.         picture.parentNode.insertBefore(node,picture);
  254. */
  255.         picture.parentNode.insertBefore(myCreateElementXHTML("br"),picture);
  256.       }
  257.       if (isIE) picture.onmousemove = ASupdateCoords[index];
  258.       else picture.setAttribute("onmousemove","updateCoords"+index+"()");
  259.     } else src = document.getElementById("picture"+(index+1)+"input").value;
  260.     src = src.replace(/plot\(\x20*([^\"f\[][^\n\r]+?)\,/g,"plot\(\"$1\",");
  261.     src = src.replace(/plot\(\x20*([^\"f\[][^\n\r]+)\)/g,"plot(\"$1\")");
  262.     src = src.replace(/([0-9])([a-zA-Z])/g,"$1*$2");
  263.     src = src.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1");
  264. //    eval(src.replace(/\s\s/g,";")); //for XML version
  265.     id = picture.getAttribute("id");
  266.     dsvg = picture.getAttribute("src");
  267.     onmov = picture.getAttribute("onmousemove");
  268.     if (id == null || id == "") {
  269.       id = "picture"+(index+1);
  270.       picture.setAttribute("id",id);
  271.     }
  272.     try {
  273.       with (Math) eval(src);
  274.     } catch(err) {alert(err+"\n"+src)}
  275.     if (isIE) src = src.replace(/([^\r])\n/g,"$1\r");
  276.     setText("<embed width=\""+width+"\" height=\""+height+"\" src=\""+
  277.             dsvg+"\" "+(onmov!=null?"onmousemove=\""+onmov+"\"":"")+
  278.             (isIE?"\r":"\n")+"script=\'"+src+"\'>",id+"script");
  279. //    setText(src.replace(/\s\s/g,"\r"),id+"script"); //for XML version
  280.   }
  281.  }
  282. }
  283.  
  284. function switchTo(id) {
  285. //alert(id);
  286.   picture = document.getElementById(id);
  287.   width = picture.getAttribute("width")-0;
  288.   height = picture.getAttribute("height")-0;
  289.   strokewidth = "1" // pixel
  290.   stroke = "black"; // default line color
  291.   fill = "none";    // default fill color
  292.   marker = "none";
  293.   if ((picture.nodeName == "EMBED" || picture.nodeName == "embed") && isIE) {
  294.     svgpicture = picture.getSVGDocument().getElementById("root");
  295.     doc = picture.getSVGDocument();
  296.   } else {
  297.     picture.setAttribute("onmousemove","updateCoords"+(id.slice(id.length-1)-1)+"()");
  298. //alert(picture.getAttribute("onmousemove")+"***");
  299.     svgpicture = picture;
  300.     doc = document;
  301.   }
  302.   xunitlength = svgpicture.getAttribute("xunitlength")-0;
  303.   yunitlength = svgpicture.getAttribute("yunitlength")-0;
  304.   xmin = svgpicture.getAttribute("xmin")-0;
  305.   xmax = svgpicture.getAttribute("xmax")-0;
  306.   ymin = svgpicture.getAttribute("ymin")-0;
  307.   ymax = svgpicture.getAttribute("ymax")-0;
  308.   origin = [svgpicture.getAttribute("ox")-0,svgpicture.getAttribute("oy")-0];
  309. }
  310.  
  311. function updatePicture(obj) {
  312. //alert(typeof obj)
  313.   var src = document.getElementById((typeof obj=="string"?
  314.               obj:"picture"+(obj+1)+"input")).value;
  315.   xmin = null; xmax = null; ymin = null; ymax = null;
  316.   xscl = null; xgrid = null; yscl = null; ygrid = null;
  317.   initialized = false;
  318.   switchTo((typeof obj=="string"?obj.slice(0,8):"picture"+(obj+1)));
  319.   src = src.replace(/plot\(\x20*([^\"f\[][^\n\r]+?)\,/g,"plot\(\"$1\",");
  320.   src = src.replace(/plot\(\x20*([^\"f\[][^\n\r]+)\)/g,"plot(\"$1\")");
  321.   src = src.replace(/([0-9])([a-zA-Z])/g,"$1*$2");
  322.   src = src.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1");
  323. //alert(src);
  324.   try {
  325.     with (Math) eval(src);
  326.   } catch(err) {alert(err+"\n"+src)}
  327. }
  328.  
  329. function showHideCode(obj) {
  330.   var node = obj.nextSibling;
  331.   while (node != null && node.nodeName != "BUTTON" &&
  332.     node.nodeName != "button") node = node.nextSibling;
  333.   if (node.style.display == "none") node.style.display = "";
  334.   else node.style.display = "none";
  335.   while (node != null && node.nodeName != "TEXTAREA" &&
  336.     node.nodeName != "textarea") node = node.previousSibling;
  337.   if (node.style.display == "none") node.style.display = "";
  338.   else node.style.display = "none";
  339. //  updatePicture(node.getAttribute("id"));
  340. }
  341.  
  342. function hideCode() { //do nothing
  343. }
  344.  
  345. function showcode() { //do nothing
  346. }
  347.  
  348. function nobutton() { //do nothing
  349. }
  350.  
  351. function setBorder(x) { border = x }
  352.  
  353. function initPicture(x_min,x_max,y_min,y_max) {
  354.  if (!initialized) {
  355.   strokewidth = "1"; // pixel
  356.   strokedasharray = null;
  357.   stroke = "black"; // default line color
  358.   fill = "none";    // default fill color
  359.   fontstyle = "italic"; // default shape for text labels
  360.   fontfamily = "times"; // default font
  361.   fontsize = "16";      // default size
  362.   fontweight = "normal";
  363.   fontstroke = "none";  // default font outline color
  364.   fontfill = "none";    // default font color
  365.   marker = "none";
  366.   initialized = true;
  367.   if (x_min!=null) xmin = x_min;
  368.   if (x_max!=null) xmax = x_max;
  369.   if (y_min!=null) ymin = y_min;
  370.   if (y_max!=null) ymax = y_max;
  371.   if (xmin==null) xmin = -5;
  372.   if (xmax==null) xmax = 5;
  373.  if (typeof xmin != "number" || typeof xmax != "number" || xmin >= xmax)
  374.    alert("Picture requires at least two numbers: xmin < xmax");
  375.  else if (y_max != null && (typeof y_min != "number" ||
  376.           typeof y_max != "number" || y_min >= y_max))
  377.    alert("initPicture(xmin,xmax,ymin,ymax) requires numbers ymin < ymax");
  378.  else {
  379.   if (width==null) width = picture.getAttribute("width");
  380.   else picture.setAttribute("width",width);
  381.   if (width==null || width=="") width=defaultwidth;
  382.   if (height==null) height = picture.getAttribute("height");
  383.   else picture.setAttribute("height",height);
  384.   if (height==null || height=="") height=defaultheight;
  385.   xunitlength = (width-2*border)/(xmax-xmin);
  386.   yunitlength = xunitlength;
  387. //alert(xmin+" "+xmax+" "+ymin+" "+ymax)
  388.   if (ymin==null) {
  389.     origin = [-xmin*xunitlength+border,height/2];
  390.     ymin = -(height-2*border)/(2*yunitlength);
  391.     ymax = -ymin;
  392.   } else {
  393.     if (ymax!=null) yunitlength = (height-2*border)/(ymax-ymin);
  394.     else ymax = (height-2*border)/yunitlength + ymin;
  395.     origin = [-xmin*xunitlength+border,-ymin*yunitlength+border];
  396.   }
  397. //  if (true ||picture.nodeName == "EMBED" || picture.nodeName == "embed") {
  398.     if (isIE) {
  399.       svgpicture = picture.getSVGDocument().getElementById("root");
  400.       while (svgpicture.childNodes.length()>5)
  401.         svgpicture.removeChild(svgpicture.lastChild);
  402.       svgpicture.setAttribute("width",width);
  403.       svgpicture.setAttribute("height",height);
  404.       doc = picture.getSVGDocument();
  405.     } else {
  406.       var qnode = document.createElementNS("http://www.w3.org/2000/svg","svg");
  407.       qnode.setAttribute("id",picture.getAttribute("id"));
  408.       qnode.setAttribute("style","display:inline");
  409.       qnode.setAttribute("width",picture.getAttribute("width"));
  410.       qnode.setAttribute("height",picture.getAttribute("height"));
  411.       if (picture.parentNode!=null)
  412.         picture.parentNode.replaceChild(qnode,picture);
  413.       else
  414.         svgpicture.parentNode.replaceChild(qnode,svgpicture);
  415.       svgpicture = qnode;
  416.       doc = document;
  417.       pointerpos = doc.getElementById("pointerpos");
  418.       if (pointerpos==null) {
  419.         pointerpos = myCreateElementSVG("circle");
  420.         pointerpos.setAttribute("id","pointerpos");
  421.         pointerpos.setAttribute("cx",0);
  422.         pointerpos.setAttribute("cy",0);
  423.         pointerpos.setAttribute("r",0.5);
  424.         pointerpos.setAttribute("fill","red");
  425.         svgpicture.appendChild(pointerpos);
  426.       }
  427.     }
  428. //  } else {
  429. //    svgpicture = picture;
  430. //    doc = document;
  431. //  }
  432.   svgpicture.setAttribute("xunitlength",xunitlength);
  433.   svgpicture.setAttribute("yunitlength",yunitlength);
  434.   svgpicture.setAttribute("xmin",xmin);
  435.   svgpicture.setAttribute("xmax",xmax);
  436.   svgpicture.setAttribute("ymin",ymin);
  437.   svgpicture.setAttribute("ymax",ymax);
  438.   svgpicture.setAttribute("ox",origin[0]);
  439.   svgpicture.setAttribute("oy",origin[1]);
  440.   var node = myCreateElementSVG("rect");
  441.   node.setAttribute("x","0");
  442.   node.setAttribute("y","0");
  443.   node.setAttribute("width",width);
  444.   node.setAttribute("height",height);
  445.   node.setAttribute("style","stroke-width:1;fill:white");
  446.   svgpicture.appendChild(node);
  447.   if (!isIE && picture.getAttribute("onmousemove")!=null) {
  448.     svgpicture.addEventListener("mousemove", mousemove_listener, true);
  449.     var st = picture.getAttribute("onmousemove");
  450.     svgpicture.addEventListener("mousemove", eval(st.slice(0,st.indexOf("("))), true);
  451.     node = myCreateElementSVG("polyline");
  452.     node.setAttribute("points","0,0 "+width+",0");
  453.     node.setAttribute("style","stroke:white; stroke-width:3");
  454.     node.addEventListener("mousemove", top_listener, true);
  455.     svgpicture.appendChild(node);
  456.     node = myCreateElementSVG("polyline");
  457.     node.setAttribute("points","0,"+height+" "+width+","+height);
  458.     node.setAttribute("style","stroke:white; stroke-width:3");
  459.     node.addEventListener("mousemove", bottom_listener, true);
  460.     svgpicture.appendChild(node);
  461.     node = myCreateElementSVG("polyline");
  462.     node.setAttribute("points","0,0 0,"+height);
  463.     node.setAttribute("style","stroke:white; stroke-width:3");
  464.     node.addEventListener("mousemove", left_listener, true);
  465.     svgpicture.appendChild(node);
  466.     node = myCreateElementSVG("polyline");
  467.     node.setAttribute("points",(width-1)+",0 "+(width-1)+","+height);
  468.     node.setAttribute("style","stroke:white; stroke-width:3");
  469.     node.addEventListener("mousemove", right_listener, true);
  470.     svgpicture.appendChild(node);
  471.   }
  472.   border = defaultborder;
  473.  }
  474.  }
  475. }
  476.  
  477. function line(p,q,id) { // segment connecting points p,q (coordinates in units)
  478.   var node;
  479.   if (id!=null) node = doc.getElementById(id);
  480.   if (node==null) {
  481.     node = myCreateElementSVG("path");
  482.     node.setAttribute("id", id);
  483.     svgpicture.appendChild(node);
  484.   }
  485.   node.setAttribute("d","M"+(p[0]*xunitlength+origin[0])+","+
  486.     (height-p[1]*yunitlength-origin[1])+" "+
  487.     (q[0]*xunitlength+origin[0])+","+(height-q[1]*yunitlength-origin[1]));
  488.   node.setAttribute("stroke-width", strokewidth);
  489.   if (strokedasharray!=null)
  490.     node.setAttribute("stroke-dasharray", strokedasharray);
  491.   node.setAttribute("stroke", stroke);
  492.   node.setAttribute("fill", fill);
  493.   if (marker=="dot" || marker=="arrowdot") {
  494.     ASdot(p,4,markerstroke,markerfill);
  495.     if (marker=="arrowdot") arrowhead(p,q);
  496.     ASdot(q,4,markerstroke,markerfill);
  497.   } else if (marker=="arrow") arrowhead(p,q);
  498. }
  499.  
  500. function path(plist,id,c) {
  501.   if (c==null) c="";
  502.   var node, st, i;
  503.   if (id!=null) node = doc.getElementById(id);
  504.   if (node==null) {
  505.     node = myCreateElementSVG("path");
  506.     node.setAttribute("id", id);
  507.     svgpicture.appendChild(node);
  508.   }
  509.   if (typeof plist == "string") st = plist;
  510.   else {
  511.     st = "M";
  512.     st += (plist[0][0]*xunitlength+origin[0])+","+
  513.           (height-plist[0][1]*yunitlength-origin[1])+" "+c;
  514.     for (i=1; i<plist.length; i++)
  515.       st += (plist[i][0]*xunitlength+origin[0])+","+
  516.             (height-plist[i][1]*yunitlength-origin[1])+" ";
  517.   }
  518.   node.setAttribute("d", st);
  519.   node.setAttribute("stroke-width", strokewidth);
  520.   if (strokedasharray!=null)
  521.     node.setAttribute("stroke-dasharray", strokedasharray);
  522.   node.setAttribute("stroke", stroke);
  523.   node.setAttribute("fill", fill);
  524.   if (marker=="dot" || marker=="arrowdot")
  525.     for (i=0; i<plist.length; i++)
  526.       if (c!="C" && c!="T" || i!=1 && i!=2)
  527.         ASdot(plist[i],4,markerstroke,markerfill);
  528. }
  529.  
  530. function curve(plist,id) {
  531.   path(plist,id,"T");
  532. }
  533.  
  534. function circle(center,radius,id) { // coordinates in units
  535.   var node;
  536.   if (id!=null) node = doc.getElementById(id);
  537.   if (node==null) {
  538.     node = myCreateElementSVG("circle");
  539.     node.setAttribute("id", id);
  540.     svgpicture.appendChild(node);
  541.   }
  542.   node.setAttribute("cx",center[0]*xunitlength+origin[0]);
  543.   node.setAttribute("cy",height-center[1]*yunitlength-origin[1]);
  544.   node.setAttribute("r",radius*xunitlength);
  545.   node.setAttribute("stroke-width", strokewidth);
  546.   node.setAttribute("stroke", stroke);
  547.   node.setAttribute("fill", fill);
  548. }
  549.  
  550. function loop(p,d,id) {
  551. // d is a direction vector e.g. [1,0] means loop starts in that direction
  552.   if (d==null) d=[1,0];
  553.   path([p,[p[0]+d[0],p[1]+d[1]],[p[0]-d[1],p[1]+d[0]],p],id,"C");
  554.   if (marker=="arrow" || marker=="arrowdot")
  555.     arrowhead([p[0]+Math.cos(1.4)*d[0]-Math.sin(1.4)*d[1],
  556.                p[1]+Math.sin(1.4)*d[0]+Math.cos(1.4)*d[1]],p);
  557. }
  558.  
  559. function arc(start,end,radius,id) { // coordinates in units
  560.   var node, v;
  561. //alert([fill, stroke, origin, xunitlength, yunitlength, height])
  562.   if (id!=null) node = doc.getElementById(id);
  563.   if (radius==null) {
  564.     v=[end[0]-start[0],end[1]-start[1]];
  565.     radius = Math.sqrt(v[0]*v[0]+v[1]*v[1]);
  566.   }
  567.   if (node==null) {
  568.     node = myCreateElementSVG("path");
  569.     node.setAttribute("id", id);
  570.     svgpicture.appendChild(node);
  571.   }
  572.   node.setAttribute("d","M"+(start[0]*xunitlength+origin[0])+","+
  573.     (height-start[1]*yunitlength-origin[1])+" A"+radius*xunitlength+","+
  574.      radius*yunitlength+" 0 0,0 "+(end[0]*xunitlength+origin[0])+","+
  575.     (height-end[1]*yunitlength-origin[1]));
  576.   node.setAttribute("stroke-width", strokewidth);
  577.   node.setAttribute("stroke", stroke);
  578.   node.setAttribute("fill", fill);
  579.   if (marker=="arrow" || marker=="arrowdot") {
  580.     u = [(end[1]-start[1])/4,(start[0]-end[0])/4];
  581.     v = [(end[0]-start[0])/2,(end[1]-start[1])/2];
  582. //alert([u,v])
  583.     v = [start[0]+v[0]+u[0],start[1]+v[1]+u[1]];
  584.   } else v=[start[0],start[1]];
  585.   if (marker=="dot" || marker=="arrowdot") {
  586.     ASdot(start,4,markerstroke,markerfill);
  587.     if (marker=="arrowdot") arrowhead(v,end);
  588.     ASdot(end,4,markerstroke,markerfill);
  589.   } else if (marker=="arrow") arrowhead(v,end);
  590. }
  591.  
  592. function ellipse(center,rx,ry,id) { // coordinates in units
  593.   var node;
  594.   if (id!=null) node = doc.getElementById(id);
  595.   if (node==null) {
  596.     node = myCreateElementSVG("ellipse");
  597.     node.setAttribute("id", id);
  598.     svgpicture.appendChild(node);
  599.   }
  600.   node.setAttribute("cx",center[0]*xunitlength+origin[0]);
  601.   node.setAttribute("cy",height-center[1]*yunitlength-origin[1]);
  602.   node.setAttribute("rx",rx*xunitlength);
  603.   node.setAttribute("ry",ry*yunitlength);
  604.   node.setAttribute("stroke-width", strokewidth);
  605.   node.setAttribute("stroke", stroke);
  606.   node.setAttribute("fill", fill);
  607. }
  608.  
  609. function rect(p,q,id,rx,ry) { // opposite corners in units, rounded by radii
  610.   var node;
  611.   if (id!=null) node = doc.getElementById(id);
  612.   if (node==null) {
  613.     node = myCreateElementSVG("rect");
  614.     node.setAttribute("id", id);
  615.     svgpicture.appendChild(node);
  616.   }
  617.   node.setAttribute("x",p[0]*xunitlength+origin[0]);
  618.   node.setAttribute("y",height-q[1]*yunitlength-origin[1]);
  619.   node.setAttribute("width",(q[0]-p[0])*xunitlength);
  620.   node.setAttribute("height",(q[1]-p[1])*yunitlength);
  621.   if (rx!=null) node.setAttribute("rx",rx*xunitlength);
  622.   if (ry!=null) node.setAttribute("ry",ry*yunitlength);
  623.   node.setAttribute("stroke-width", strokewidth);
  624.   node.setAttribute("stroke", stroke);
  625.   node.setAttribute("fill", fill);
  626. }
  627.  
  628. function text(p,st,pos,id,fontsty) {
  629.   var textanchor = "middle";
  630.   var dx = 0; var dy = fontsize/3;
  631.   if (pos!=null) {
  632.     if (pos.slice(0,5)=="above") dy = -fontsize/2;
  633.     if (pos.slice(0,5)=="below") dy = fontsize-0;
  634.     if (pos.slice(0,5)=="right" || pos.slice(5,10)=="right") {
  635.       textanchor = "start";
  636.       dx = fontsize/2;
  637.     }
  638.     if (pos.slice(0,4)=="left" || pos.slice(5,9)=="left") {
  639.       textanchor = "end";
  640.       dx = -fontsize/2;
  641.     }
  642.   }
  643.   var node;
  644.   if (id!=null) node = doc.getElementById(id);
  645.   if (node==null) {
  646.     node = myCreateElementSVG("text");
  647.     node.setAttribute("id", id);
  648.     svgpicture.appendChild(node);
  649.     node.appendChild(doc.createTextNode(st));
  650.   }
  651.   node.lastChild.nodeValue = st;
  652.   node.setAttribute("x",p[0]*xunitlength+origin[0]+dx);
  653.   node.setAttribute("y",height-p[1]*yunitlength-origin[1]+dy);
  654.   node.setAttribute("font-style",(fontsty!=null?fontsty:fontstyle));
  655.   node.setAttribute("font-family",fontfamily);
  656.   node.setAttribute("font-size",fontsize);
  657.   node.setAttribute("font-weight",fontweight);
  658.   node.setAttribute("text-anchor",textanchor);
  659.   if (fontstroke!="none") node.setAttribute("stroke",fontstroke);
  660.   if (fontfill!="none") node.setAttribute("fill",fontfill);
  661.   return p;
  662. }
  663.  
  664. function ASdot(center,radius,s,f) { // coordinates in units, radius in pixel
  665.   if (s==null) s = stroke; if (f==null) f = fill;
  666.   var node = myCreateElementSVG("circle");
  667.   node.setAttribute("cx",center[0]*xunitlength+origin[0]);
  668.   node.setAttribute("cy",height-center[1]*yunitlength-origin[1]);
  669.   node.setAttribute("r",radius);
  670.   node.setAttribute("stroke-width", strokewidth);
  671.   node.setAttribute("stroke", s);
  672.   node.setAttribute("fill", f);
  673.   svgpicture.appendChild(node);
  674. }
  675.  
  676. function dot(center, typ, label, pos, id) {
  677.   var node;
  678.   var cx = center[0]*xunitlength+origin[0];
  679.   var cy = height-center[1]*yunitlength-origin[1];
  680.   if (id!=null) node = doc.getElementById(id);
  681.   if (typ=="+" || typ=="-" || typ=="|") {
  682.     if (node==null) {
  683.       node = myCreateElementSVG("path");
  684.       node.setAttribute("id", id);
  685.       svgpicture.appendChild(node);
  686.     }
  687.     if (typ=="+") {
  688.       node.setAttribute("d",
  689.         " M "+(cx-ticklength)+" "+cy+" L "+(cx+ticklength)+" "+cy+
  690.         " M "+cx+" "+(cy-ticklength)+" L "+cx+" "+(cy+ticklength));
  691.       node.setAttribute("stroke-width", .5);
  692.       node.setAttribute("stroke", axesstroke);
  693.     } else {
  694.       if (typ=="-") node.setAttribute("d",
  695.         " M "+(cx-ticklength)+" "+cy+" L "+(cx+ticklength)+" "+cy);
  696.       else node.setAttribute("d",
  697.         " M "+cx+" "+(cy-ticklength)+" L "+cx+" "+(cy+ticklength));
  698.       node.setAttribute("stroke-width", strokewidth);
  699.       node.setAttribute("stroke", stroke);
  700.     }
  701.   } else {
  702.     if (node==null) {
  703.       node = myCreateElementSVG("circle");
  704.       node.setAttribute("id", id);
  705.       svgpicture.appendChild(node);
  706.     }
  707.     node.setAttribute("cx",cx);
  708.     node.setAttribute("cy",cy);
  709.     node.setAttribute("r",dotradius);
  710.     node.setAttribute("stroke-width", strokewidth);
  711.     node.setAttribute("stroke", stroke);
  712.     node.setAttribute("fill", (typ=="open"?"white":stroke));
  713.   }
  714.   if (label!=null)
  715.     text(center,label,(pos==null?"below":pos),(id==null?id:id+"label"))
  716. }
  717.  
  718. function arrowhead(p,q) { // draw arrowhead at q (in units)
  719.   var up;
  720.   var v = [p[0]*xunitlength+origin[0],height-p[1]*yunitlength-origin[1]];
  721.   var w = [q[0]*xunitlength+origin[0],height-q[1]*yunitlength-origin[1]];
  722.   var u = [w[0]-v[0],w[1]-v[1]];
  723.   var d = Math.sqrt(u[0]*u[0]+u[1]*u[1]);
  724.   if (d > 0.00000001) {
  725.     u = [u[0]/d, u[1]/d];
  726.     up = [-u[1],u[0]];
  727.     var node = myCreateElementSVG("path");
  728.     node.setAttribute("d","M "+(w[0]-15*u[0]-4*up[0])+" "+
  729.       (w[1]-15*u[1]-4*up[1])+" L "+(w[0]-3*u[0])+" "+(w[1]-3*u[1])+" L "+
  730.       (w[0]-15*u[0]+4*up[0])+" "+(w[1]-15*u[1]+4*up[1])+" z");
  731.     node.setAttribute("stroke-width", markerstrokewidth);
  732.     node.setAttribute("stroke", stroke); /*was markerstroke*/
  733.     node.setAttribute("fill", stroke); /*was arrowfill*/
  734.     svgpicture.appendChild(node);    
  735.   }
  736. }
  737.  
  738. function chopZ(st) {
  739.   var k = st.indexOf(".");
  740.   if (k==-1) return st;
  741.   for (var i=st.length-1; i>k && st.charAt(i)=="0"; i--);
  742.   if (i==k) i--;
  743.   return st.slice(0,i+1);
  744. }
  745.  
  746. function grid(dx,dy) { // for backward compatibility
  747.   axes(dx,dy,null,dx,dy)
  748. }
  749.  
  750. function noaxes() {
  751.   if (!initialized) initPicture();
  752. }
  753.  
  754. function axes(dx,dy,labels,gdx,gdy) {
  755. //xscl=x is equivalent to xtick=x; xgrid=x; labels=true;
  756.   var x, y, ldx, ldy, lx, ly, lxp, lyp, pnode, st;
  757.   if (!initialized) initPicture();
  758.   if (typeof dx=="string") { labels = dx; dx = null; }
  759.   if (typeof dy=="string") { gdx = dy; dy = null; }
  760.   if (xscl!=null) {dx = xscl; gdx = xscl; labels = dx}
  761.   if (yscl!=null) {dy = yscl; gdy = yscl}
  762.   if (xtick!=null) {dx = xtick}
  763.   if (ytick!=null) {dy = ytick}
  764. //alert(null)
  765.   dx = (dx==null?xunitlength:dx*xunitlength);
  766.   dy = (dy==null?dx:dy*yunitlength);
  767.   fontsize = Math.min(dx/2,dy/2,16);//alert(fontsize)
  768.   ticklength = fontsize/4;
  769.   if (xgrid!=null) gdx = xgrid;
  770.   if (ygrid!=null) gdy = ygrid;
  771.   if (gdx!=null) {
  772.     gdx = (typeof gdx=="string"?dx:gdx*xunitlength);
  773.     gdy = (gdy==null?dy:gdy*yunitlength);
  774.     pnode = myCreateElementSVG("path");
  775.     st="";
  776.     for (x = origin[0]; x<width; x = x+gdx)
  777.       st += " M"+x+",0"+" "+x+","+height;
  778.     for (x = origin[0]-gdx; x>0; x = x-gdx)
  779.       st += " M"+x+",0"+" "+x+","+height;
  780.     for (y = height-origin[1]; y<height; y = y+gdy)
  781.       st += " M0,"+y+" "+width+","+y;
  782.     for (y = height-origin[1]-gdy; y>0; y = y-gdy)
  783.       st += " M0,"+y+" "+width+","+y;
  784.     pnode.setAttribute("d",st);
  785.     pnode.setAttribute("stroke-width", .5);
  786.     pnode.setAttribute("stroke", gridstroke);
  787.     pnode.setAttribute("fill", fill);
  788.     svgpicture.appendChild(pnode);
  789.   }
  790.   pnode = myCreateElementSVG("path");
  791.   st="M0,"+(height-origin[1])+" "+width+","+
  792.     (height-origin[1])+" M"+origin[0]+",0 "+origin[0]+","+height;
  793.   for (x = origin[0]+dx; x<width; x = x+dx)
  794.     st += " M"+x+","+(height-origin[1]+ticklength)+" "+x+","+
  795.            (height-origin[1]-ticklength);
  796.   for (x = origin[0]-dx; x>0; x = x-dx)
  797.     st += " M"+x+","+(height-origin[1]+ticklength)+" "+x+","+
  798.            (height-origin[1]-ticklength);
  799.   for (y = height-origin[1]+dy; y<height; y = y+dy)
  800.     st += " M"+(origin[0]+ticklength)+","+y+" "+(origin[0]-ticklength)+","+y;
  801.   for (y = height-origin[1]-dy; y>0; y = y-dy)
  802.     st += " M"+(origin[0]+ticklength)+","+y+" "+(origin[0]-ticklength)+","+y;
  803.   if (labels!=null) with (Math) {
  804.     ldx = dx/xunitlength;
  805.     ldy = dy/yunitlength;
  806.     lx = (xmin>0 || xmax<0?xmin:0);
  807.     ly = (ymin>0 || ymax<0?ymin:0);
  808.     lxp = (ly==0?"below":"above");
  809.     lyp = (lx==0?"left":"right");
  810.     var ddx = floor(1.1-log(ldx)/log(10))+1;
  811.     var ddy = floor(1.1-log(ldy)/log(10))+1;
  812.     for (x = ldx; x<=xmax; x = x+ldx)
  813.       text([x,ly],chopZ(x.toFixed(ddx)),lxp);
  814.     for (x = -ldx; xmin<=x; x = x-ldx)
  815.       text([x,ly],chopZ(x.toFixed(ddx)),lxp);
  816.     for (y = ldy; y<=ymax; y = y+ldy)
  817.       text([lx,y],chopZ(y.toFixed(ddy)),lyp);
  818.     for (y = -ldy; ymin<=y; y = y-ldy)
  819.       text([lx,y],chopZ(y.toFixed(ddy)),lyp);
  820.   }
  821.   pnode.setAttribute("d",st);
  822.   pnode.setAttribute("stroke-width", .5);
  823.   pnode.setAttribute("stroke", axesstroke);
  824.   pnode.setAttribute("fill", fill);
  825.   svgpicture.appendChild(pnode);
  826. }
  827.  
  828. function mathjs(st) {
  829.   //translate a math formula to js function notation
  830.   // a^b --> pow(a,b)
  831.   // na --> n*a
  832.   // (...)d --> (...)*d
  833.   // n! --> factorial(n)
  834.   // sin^-1 --> arcsin etc.
  835.   //while ^ in string, find term on left and right
  836.   //slice and concat new formula string
  837.   st = st.replace(/\s/g,"");
  838.   if (st.indexOf("^-1")!=-1) {
  839.     st = st.replace(/sin\^-1/g,"arcsin");
  840.     st = st.replace(/cos\^-1/g,"arccos");
  841.     st = st.replace(/tan\^-1/g,"arctan");
  842.     st = st.replace(/sec\^-1/g,"arcsec");
  843.     st = st.replace(/csc\^-1/g,"arccsc");
  844.     st = st.replace(/cot\^-1/g,"arccot");
  845.     st = st.replace(/sinh\^-1/g,"arcsinh");
  846.     st = st.replace(/cosh\^-1/g,"arccosh");
  847.     st = st.replace(/tanh\^-1/g,"arctanh");
  848.     st = st.replace(/sech\^-1/g,"arcsech");
  849.     st = st.replace(/csch\^-1/g,"arccsch");
  850.     st = st.replace(/coth\^-1/g,"arccoth");
  851.   }
  852.   st = st.replace(/^e$/g,"(E)");
  853.   st = st.replace(/^e([^a-zA-Z])/g,"(E)$1");
  854.   st = st.replace(/([^a-zA-Z])e([^a-zA-Z])/g,"$1(E)$2");
  855.   st = st.replace(/([0-9])([\(a-zA-Z])/g,"$1*$2");
  856.   st = st.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1");
  857.   var i,j,k, ch, nested;
  858.   while ((i=st.indexOf("^"))!=-1) {
  859.     //find left argument
  860.     if (i==0) return "Error: missing argument";
  861.     j = i-1;
  862.     ch = st.charAt(j);
  863.     if (ch>="0" && ch<="9") {// look for (decimal) number
  864.       j--;
  865.       while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  866.       if (ch==".") {
  867.         j--;
  868.         while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  869.       }
  870.     } else if (ch==")") {// look for matching opening bracket and function name
  871.       nested = 1;
  872.       j--;
  873.       while (j>=0 && nested>0) {
  874.         ch = st.charAt(j);
  875.         if (ch=="(") nested--;
  876.         else if (ch==")") nested++;
  877.         j--;
  878.       }
  879.       while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  880.         j--;
  881.     } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable
  882.       j--;
  883.       while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  884.         j--;
  885.     } else {
  886.       return "Error: incorrect syntax in "+st+" at position "+j;
  887.     }
  888.     //find right argument
  889.     if (i==st.length-1) return "Error: missing argument";
  890.     k = i+1;
  891.     ch = st.charAt(k);
  892.     if (ch>="0" && ch<="9" || ch=="-") {// look for signed (decimal) number
  893.       k++;
  894.       while (k<st.length && (ch=st.charAt(k))>="0" && ch<="9") k++;
  895.       if (ch==".") {
  896.         k++;
  897.         while (k<st.length && (ch=st.charAt(k))>="0" && ch<="9") k++;
  898.       }
  899.     } else if (ch=="(") {// look for matching closing bracket and function name
  900.       nested = 1;
  901.       k++;
  902.       while (k<st.length && nested>0) {
  903.         ch = st.charAt(k);
  904.         if (ch=="(") nested++;
  905.         else if (ch==")") nested--;
  906.         k++;
  907.       }
  908.     } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable
  909.       k++;
  910.       while (k<st.length && (ch=st.charAt(k))>="a" && ch<="z" ||
  911.                ch>="A" && ch<="Z") k++;
  912.     } else {
  913.       return "Error: incorrect syntax in "+st+" at position "+k;
  914.     }
  915.     st = st.slice(0,j+1)+"pow("+st.slice(j+1,i)+","+st.slice(i+1,k)+")"+
  916.            st.slice(k);
  917.   }
  918.   while ((i=st.indexOf("!"))!=-1) {
  919.     //find left argument
  920.     if (i==0) return "Error: missing argument";
  921.     j = i-1;
  922.     ch = st.charAt(j);
  923.     if (ch>="0" && ch<="9") {// look for (decimal) number
  924.       j--;
  925.       while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  926.       if (ch==".") {
  927.         j--;
  928.         while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  929.       }
  930.     } else if (ch==")") {// look for matching opening bracket and function name
  931.       nested = 1;
  932.       j--;
  933.       while (j>=0 && nested>0) {
  934.         ch = st.charAt(j);
  935.         if (ch=="(") nested--;
  936.         else if (ch==")") nested++;
  937.         j--;
  938.       }
  939.       while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  940.         j--;
  941.     } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable
  942.       j--;
  943.       while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  944.         j--;
  945.     } else {
  946.       return "Error: incorrect syntax in "+st+" at position "+j;
  947.     }
  948.     st = st.slice(0,j+1)+"factorial("+st.slice(j+1,i)+")"+st.slice(i+1);
  949.   }
  950.   return st;
  951. }
  952.  
  953. function plot(fun,x_min,x_max,points,id) {
  954.   var pth = [];
  955.   var f = function(x) { return x }, g = fun;
  956.   var name = null;
  957.   if (typeof fun=="string")
  958.     eval("g = function(x){ with(Math) return "+mathjs(fun)+" }");
  959.   else if (typeof fun=="object") {
  960.     eval("f = function(t){ with(Math) return "+mathjs(fun[0])+" }");
  961.     eval("g = function(t){ with(Math) return "+mathjs(fun[1])+" }");
  962.   }
  963.   if (typeof x_min=="string") { name = x_min; x_min = xmin }
  964.   else name = id;
  965.   var min = (x_min==null?xmin:x_min);
  966.   var max = (x_max==null?xmax:x_max);
  967.   var inc = max-min-0.000001*(max-min);
  968.   inc = (points==null?inc/200:inc/points);
  969.   var gt;
  970. //alert(typeof g(min))
  971.   for (var t = min; t <= max; t += inc) {
  972.     gt = g(t);
  973.     if (!(isNaN(gt)||Math.abs(gt)=="Infinity")) pth[pth.length] = [f(t), gt];
  974.   }
  975.   path(pth,name)
  976.   return p;
  977. }
  978.  
  979. function slopefield(fun,dx,dy) {
  980.   var g = fun;
  981.   if (typeof fun=="string")
  982.     eval("g = function(x,y){ with(Math) return "+mathjs(fun)+" }");
  983.   var gxy,x,y,u,v,dz;
  984.   if (dx==null) dx=1;
  985.   if (dy==null) dy=1;
  986.   dz = Math.sqrt(dx*dx+dy*dy)/6;
  987.   var x_min = Math.ceil(xmin/dx);
  988.   var y_min = Math.ceil(ymin/dy);
  989.   for (x = x_min; x <= xmax; x += dx)
  990.     for (y = y_min; y <= ymax; y += dy) {
  991.       gxy = g(x,y);
  992.       if (!isNaN(gxy)) {
  993.         if (Math.abs(gxy)=="Infinity") {u = 0; v = dz;}
  994.         else {u = dz/Math.sqrt(1+gxy*gxy); v = gxy*u;}
  995.         line([x-u,y-v],[x+u,y+v]);
  996.       }
  997.     }
  998. }
  999.  
  1000. function updateCoords(ind) {
  1001.   switchTo("picture"+(ind+1));
  1002.   var gx=getX(), gy=getY();
  1003.   if ((xmax-gx)*xunitlength > 6*fontsize || (gy-ymin)*yunitlength > 2*fontsize)
  1004.     text([xmax,ymin],"("+gx.toFixed(2)+", "+gy.toFixed(2)+")",
  1005.          "aboveleft","AScoord"+ind,"");
  1006.   else text([xmax,ymin]," ","aboveleft","AScoord"+ind,"");
  1007. }
  1008.  
  1009. function updateCoords0() {updateCoords(0)}
  1010. function updateCoords1() {updateCoords(1)}
  1011. function updateCoords2() {updateCoords(2)}
  1012. function updateCoords3() {updateCoords(3)}
  1013. function updateCoords4() {updateCoords(4)}
  1014. function updateCoords5() {updateCoords(5)}
  1015. function updateCoords6() {updateCoords(6)}
  1016. function updateCoords7() {updateCoords(7)}
  1017. function updateCoords8() {updateCoords(8)}
  1018. function updateCoords9() {updateCoords(9)}
  1019. ASfn = [function() {updatePicture(0)},
  1020.   function() {updatePicture(1)},
  1021.   function() {updatePicture(2)},
  1022.   function() {updatePicture(3)},
  1023.   function() {updatePicture(4)},
  1024.   function() {updatePicture(5)},
  1025.   function() {updatePicture(6)},
  1026.   function() {updatePicture(7)},
  1027.   function() {updatePicture(8)},
  1028.   function() {updatePicture(9)}];
  1029. ASupdateCoords = [function() {updateCoords(0)},
  1030.   function() {updateCoords(1)},
  1031.   function() {updateCoords(2)},
  1032.   function() {updateCoords(3)},
  1033.   function() {updateCoords(4)},
  1034.   function() {updateCoords(5)},
  1035.   function() {updateCoords(6)},
  1036.   function() {updateCoords(7)},
  1037.   function() {updateCoords(8)},
  1038.   function() {updateCoords(9)}];
  1039.  
  1040. // GO1.1 Generic onload by Brothercake
  1041. // http://www.brothercake.com/
  1042. //onload function
  1043. function generic()
  1044. {
  1045.   drawPictures();
  1046. };
  1047. //setup onload function
  1048. if(typeof window.addEventListener != 'undefined')
  1049. {
  1050.   //.. gecko, safari, konqueror and standard
  1051.   window.addEventListener('load', generic, false);
  1052. }
  1053. else if(typeof document.addEventListener != 'undefined')
  1054. {
  1055.   //.. opera 7
  1056.   document.addEventListener('load', generic, false);
  1057. }
  1058. else if(typeof window.attachEvent != 'undefined')
  1059. {
  1060.   //.. win/ie
  1061.   window.attachEvent('onload', generic);
  1062. }
  1063. //** remove this condition to degrade older browsers
  1064. else
  1065. {
  1066.   //.. mac/ie5 and anything else that gets this far
  1067.   //if there's an existing onload function
  1068.   if(typeof window.onload == 'function')
  1069.   {
  1070.     //store it
  1071.     var existing = onload;
  1072.     //add new onload handler
  1073.     window.onload = function()
  1074.     {
  1075.       //call existing onload function
  1076.       existing();
  1077.       //call generic onload function
  1078.       generic();
  1079.     };
  1080.   }
  1081.   else
  1082.   {
  1083.     //setup onload function
  1084.     window.onload = generic;
  1085.   }
  1086. }
  1087.