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