ASCIIsvgPI.js 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433
  1. /* ASCIIsvgPI.js
  2. ==============
  3. This version contains modifications needed for ASCIIsvg to work with
  4. tinyMCE editor plugins. Modifications made by David Lippman, (c) 2008
  5. Revised 8/14/09
  6. JavaScript routines to dynamically generate Scalable Vector Graphics
  7. using a mathematical xy-coordinate system (y increases upwards) and
  8. very intuitive JavaScript commands (no programming experience required).
  9. ASCIIsvg.js is good for learning math and illustrating online math texts.
  10. Works with Internet Explorer+Adobe SVGviewer and SVG enabled Mozilla/Firefox.
  11. Ver 1.2.7 Oct 13, 2005 (c) Peter Jipsen http://www.chapman.edu/~jipsen
  12. Latest version at http://www.chapman.edu/~jipsen/svg/ASCIIsvg.js
  13. If you use it on a webpage, please send the URL to jipsen@chapman.edu
  14. This program is free software; you can redistribute it and/or modify
  15. it under the terms of the GNU Lesser General Public License as published by
  16. the Free Software Foundation; either version 2.1 of the License, or (at
  17. your option) any later version.
  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. var AScgiloc = 'http://www.imathas.com/imathas/filter/graph/svgimg.php';
  24. var ASnoSVG = false;
  25. var checkIfSVGavailable = true;
  26. var notifyIfNoSVG = false;
  27. var alertIfNoSVG = false;
  28. var xunitlength = 20; // pixels
  29. var yunitlength = 20; // pixels
  30. var origin = [0,0]; // in pixels (default is bottom left corner)
  31. var defaultwidth = 300; defaultheight = 200; defaultborder = [0,0,0,0];
  32. var border = defaultborder;
  33. var strokewidth, strokedasharray, stroke, fill;
  34. var fontstyle, fontfamily, fontsize, fontweight, fontstroke, fontfill, fontbackground;
  35. var markerstrokewidth = "1";
  36. var markerstroke = "black";
  37. var markerfill = "yellow";
  38. var marker = "none";
  39. var arrowfill = stroke;
  40. var dotradius = 4;
  41. var ticklength = 4;
  42. var axesstroke = "black";
  43. var gridstroke = "grey";
  44. var pointerpos = null;
  45. var coordinates = null;
  46. var above = "above";
  47. var below = "below";
  48. var left = "left";
  49. var right = "right";
  50. var aboveleft = "aboveleft";
  51. var aboveright = "aboveright";
  52. var belowleft = "belowleft";
  53. var belowright = "belowright";
  54. var cpi = "\u03C0", ctheta = "\u03B8";
  55. var pi = Math.PI, ln = Math.log, e = Math.E;
  56. var arcsin = Math.asin, arccos = Math.acos, arctan = Math.atan;
  57. var sec = function(x) { return 1/Math.cos(x) };
  58. var csc = function(x) { return 1/Math.sin(x) };
  59. var cot = function(x) { return 1/Math.tan(x) };
  60. var xmin, xmax, ymin, ymax, xscl, yscl,
  61. xgrid, ygrid, xtick, ytick, initialized;
  62. var isIE = document.createElementNS==null;
  63. var picture, svgpicture, doc, width, height, a, b, c, d, i, n, p, t, x, y;
  64. var arcsec = function(x) { return arccos(1/x) };
  65. var arccsc = function(x) { return arcsin(1/x) };
  66. var arccot = function(x) { return arctan(1/x) };
  67. var sinh = function(x) { return (Math.exp(x)-Math.exp(-x))/2 };
  68. var cosh = function(x) { return (Math.exp(x)+Math.exp(-x))/2 };
  69. var tanh =
  70. function(x) { return (Math.exp(x)-Math.exp(-x))/(Math.exp(x)+Math.exp(-x)) };
  71. var sech = function(x) { return 1/cosh(x) };
  72. var csch = function(x) { return 1/sinh(x) };
  73. var coth = function(x) { return 1/tanh(x) };
  74. var arcsinh = function(x) { return ln(x+Math.sqrt(x*x+1)) };
  75. var arccosh = function(x) { return ln(x+Math.sqrt(x*x-1)) };
  76. var arctanh = function(x) { return ln((1+x)/(1-x))/2 };
  77. var sech = function(x) { return 1/cosh(x) };
  78. var csch = function(x) { return 1/sinh(x) };
  79. var coth = function(x) { return 1/tanh(x) };
  80. var arcsech = function(x) { return arccosh(1/x) };
  81. var arccsch = function(x) { return arcsinh(1/x) };
  82. var arccoth = function(x) { return arctanh(1/x) };
  83. var sign = function(x) { return (x==0?0:(x<0?-1:1)) };
  84. var logten = function(x) { return (Math.LOG10E*Math.log(x)) };
  85. function factorial(x,n) {
  86. if (n==null) n=1;
  87. for (var i=x-n; i>0; i-=n) x*=i;
  88. return (x<0?NaN:(x==0?1:x));
  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. function chop(x,n) {
  96. if (n==null) n=0;
  97. return Math.floor(x*Math.pow(10,n))/Math.pow(10,n);
  98. }
  99. function ran(a,b,n) {
  100. if (n==null) n=0;
  101. return chop((b+Math.pow(10,-n)-a)*Math.random()+a,n);
  102. }
  103. function myCreateElementXHTML(t) {
  104. if (isIE) return document.createElement(t);
  105. else return document.createElementNS("http://www.w3.org/1999/xhtml",t);
  106. }
  107. function isSVGavailable() {
  108. // Disabled by Ivan Tcholakov, 06-JAN-2011.
  109. /*
  110. //Safari 3 can do SVG, but still has issues.
  111. if ((ver = navigator.userAgent.toLowerCase().match(/safari\/(\d+)/))!=null) {
  112. //if (ver[1]>524) {
  113. // return null;
  114. //}
  115. return 1;
  116. }
  117. //Opera can do SVG, but not very pretty, so might want to skip it
  118. if ((ver = navigator.userAgent.toLowerCase().match(/opera\/([\d\.]+)/))!=null) {
  119. if (ver[1]>9.1) {
  120. return null;
  121. }
  122. }
  123. //Mozillas.
  124. if (navigator.product && navigator.product=='Gecko') {
  125. var rv = navigator.userAgent.toLowerCase().match(/rv:\s*([\d\.]+)/);
  126. if (rv!=null) {
  127. rv = rv[1].split('.');
  128. if (rv.length<3) { rv[2] = 0;}
  129. if (rv.length<2) { rv[1] = 0;}
  130. }
  131. if (rv!=null && 10000*rv[0]+100*rv[1]+1*rv[2]>=10800) return null;
  132. else return 1;
  133. }
  134. //IE + AdobeSVGviewer
  135. if (navigator.appName.slice(0,9)=="Microsoft")
  136. try {
  137. var oSVG=eval("new ActiveXObject('Adobe.SVGCtl.3');");
  138. return null;
  139. } catch (e) {
  140. return 1;
  141. }
  142. else return 1;
  143. */
  144. // Added by Ivan Tcholakov, 06-JAN-2011.
  145. if (navigator.appName.slice(0, 8) == "Netscape") { // Gecko, Chrome, Safari
  146. if (window['SVGElement']) {
  147. return null;
  148. } else {
  149. return 1;
  150. }
  151. } else if (navigator.appName.slice(0, 9) == "Microsoft") { // IE
  152. try {
  153. var oSVG = eval("new ActiveXObject('Adobe.SVGCtl.3');");
  154. return null;
  155. } catch (ex) {
  156. return 1;
  157. }
  158. } else if (navigator.appName.slice(0, 5) == "Opera") { // Opera 9.50b1 or higher
  159. return null;
  160. }
  161. return 1;
  162. //
  163. }
  164. function less(x,y) { return x < y } // used for scripts in XML files
  165. // since IE does not handle CDATA well
  166. function setText(st,id) {
  167. var node = document.getElementById(id);
  168. if (node!=null)
  169. if (node.childNodes.length!=0) node.childNodes[0].nodeValue = st;
  170. else node.appendChild(document.createTextNode(st));
  171. }
  172. function myCreateElementSVG(t) {
  173. if (isIE) return doc.createElement(t);
  174. else return doc.createElementNS("http://www.w3.org/2000/svg",t);
  175. }
  176. function getX() {
  177. return (doc.getElementById("pointerpos").getAttribute("cx")-origin[0])/xunitlength;
  178. }
  179. function getY() {
  180. return (height-origin[1]-doc.getElementById("pointerpos").getAttribute("cy"))/yunitlength;
  181. }
  182. function mousemove_listener(evt) {
  183. if (svgpicture.getAttribute("xbase")!=null)
  184. pointerpos.cx.baseVal.value = evt.clientX-svgpicture.getAttribute("xbase");
  185. if (svgpicture.getAttribute("ybase")!=null)
  186. pointerpos.cy.baseVal.value = evt.clientY-svgpicture.getAttribute("ybase");
  187. }
  188. function top_listener(evt) {
  189. svgpicture.setAttribute("ybase",evt.clientY);
  190. }
  191. function bottom_listener(evt) {
  192. svgpicture.setAttribute("ybase",evt.clientY-height+1);
  193. }
  194. function left_listener(evt) {
  195. svgpicture.setAttribute("xbase",evt.clientX);
  196. }
  197. function right_listener(evt) {
  198. svgpicture.setAttribute("xbase",evt.clientX-width+1);
  199. }
  200. function switchTo(id) {
  201. //alert(id);
  202. picture = document.getElementById(id);
  203. width = picture.getAttribute("width")-0;
  204. height = picture.getAttribute("height")-0;
  205. strokewidth = "1" // pixel
  206. stroke = "black"; // default line color
  207. fill = "none"; // default fill color
  208. marker = "none";
  209. if ((picture.nodeName == "EMBED" || picture.nodeName == "embed") && isIE) {
  210. svgpicture = picture.getSVGDocument().getElementById("root");
  211. doc = picture.getSVGDocument();
  212. } else {
  213. picture.setAttribute("onmousemove","updateCoords"+(id.slice(id.length-1)-1)+"()");
  214. //alert(picture.getAttribute("onmousemove")+"***");
  215. svgpicture = picture;
  216. doc = document;
  217. }
  218. xunitlength = svgpicture.getAttribute("xunitlength")-0;
  219. yunitlength = svgpicture.getAttribute("yunitlength")-0;
  220. xmin = svgpicture.getAttribute("xmin")-0;
  221. xmax = svgpicture.getAttribute("xmax")-0;
  222. ymin = svgpicture.getAttribute("ymin")-0;
  223. ymax = svgpicture.getAttribute("ymax")-0;
  224. origin = [svgpicture.getAttribute("ox")-0,svgpicture.getAttribute("oy")-0];
  225. }
  226. function updatePicture(obj) {
  227. //alert(typeof obj)
  228. var src = document.getElementById((typeof obj=="string"?
  229. obj:"picture"+(obj+1)+"input")).value;
  230. xmin = null; xmax = null; ymin = null; ymax = null;
  231. xscl = null; xgrid = null; yscl = null; ygrid = null;
  232. initialized = false;console.log(src);
  233. switchTo((typeof obj=="string"?obj.slice(0,8):"picture"+(obj+1)));
  234. src = src.replace(/plot\(\x20*([^\"f\[][^\n\r]+?)\,/g,"plot\(\"$1\",");
  235. src = src.replace(/plot\(\x20*([^\"f\[][^\n\r]+)\)/g,"plot(\"$1\")");
  236. src = src.replace(/([0-9])([a-zA-Z])/g,"$1*$2");
  237. src = src.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1");
  238. //alert(src);
  239. try {
  240. with (Math) eval(src);
  241. } catch(err) {alert(err+"\n"+src)}
  242. }
  243. function showHideCode(obj) {
  244. var node = obj.nextSibling;
  245. while (node != null && node.nodeName != "BUTTON" &&
  246. node.nodeName != "button") node = node.nextSibling;
  247. if (node.style.display == "none") node.style.display = "";
  248. else node.style.display = "none";
  249. while (node != null && node.nodeName != "TEXTAREA" &&
  250. node.nodeName != "textarea") node = node.previousSibling;
  251. if (node.style.display == "none") node.style.display = "";
  252. else node.style.display = "none";
  253. // updatePicture(node.getAttribute("id"));
  254. }
  255. function hideCode() { //do nothing
  256. }
  257. function showcode() { //do nothing
  258. }
  259. function nobutton() { //do nothing
  260. }
  261. function setBorder(l,b,r,t) {
  262. if (t==null) {
  263. border = new Array(l,l,l,l);
  264. } else {
  265. border = new Array(l,b,r,t);
  266. }
  267. }
  268. function initPicture(x_min,x_max,y_min,y_max) {
  269. if (!initialized) {
  270. strokewidth = "1"; // pixel
  271. strokedasharray = null;
  272. stroke = "black"; // default line color
  273. fill = "none"; // default fill color
  274. fontstyle = "italic"; // default shape for text labels
  275. fontfamily = "times"; // default font
  276. fontsize = "16"; // default size
  277. fontweight = "normal";
  278. fontstroke = "black"; // default font outline color
  279. fontfill = "black"; // default font color
  280. fontbackground = "none";
  281. marker = "none";
  282. initialized = true;
  283. if (x_min!=null) xmin = x_min;
  284. if (x_max!=null) xmax = x_max;
  285. if (y_min!=null) ymin = y_min;
  286. if (y_max!=null) ymax = y_max;
  287. if (xmin==null) xmin = -5;
  288. if (xmax==null) xmax = 5;
  289. if (typeof xmin != "number" || typeof xmax != "number" || xmin >= xmax)
  290. alert("Picture requires at least two numbers: xmin < xmax");
  291. else if (y_max != null && (typeof y_min != "number" ||
  292. typeof y_max != "number" || y_min >= y_max))
  293. alert("initPicture(xmin,xmax,ymin,ymax) requires numbers ymin < ymax");
  294. else {
  295. //if (width==null)
  296. width = picture.getAttribute("width");
  297. //else picture.setAttribute("width",width);
  298. if (width==null || width=="") width=defaultwidth;
  299. //if (height==null)
  300. height = picture.getAttribute("height");
  301. //else picture.setAttribute("height",height);
  302. if (height==null || height=="") height=defaultheight;
  303. xunitlength = (width-border[0]-border[2])/(xmax-xmin);
  304. yunitlength = xunitlength;
  305. //alert(xmin+" "+xmax+" "+ymin+" "+ymax)
  306. if (ymin==null) {
  307. origin = [-xmin*xunitlength+border[0],height/2];
  308. ymin = -(height-border[1]-border[3])/(2*yunitlength);
  309. ymax = -ymin;
  310. } else {
  311. if (ymax!=null) yunitlength = (height-border[1]-border[3])/(ymax-ymin);
  312. else ymax = (height-border[1]-border[3])/yunitlength + ymin;
  313. origin = [-xmin*xunitlength+border[0],-ymin*yunitlength+border[1]];
  314. }
  315. winxmin = Math.max(border[0]-5,0);
  316. winxmax = Math.min(width-border[2]+5,width);
  317. winymin = Math.max(border[3]-5,0);
  318. winymax = Math.min(height-border[1]+5,height);
  319. // if (true ||picture.nodeName == "EMBED" || picture.nodeName == "embed") {
  320. if (isIE) {
  321. svgpicture = picture.getSVGDocument().getElementById("root");
  322. while (svgpicture.childNodes.length()>5)
  323. svgpicture.removeChild(svgpicture.lastChild);
  324. svgpicture.setAttribute("width",width);
  325. svgpicture.setAttribute("height",height);
  326. doc = picture.getSVGDocument();
  327. } else {
  328. var qnode = document.createElementNS("http://www.w3.org/2000/svg","svg");
  329. qnode.setAttribute("id",picture.getAttribute("id"));
  330. qnode.setAttribute("style","display:inline; "+picture.getAttribute("style"));
  331. qnode.setAttribute("width",picture.getAttribute("width"));
  332. qnode.setAttribute("height",picture.getAttribute("height"));
  333. if (picture.parentNode!=null)
  334. picture.parentNode.replaceChild(qnode,picture);
  335. else
  336. svgpicture.parentNode.replaceChild(qnode,svgpicture);
  337. svgpicture = qnode;
  338. doc = document;
  339. pointerpos = doc.getElementById("pointerpos");
  340. if (pointerpos==null) {
  341. pointerpos = myCreateElementSVG("circle");
  342. pointerpos.setAttribute("id","pointerpos");
  343. pointerpos.setAttribute("cx",0);
  344. pointerpos.setAttribute("cy",0);
  345. pointerpos.setAttribute("r",0.5);
  346. pointerpos.setAttribute("fill","red");
  347. svgpicture.appendChild(pointerpos);
  348. }
  349. }
  350. // } else {
  351. // svgpicture = picture;
  352. // doc = document;
  353. // }
  354. svgpicture.setAttribute("xunitlength",xunitlength);
  355. svgpicture.setAttribute("yunitlength",yunitlength);
  356. svgpicture.setAttribute("xmin",xmin);
  357. svgpicture.setAttribute("xmax",xmax);
  358. svgpicture.setAttribute("ymin",ymin);
  359. svgpicture.setAttribute("ymax",ymax);
  360. svgpicture.setAttribute("ox",origin[0]);
  361. svgpicture.setAttribute("oy",origin[1]);
  362. var node = myCreateElementSVG("rect");
  363. node.setAttribute("x","0");
  364. node.setAttribute("y","0");
  365. node.setAttribute("width",width);
  366. node.setAttribute("height",height);
  367. node.setAttribute("style","stroke-width:1;fill:white");
  368. svgpicture.appendChild(node);
  369. if (!isIE && picture.getAttribute("onmousemove")!=null) {
  370. svgpicture.addEventListener("mousemove", mousemove_listener, true);
  371. var st = picture.getAttribute("onmousemove");
  372. svgpicture.addEventListener("mousemove", eval(st.slice(0,st.indexOf("("))), true);
  373. node = myCreateElementSVG("polyline");
  374. node.setAttribute("points","0,0 "+width+",0");
  375. node.setAttribute("style","stroke:white; stroke-width:3");
  376. node.addEventListener("mousemove", top_listener, true);
  377. svgpicture.appendChild(node);
  378. node = myCreateElementSVG("polyline");
  379. node.setAttribute("points","0,"+height+" "+width+","+height);
  380. node.setAttribute("style","stroke:white; stroke-width:3");
  381. node.addEventListener("mousemove", bottom_listener, true);
  382. svgpicture.appendChild(node);
  383. node = myCreateElementSVG("polyline");
  384. node.setAttribute("points","0,0 0,"+height);
  385. node.setAttribute("style","stroke:white; stroke-width:3");
  386. node.addEventListener("mousemove", left_listener, true);
  387. svgpicture.appendChild(node);
  388. node = myCreateElementSVG("polyline");
  389. node.setAttribute("points",(width-1)+",0 "+(width-1)+","+height);
  390. node.setAttribute("style","stroke:white; stroke-width:3");
  391. node.addEventListener("mousemove", right_listener, true);
  392. svgpicture.appendChild(node);
  393. }
  394. border = defaultborder;
  395. }
  396. }
  397. }
  398. function line(p,q,id) { // segment connecting points p,q (coordinates in units)
  399. var node;
  400. if (id!=null) node = doc.getElementById(id);
  401. if (node==null) {
  402. node = myCreateElementSVG("path");
  403. node.setAttribute("id", id);
  404. svgpicture.appendChild(node);
  405. }
  406. node.setAttribute("d","M"+(p[0]*xunitlength+origin[0])+","+
  407. (height-p[1]*yunitlength-origin[1])+" "+
  408. (q[0]*xunitlength+origin[0])+","+(height-q[1]*yunitlength-origin[1]));
  409. node.setAttribute("stroke-width", strokewidth);
  410. if (strokedasharray!=null)
  411. node.setAttribute("stroke-dasharray", strokedasharray);
  412. node.setAttribute("stroke", stroke);
  413. node.setAttribute("fill", fill);
  414. if (marker=="dot" || marker=="arrowdot") {
  415. ASdot(p,4,markerstroke,markerfill);
  416. if (marker=="arrowdot") arrowhead(p,q);
  417. ASdot(q,4,markerstroke,markerfill);
  418. } else if (marker=="arrow") arrowhead(p,q);
  419. }
  420. function path(plist,id,c) {
  421. if (c==null) c="";
  422. var node, st, i;
  423. if (id!=null) node = doc.getElementById(id);
  424. if (node==null) {
  425. node = myCreateElementSVG("path");
  426. node.setAttribute("id", id);
  427. svgpicture.appendChild(node);
  428. }
  429. if (typeof plist == "string") st = plist;
  430. else {
  431. st = "M";
  432. st += (plist[0][0]*xunitlength+origin[0])+","+
  433. (height-plist[0][1]*yunitlength-origin[1])+" "+c;
  434. for (i=1; i<plist.length; i++)
  435. st += (plist[i][0]*xunitlength+origin[0])+","+
  436. (height-plist[i][1]*yunitlength-origin[1])+" ";
  437. }
  438. node.setAttribute("d", st);
  439. node.setAttribute("stroke-width", strokewidth);
  440. if (strokedasharray!=null)
  441. node.setAttribute("stroke-dasharray", strokedasharray);
  442. node.setAttribute("stroke", stroke);
  443. node.setAttribute("fill", fill);
  444. if (marker=="dot" || marker=="arrowdot")
  445. for (i=0; i<plist.length; i++)
  446. if (c!="C" && c!="T" || i!=1 && i!=2)
  447. ASdot(plist[i],4,markerstroke,markerfill);
  448. }
  449. function curve(plist,id) {
  450. path(plist,id,"T");
  451. }
  452. function circle(center,radius,id) { // coordinates in units
  453. var node;
  454. if (id!=null) node = doc.getElementById(id);
  455. if (node==null) {
  456. node = myCreateElementSVG("circle");
  457. node.setAttribute("id", id);
  458. svgpicture.appendChild(node);
  459. }
  460. node.setAttribute("cx",center[0]*xunitlength+origin[0]);
  461. node.setAttribute("cy",height-center[1]*yunitlength-origin[1]);
  462. node.setAttribute("r",radius*xunitlength);
  463. node.setAttribute("stroke-width", strokewidth);
  464. node.setAttribute("stroke", stroke);
  465. node.setAttribute("fill", fill);
  466. }
  467. function loop(p,d,id) {
  468. // d is a direction vector e.g. [1,0] means loop starts in that direction
  469. if (d==null) d=[1,0];
  470. path([p,[p[0]+d[0],p[1]+d[1]],[p[0]-d[1],p[1]+d[0]],p],id,"C");
  471. if (marker=="arrow" || marker=="arrowdot")
  472. arrowhead([p[0]+Math.cos(1.4)*d[0]-Math.sin(1.4)*d[1],
  473. p[1]+Math.sin(1.4)*d[0]+Math.cos(1.4)*d[1]],p);
  474. }
  475. function arc(start,end,radius,id) { // coordinates in units
  476. var node, v;
  477. //alert([fill, stroke, origin, xunitlength, yunitlength, height])
  478. if (id!=null) node = doc.getElementById(id);
  479. if (radius==null) {
  480. v=[end[0]-start[0],end[1]-start[1]];
  481. radius = Math.sqrt(v[0]*v[0]+v[1]*v[1]);
  482. }
  483. if (node==null) {
  484. node = myCreateElementSVG("path");
  485. node.setAttribute("id", id);
  486. svgpicture.appendChild(node);
  487. }
  488. node.setAttribute("d","M"+(start[0]*xunitlength+origin[0])+","+
  489. (height-start[1]*yunitlength-origin[1])+" A"+radius*xunitlength+","+
  490. radius*yunitlength+" 0 0,0 "+(end[0]*xunitlength+origin[0])+","+
  491. (height-end[1]*yunitlength-origin[1]));
  492. node.setAttribute("stroke-width", strokewidth);
  493. node.setAttribute("stroke", stroke);
  494. node.setAttribute("fill", fill);
  495. if (marker=="arrow" || marker=="arrowdot") {
  496. u = [(end[1]-start[1])/4,(start[0]-end[0])/4];
  497. v = [(end[0]-start[0])/2,(end[1]-start[1])/2];
  498. //alert([u,v])
  499. v = [start[0]+v[0]+u[0],start[1]+v[1]+u[1]];
  500. } else v=[start[0],start[1]];
  501. if (marker=="dot" || marker=="arrowdot") {
  502. ASdot(start,4,markerstroke,markerfill);
  503. if (marker=="arrowdot") arrowhead(v,end);
  504. ASdot(end,4,markerstroke,markerfill);
  505. } else if (marker=="arrow") arrowhead(v,end);
  506. }
  507. function ellipse(center,rx,ry,id) { // coordinates in units
  508. var node;
  509. if (id!=null) node = doc.getElementById(id);
  510. if (node==null) {
  511. node = myCreateElementSVG("ellipse");
  512. node.setAttribute("id", id);
  513. svgpicture.appendChild(node);
  514. }
  515. node.setAttribute("cx",center[0]*xunitlength+origin[0]);
  516. node.setAttribute("cy",height-center[1]*yunitlength-origin[1]);
  517. node.setAttribute("rx",rx*xunitlength);
  518. node.setAttribute("ry",ry*yunitlength);
  519. node.setAttribute("stroke-width", strokewidth);
  520. node.setAttribute("stroke", stroke);
  521. node.setAttribute("fill", fill);
  522. }
  523. function rect(p,q,id,rx,ry) { // opposite corners in units, rounded by radii
  524. var node;
  525. if (id!=null) node = doc.getElementById(id);
  526. if (node==null) {
  527. node = myCreateElementSVG("rect");
  528. node.setAttribute("id", id);
  529. svgpicture.appendChild(node);
  530. }
  531. node.setAttribute("x",p[0]*xunitlength+origin[0]);
  532. node.setAttribute("y",height-q[1]*yunitlength-origin[1]);
  533. node.setAttribute("width",(q[0]-p[0])*xunitlength);
  534. node.setAttribute("height",(q[1]-p[1])*yunitlength);
  535. if (rx!=null) node.setAttribute("rx",rx*xunitlength);
  536. if (ry!=null) node.setAttribute("ry",ry*yunitlength);
  537. node.setAttribute("stroke-width", strokewidth);
  538. node.setAttribute("stroke", stroke);
  539. node.setAttribute("fill", fill);
  540. }
  541. function text(p,st,pos,angle) {
  542. p[0] = p[0]*xunitlength+origin[0];
  543. p[1] = p[1]*yunitlength+origin[1];
  544. textabs(p,st,pos,angle);
  545. }
  546. function textabs(p,st,pos,angle,id,fontsty) {
  547. if (angle==null) {
  548. angle = 0;
  549. } else {
  550. angle = (360 - angle)%360;
  551. }
  552. var textanchor = "middle";
  553. var dx=0; var dy=0;
  554. if (angle==270) {
  555. var dy = 0; var dx = fontsize/3;
  556. if (pos!=null) {
  557. if (pos.match(/left/)) {dx = -fontsize/2;}
  558. if (pos.match(/right/)) {dx = fontsize-0;}
  559. if (pos.match(/above/)) {
  560. textanchor = "start";
  561. dy = -fontsize/2;
  562. }
  563. if (pos.match(/below/)) {
  564. textanchor = "end";
  565. dy = fontsize/2;
  566. }
  567. }
  568. }
  569. if (angle==90) {
  570. var dy = 0; var dx = -fontsize/3;
  571. if (pos!=null) {
  572. if (pos.match(/left/)) dx = -fontsize-0;
  573. if (pos.match(/right/)) dx = fontsize/2;
  574. if (pos.match(/above/)) {
  575. textanchor = "end";
  576. dy = -fontsize/2;
  577. }
  578. if (pos.match(/below/)) {
  579. textanchor = "start";
  580. dy = fontsize/2;
  581. }
  582. }
  583. }
  584. if (angle==0) {
  585. var dx = 0; var dy = fontsize/3;
  586. if (pos!=null) {
  587. if (pos.match(/above/)) { dy = -fontsize/3; }
  588. if (pos.match(/below/)) { dy = fontsize-0; }
  589. if (pos.match(/right/)) {
  590. textanchor = "start";
  591. dx = fontsize/3;
  592. }
  593. if (pos.match(/left/)) {
  594. textanchor = "end";
  595. dx = -fontsize/3;
  596. }
  597. }
  598. }
  599. var node;
  600. if (id!=null) node = doc.getElementById(id);
  601. if (node==null) {
  602. node = myCreateElementSVG("text");
  603. node.setAttribute("id", id);
  604. svgpicture.appendChild(node);
  605. node.appendChild(doc.createTextNode(st));
  606. }
  607. node.lastChild.nodeValue = st;
  608. node.setAttribute("x",p[0]+dx);
  609. node.setAttribute("y",height-p[1]+dy);
  610. if (angle != 0) {
  611. node.setAttribute("transform","rotate("+angle+" "+(p[0]+dx)+" "+(height-p[1]+dy)+")");
  612. }
  613. node.setAttribute("font-style",(fontsty!=null?fontsty:fontstyle));
  614. node.setAttribute("font-family",fontfamily);
  615. node.setAttribute("font-size",fontsize);
  616. node.setAttribute("font-weight",fontweight);
  617. node.setAttribute("text-anchor",textanchor);
  618. if (fontstroke!="none") node.setAttribute("stroke",fontstroke);
  619. if (fontfill!="none") node.setAttribute("fill",fontfill);
  620. node.setAttribute("stroke-width","0px");
  621. if (fontbackground!="none") {
  622. var bgnode = myCreateElementSVG("rect");
  623. var bb = node.getBBox();
  624. bgnode.setAttribute("fill",fontbackground);
  625. bgnode.setAttribute("stroke-width","0px");
  626. bgnode.setAttribute("x",bb.x-2);
  627. bgnode.setAttribute("y",bb.y-2);
  628. bgnode.setAttribute("width",bb.width+4);
  629. bgnode.setAttribute("height",bb.height+4);
  630. if (angle != 0) {
  631. bgnode.setAttribute("transform","rotate("+angle+" "+(p[0]+dx)+" "+(height-p[1]+dy)+")");
  632. }
  633. svgpicture.insertBefore(bgnode,node);
  634. }
  635. return p;
  636. }
  637. function ASdot(center,radius,s,f) { // coordinates in units, radius in pixel
  638. if (s==null) s = stroke; if (f==null) f = fill;
  639. var node = myCreateElementSVG("circle");
  640. node.setAttribute("cx",center[0]*xunitlength+origin[0]);
  641. node.setAttribute("cy",height-center[1]*yunitlength-origin[1]);
  642. node.setAttribute("r",radius);
  643. node.setAttribute("stroke-width", strokewidth);
  644. node.setAttribute("stroke", s);
  645. node.setAttribute("fill", f);
  646. svgpicture.appendChild(node);
  647. }
  648. function dot(center, typ, label, pos, id) {
  649. var node;
  650. var cx = center[0]*xunitlength+origin[0];
  651. var cy = height-center[1]*yunitlength-origin[1];
  652. if (id!=null) node = doc.getElementById(id);
  653. if (typ=="+" || typ=="-" || typ=="|") {
  654. if (node==null) {
  655. node = myCreateElementSVG("path");
  656. node.setAttribute("id", id);
  657. svgpicture.appendChild(node);
  658. }
  659. if (typ=="+") {
  660. node.setAttribute("d",
  661. " M "+(cx-ticklength)+" "+cy+" L "+(cx+ticklength)+" "+cy+
  662. " M "+cx+" "+(cy-ticklength)+" L "+cx+" "+(cy+ticklength));
  663. node.setAttribute("stroke-width", .5);
  664. node.setAttribute("stroke", axesstroke);
  665. } else {
  666. if (typ=="-") node.setAttribute("d",
  667. " M "+(cx-ticklength)+" "+cy+" L "+(cx+ticklength)+" "+cy);
  668. else node.setAttribute("d",
  669. " M "+cx+" "+(cy-ticklength)+" L "+cx+" "+(cy+ticklength));
  670. node.setAttribute("stroke-width", strokewidth);
  671. node.setAttribute("stroke", stroke);
  672. }
  673. } else {
  674. if (node==null) {
  675. node = myCreateElementSVG("circle");
  676. node.setAttribute("id", id);
  677. svgpicture.appendChild(node);
  678. }
  679. node.setAttribute("cx",cx);
  680. node.setAttribute("cy",cy);
  681. node.setAttribute("r",dotradius);
  682. node.setAttribute("stroke-width", strokewidth);
  683. node.setAttribute("stroke", stroke);
  684. node.setAttribute("fill", (typ=="open"?"white":stroke));
  685. }
  686. if (label!=null)
  687. text(center,label,(pos==null?"below":pos),(id==null?id:id+"label"))
  688. }
  689. function arrowhead(p,q) { // draw arrowhead at q (in units)
  690. var up;
  691. var v = [p[0]*xunitlength+origin[0],height-p[1]*yunitlength-origin[1]];
  692. var w = [q[0]*xunitlength+origin[0],height-q[1]*yunitlength-origin[1]];
  693. var u = [w[0]-v[0],w[1]-v[1]];
  694. var d = Math.sqrt(u[0]*u[0]+u[1]*u[1]);
  695. if (d > 0.00000001) {
  696. u = [u[0]/d, u[1]/d];
  697. up = [-u[1],u[0]];
  698. var node = myCreateElementSVG("path");
  699. node.setAttribute("d","M "+(w[0]-15*u[0]-4*up[0])+" "+
  700. (w[1]-15*u[1]-4*up[1])+" L "+(w[0]-3*u[0])+" "+(w[1]-3*u[1])+" L "+
  701. (w[0]-15*u[0]+4*up[0])+" "+(w[1]-15*u[1]+4*up[1])+" z");
  702. node.setAttribute("stroke-width", markerstrokewidth);
  703. node.setAttribute("stroke", stroke); /*was markerstroke*/
  704. node.setAttribute("fill", stroke); /*was arrowfill*/
  705. svgpicture.appendChild(node);
  706. }
  707. }
  708. function chopZ(st) {
  709. var k = st.indexOf(".");
  710. if (k==-1) return st;
  711. for (var i=st.length-1; i>k && st.charAt(i)=="0"; i--);
  712. if (i==k) i--;
  713. return st.slice(0,i+1);
  714. }
  715. function grid(dx,dy) { // for backward compatibility
  716. axes(dx,dy,null,dx,dy)
  717. }
  718. function noaxes() {
  719. if (!initialized) initPicture();
  720. }
  721. function axes(dx,dy,labels,gdx,gdy,dox,doy) {
  722. //xscl=x is equivalent to xtick=x; xgrid=x; labels=true;
  723. var x, y, ldx, ldy, lx, ly, lxp, lyp, pnode, st;
  724. if (!initialized) initPicture();
  725. if (typeof dx=="string") { labels = dx; dx = null; }
  726. if (typeof dy=="string") { gdx = dy; dy = null; }
  727. if (xscl!=null) {dx = xscl; gdx = xscl; labels = dx}
  728. if (yscl!=null) {dy = yscl; gdy = yscl}
  729. if (xtick!=null) {dx = xtick}
  730. if (ytick!=null) {dy = ytick}
  731. if (dox==null) {dox = true;}
  732. if (doy==null) {doy = true;}
  733. if (dox=="off" || dox==0) { dox = false;} else {dox = true;}
  734. if (doy=="off" || doy==0) { doy = false;} else {doy = true;}
  735. //alert(null)
  736. dx = (dx==null?xunitlength:dx*xunitlength);
  737. dy = (dy==null?dx:dy*yunitlength);
  738. fontsize = Math.floor(Math.min(dx/1.5,dy/1.5,16));//alert(fontsize)
  739. ticklength = fontsize/4;
  740. if (xgrid!=null) gdx = xgrid;
  741. if (ygrid!=null) gdy = ygrid;
  742. if (gdx!=null) {
  743. gdx = (typeof gdx=="string"?dx:gdx*xunitlength);
  744. gdy = (gdy==null?dy:gdy*yunitlength);
  745. pnode = myCreateElementSVG("path");
  746. st="";
  747. if (dox && gdx>0) {
  748. for (x = origin[0]; x<=winxmax; x = x+gdx)
  749. if (x>=winxmin) st += " M"+x+","+winymin+" "+x+","+winymax;
  750. for (x = origin[0]-gdx; x>=winxmin; x = x-gdx)
  751. if (x<=winxmax) st += " M"+x+","+winymin+" "+x+","+winymax;
  752. }
  753. if (doy && gdy>0) {
  754. for (y = height-origin[1]; y<=winymax; y = y+gdy)
  755. if (y>=winymin) st += " M"+winxmin+","+y+" "+winxmax+","+y;
  756. for (y = height-origin[1]-gdy; y>=winymin; y = y-gdy)
  757. if (y<=winymax) st += " M"+winxmin+","+y+" "+winxmax+","+y;
  758. }
  759. pnode.setAttribute("d",st);
  760. pnode.setAttribute("stroke-width", .5);
  761. pnode.setAttribute("stroke", gridstroke);
  762. pnode.setAttribute("fill", fill);
  763. svgpicture.appendChild(pnode);
  764. }
  765. pnode = myCreateElementSVG("path");
  766. if (dox) {
  767. st="M"+winxmin+","+(height-origin[1])+" "+winxmax+","+
  768. (height-origin[1]);
  769. }
  770. if (doy) {
  771. st += " M"+origin[0]+","+winymin+" "+origin[0]+","+winymax;
  772. }
  773. if (dox) {
  774. for (x = origin[0]; x<winxmax; x = x+dx)
  775. if (x>=winymin) st += " M"+x+","+(height-origin[1]+ticklength)+" "+x+","+
  776. (height-origin[1]-ticklength);
  777. for (x = origin[0]-dx; x>winxmin; x = x-dx)
  778. if (x<=winxmax) st += " M"+x+","+(height-origin[1]+ticklength)+" "+x+","+
  779. (height-origin[1]-ticklength);
  780. }
  781. if (doy) {
  782. for (y = height-origin[1]; y<winymax; y = y+dy)
  783. if (y>=winymin) st += " M"+(origin[0]+ticklength)+","+y+" "+(origin[0]-ticklength)+","+y;
  784. for (y = height-origin[1]-dy; y>winymin; y = y-dy)
  785. if (y<=winymax) st += " M"+(origin[0]+ticklength)+","+y+" "+(origin[0]-ticklength)+","+y;
  786. }
  787. if (labels!=null) with (Math) {
  788. ldx = dx/xunitlength;
  789. ldy = dy/yunitlength;
  790. lx = (xmin>0 || xmax<0?xmin:0);
  791. ly = (ymin>0 || ymax<0?ymin:0);
  792. lxp = (ly==0?"below":"above");
  793. lyp = (lx==0?"left":"right");
  794. var ddx = floor(1.1-log(ldx)/log(10))+1;
  795. var ddy = floor(1.1-log(ldy)/log(10))+1;
  796. if (ddy<0) { ddy = 0;}
  797. if (ddx<0) { ddx = 0;}
  798. if (dox) {
  799. for (x = (doy?ldx:0); x<=xmax; x = x+ldx)
  800. if (x>=xmin) text([x,ly],chopZ(x.toFixed(ddx)),lxp);
  801. for (x = -ldx; xmin<=x; x = x-ldx)
  802. if (x<=xmax) text([x,ly],chopZ(x.toFixed(ddx)),lxp);
  803. }
  804. if (doy) {
  805. for (y = (dox?ldy:0); y<=ymax; y = y+ldy)
  806. if (y>=ymin) text([lx,y],chopZ(y.toFixed(ddy)),lyp);
  807. for (y = -ldy; ymin<=y; y = y-ldy)
  808. if (y<=ymax) text([lx,y],chopZ(y.toFixed(ddy)),lyp);
  809. }
  810. }
  811. pnode.setAttribute("d",st);
  812. pnode.setAttribute("stroke-width", .5);
  813. pnode.setAttribute("stroke", axesstroke);
  814. pnode.setAttribute("fill", fill);
  815. svgpicture.appendChild(pnode);
  816. }
  817. function safepow(base,power) {
  818. if (base<0 && Math.floor(power)!=power) {
  819. for (var j=3; j<50; j+=2) {
  820. if (Math.abs(Math.round(j*power)-(j*power))<.000001) {
  821. if (Math.round(j*power)%2==0) {
  822. return Math.pow(Math.abs(base),power);
  823. } else {
  824. return -1*Math.pow(Math.abs(base),power);
  825. }
  826. }
  827. }
  828. return sqrt(-1);
  829. } else {
  830. return Math.pow(base,power);
  831. }
  832. }
  833. function nthroot(n,base) {
  834. return safepow(base,1/n);
  835. }
  836. function nthlogten(n,v) {
  837. return ((Math.log(v))/(Math.log(n)));
  838. }
  839. function matchtolower(match) {
  840. return match.toLowerCase();
  841. }
  842. function mathjs(st,varlist) {
  843. //translate a math formula to js function notation
  844. // a^b --> pow(a,b)
  845. // na --> n*a
  846. // (...)d --> (...)*d
  847. // n! --> factorial(n)
  848. // sin^-1 --> arcsin etc.
  849. //while ^ in string, find term on left and right
  850. //slice and concat new formula string
  851. //parenthesizes the function variables
  852. st = st.replace("[","(");
  853. st = st.replace("]",")");
  854. st = st.replace(/arc(sin|cos|tan)/g,"a#r#c $1");
  855. if (varlist != null) {
  856. var reg = new RegExp("(sqrt|ln|log|sin|cos|tan|sec|csc|cot|abs)[\(]","g");
  857. st = st.replace(reg,"$1#(");
  858. var reg = new RegExp("("+varlist+")("+varlist+")$","g");
  859. st = st.replace(reg,"($1)($2)");
  860. var reg = new RegExp("("+varlist+")(a#|sqrt|ln|log|sin|cos|tan|sec|csc|cot|abs)","g");
  861. st = st.replace(reg,"($1)$2");
  862. var reg = new RegExp("("+varlist+")("+varlist+")([^a-df-zA-Z\(#])","g"); // 6/1/09 readded \( for f(350/x)
  863. st = st.replace(reg,"($1)($2)$3"); //get xy3
  864. //var reg = new RegExp("("+varlist+")("+varlist+")(\w*[^\(#])","g");
  865. //st = st.replace(reg,"($1)($2)$3"); //get xysin
  866. var reg = new RegExp("([^a-df-zA-Z#])("+varlist+")([^a-df-zA-Z#])","g");
  867. st = st.replace(reg,"$1($2)$3");
  868. var reg = new RegExp("^("+varlist+")([^a-df-zA-Z])","g");
  869. st = st.replace(reg,"($1)$2");
  870. var reg = new RegExp("([^a-df-zA-Z])("+varlist+")$","g");
  871. st = st.replace(reg,"$1($2)");
  872. }
  873. st = st.replace(/#/g,"");
  874. st = st.replace(/a#r#c\s+(sin|cos|tan)/g,"arc$1");
  875. st = st.replace(/\s/g,"");
  876. st = st.replace(/(Sin|Cos|Tan|Sec|Csc|Cot|Arc|Abs|Log|Ln)/g, matchtolower);
  877. st = st.replace(/log_(\d+)\(/,"nthlog($1,");
  878. st = st.replace(/log/g,"logten");
  879. if (st.indexOf("^-1")!=-1) {
  880. st = st.replace(/sin\^-1/g,"arcsin");
  881. st = st.replace(/cos\^-1/g,"arccos");
  882. st = st.replace(/tan\^-1/g,"arctan");
  883. st = st.replace(/sec\^-1/g,"arcsec");
  884. st = st.replace(/csc\^-1/g,"arccsc");
  885. st = st.replace(/cot\^-1/g,"arccot");
  886. st = st.replace(/sinh\^-1/g,"arcsinh");
  887. st = st.replace(/cosh\^-1/g,"arccosh");
  888. st = st.replace(/tanh\^-1/g,"arctanh");
  889. st = st.replace(/sech\^-1/g,"arcsech");
  890. st = st.replace(/csch\^-1/g,"arccsch");
  891. st = st.replace(/coth\^-1/g,"arccoth");
  892. }
  893. st = st.replace(/root\((\d+)\)\(/,"nthroot($1,");
  894. //st = st.replace(/E/g,"(EE)");
  895. st = st.replace(/([0-9])E([\-0-9])/g,"$1(EE)$2");
  896. st = st.replace(/^e$/g,"(E)");
  897. st = st.replace(/pi/g,"(pi)");
  898. st = st.replace(/^e([^a-zA-Z])/g,"(E)$1");
  899. st = st.replace(/([^a-zA-Z])e$/g,"$1(E)");
  900. st = st.replace(/([^a-zA-Z])e([^a-zA-Z])/g,"$1(E)$2");
  901. st = st.replace(/([0-9])([\(a-zA-Z])/g,"$1*$2");
  902. st = st.replace(/(!)([0-9\(])/g,"$1*$2");
  903. //want to keep scientific notation
  904. st= st.replace(/([0-9])\*\(EE\)([\-0-9])/,"$1e$2");
  905. st = st.replace(/\)([\(0-9a-zA-Z])/g,"\)*$1");
  906. var i,j,k, ch, nested;
  907. while ((i=st.indexOf("^"))!=-1) {
  908. //find left argument
  909. if (i==0) return "Error: missing argument";
  910. j = i-1;
  911. ch = st.charAt(j);
  912. if (ch>="0" && ch<="9") {// look for (decimal) number
  913. j--;
  914. while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  915. if (ch==".") {
  916. j--;
  917. while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  918. }
  919. } else if (ch==")") {// look for matching opening bracket and function name
  920. nested = 1;
  921. j--;
  922. while (j>=0 && nested>0) {
  923. ch = st.charAt(j);
  924. if (ch=="(") nested--;
  925. else if (ch==")") nested++;
  926. j--;
  927. }
  928. while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  929. j--;
  930. } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable
  931. j--;
  932. while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  933. j--;
  934. } else {
  935. return "Error: incorrect syntax in "+st+" at position "+j;
  936. }
  937. //find right argument
  938. if (i==st.length-1) return "Error: missing argument";
  939. k = i+1;
  940. ch = st.charAt(k);
  941. nch = st.charAt(k+1);
  942. if (ch>="0" && ch<="9" || (ch=="-" && nch!="(") || ch==".") {// look for signed (decimal) number
  943. k++;
  944. while (k<st.length && (ch=st.charAt(k))>="0" && ch<="9") k++;
  945. if (ch==".") {
  946. k++;
  947. while (k<st.length && (ch=st.charAt(k))>="0" && ch<="9") k++;
  948. }
  949. } else if (ch=="(" || (ch=="-" && nch=="(")) {// look for matching closing bracket and function name
  950. if (ch=="-") { k++;}
  951. nested = 1;
  952. k++;
  953. while (k<st.length && nested>0) {
  954. ch = st.charAt(k);
  955. if (ch=="(") nested++;
  956. else if (ch==")") nested--;
  957. k++;
  958. }
  959. } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable
  960. k++;
  961. while (k<st.length && (ch=st.charAt(k))>="a" && ch<="z" ||
  962. ch>="A" && ch<="Z") k++;
  963. if (ch=='(' && st.slice(i+1,k).match(/^(sin|cos|tan|sec|csc|cot|logten|log|ln|exp|arcsin|arccos|arctan|arcsec|arccsc|arccot|sinh|cosh|tanh|sech|csch|coth|arcsinh|arccosh|arctanh|arcsech|arccsch|arccoth|sqrt|abs|nthroot)$/)) {
  964. nested = 1;
  965. k++;
  966. while (k<st.length && nested>0) {
  967. ch = st.charAt(k);
  968. if (ch=="(") nested++;
  969. else if (ch==")") nested--;
  970. k++;
  971. }
  972. }
  973. } else {
  974. return "Error: incorrect syntax in "+st+" at position "+k;
  975. }
  976. st = st.slice(0,j+1)+"safepow("+st.slice(j+1,i)+","+st.slice(i+1,k)+")"+
  977. st.slice(k);
  978. }
  979. while ((i=st.indexOf("!"))!=-1) {
  980. //find left argument
  981. if (i==0) return "Error: missing argument";
  982. j = i-1;
  983. ch = st.charAt(j);
  984. if (ch>="0" && ch<="9") {// look for (decimal) number
  985. j--;
  986. while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  987. if (ch==".") {
  988. j--;
  989. while (j>=0 && (ch=st.charAt(j))>="0" && ch<="9") j--;
  990. }
  991. } else if (ch==")") {// look for matching opening bracket and function name
  992. nested = 1;
  993. j--;
  994. while (j>=0 && nested>0) {
  995. ch = st.charAt(j);
  996. if (ch=="(") nested--;
  997. else if (ch==")") nested++;
  998. j--;
  999. }
  1000. while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  1001. j--;
  1002. } else if (ch>="a" && ch<="z" || ch>="A" && ch<="Z") {// look for variable
  1003. j--;
  1004. while (j>=0 && (ch=st.charAt(j))>="a" && ch<="z" || ch>="A" && ch<="Z")
  1005. j--;
  1006. } else {
  1007. return "Error: incorrect syntax in "+st+" at position "+j;
  1008. }
  1009. st = st.slice(0,j+1)+"factorial("+st.slice(j+1,i)+")"+st.slice(i+1);
  1010. }
  1011. return st;
  1012. }
  1013. function slopefield(fun,dx,dy) {
  1014. var g = fun;
  1015. if (typeof fun=="string")
  1016. eval("g = function(x,y){ with(Math) return "+mathjs(fun)+" }");
  1017. var gxy,x,y,u,v,dz;
  1018. if (dx==null) dx=1;
  1019. if (dy==null) dy=1;
  1020. dz = Math.sqrt(dx*dx+dy*dy)/6;
  1021. var x_min = Math.ceil(xmin/dx);
  1022. var y_min = Math.ceil(ymin/dy);
  1023. for (x = x_min; x <= xmax; x += dx)
  1024. for (y = y_min; y <= ymax; y += dy) {
  1025. gxy = g(x,y);
  1026. if (!isNaN(gxy)) {
  1027. if (Math.abs(gxy)=="Infinity") {u = 0; v = dz;}
  1028. else {u = dz/Math.sqrt(1+gxy*gxy); v = gxy*u;}
  1029. line([x-u,y-v],[x+u,y+v]);
  1030. }
  1031. }
  1032. }
  1033. //ASCIIsvgAddon.js dumped here
  1034. function drawPictures() {
  1035. drawPics()
  1036. }
  1037. //ShortScript format:
  1038. //xmin,xmax,ymin,ymax,xscl,yscl,labels,xgscl,ygscl,width,height plotcommands(see blow)
  1039. //plotcommands: type,eq1,eq2,startmaker,endmarker,xmin,xmax,color,strokewidth,strokedash
  1040. function parseShortScript(sscript,gw,gh) {
  1041. // Added by Ivan Tcholakov, 07-JAN-2011.
  1042. if (typeof picture == 'undefined') {
  1043. return;
  1044. }
  1045. //
  1046. if (sscript == null) {
  1047. initialized = false;
  1048. sscript = picture.sscr;
  1049. }
  1050. var sa= sscript.split(",");
  1051. if (gw && gh) {
  1052. sa[9] = gw;
  1053. sa[10] = gh;
  1054. sscript = sa.join(",");
  1055. picture.setAttribute("sscr", sscript);
  1056. }
  1057. picture.setAttribute("width", sa[9]);
  1058. picture.setAttribute("height", sa[10]);
  1059. picture.style.width = sa[9] + "px";
  1060. picture.style.height = sa[10] + "px";
  1061. if (sa.length > 10) {
  1062. commands = 'setBorder(5);';
  1063. commands += 'width=' +sa[9] + '; height=' +sa[10] + ';';
  1064. commands += 'initPicture(' + sa[0] +','+ sa[1] +','+ sa[2] +','+ sa[3] + ');';
  1065. commands += 'axes(' + sa[4] +','+ sa[5] +','+ sa[6] +','+ sa[7] +','+ sa[8]+ ');';
  1066. var inx = 11;
  1067. var eqnlist = 'Graphs: ';
  1068. while (sa.length > inx+9) {
  1069. commands += 'stroke="' + sa[inx+7] + '";';
  1070. commands += 'strokewidth="' + sa[inx+8] + '";'
  1071. //commands += 'strokedasharray="' + sa[inx+9] + '";'
  1072. if (sa[inx+9] != "") {
  1073. commands += 'strokedasharray="' + sa[inx+9].replace(/\s+/g,',') + '";';
  1074. }
  1075. if (sa[inx]=="slope") {
  1076. eqnlist += "dy/dx="+sa[inx+1] + "; ";
  1077. commands += 'slopefield("' + sa[inx+1] + '",' + sa[inx+2] + ',' + sa[inx+2] + ');';
  1078. } else {
  1079. if (sa[inx]=="func") {
  1080. eqnlist += "y="+sa[inx+1] + "; ";
  1081. eqn = '"' + sa[inx+1] + '"';
  1082. } else if (sa[inx] == "polar") {
  1083. eqnlist += "r="+sa[inx+1] + "; ";
  1084. eqn = '["cos(t)*(' + sa[inx+1] + ')","sin(t)*(' + sa[inx+1] + ')"]';
  1085. } else if (sa[inx] == "param") {
  1086. eqnlist += "[x,y]=["+sa[inx+1] + "," + sa[inx+2] + "]; ";
  1087. eqn = '["' + sa[inx+1] + '","'+ sa[inx+2] + '"]';
  1088. }
  1089. if (typeof eval(sa[inx+5]) == "number") {
  1090. // if ((sa[inx+5]!='null')&&(sa[inx+5].length>0)) {
  1091. //commands += 'myplot(' + eqn +',"' + sa[inx+3] + '","' + sa[inx+4]+'",' + sa[inx+5] + ',' + sa[inx+6] +');';
  1092. commands += 'plot(' + eqn +',' + sa[inx+5] + ',' + sa[inx+6] +',null,null,' + sa[inx+3] + ',' + sa[inx+4] +');';
  1093. } else {
  1094. commands += 'plot(' + eqn +',null,null,null,null,' + sa[inx+3] + ',' + sa[inx+4]+');';
  1095. }
  1096. }
  1097. inx += 10;
  1098. }
  1099. try {
  1100. eval(commands);
  1101. } catch (e) {
  1102. setTimeout(function() {parseShortScript(sscript,gw,gh)},100);
  1103. //alert("Graph not ready");
  1104. }
  1105. picture.setAttribute("alt",eqnlist);
  1106. //picture.setAttribute("width", sa[9]);
  1107. //picture.setAttribute("height", sa[9]);
  1108. return commands;
  1109. }
  1110. }
  1111. function drawPics() {
  1112. var index, nd;
  1113. pictures = document.getElementsByTagName("embed");
  1114. // might be needed if setTimeout on parseShortScript isn't working
  1115. if (!ASnoSVG) {
  1116. try {
  1117. for (var i = 0; i < pictures.length; i++) {
  1118. if (pictures[i].getAttribute("sscr")!='' || pictures[i].getAttribute("script")!='') {
  1119. if (pictures[i].getSVGDocument().getElementById("root") == null) {
  1120. setTimeout(drawPics,100);
  1121. return;
  1122. }
  1123. }
  1124. }
  1125. } catch (e) {
  1126. setTimeout(drawPics,100);
  1127. return;
  1128. }
  1129. }
  1130. var len = pictures.length;
  1131. for (index = 0; index < len; index++) {
  1132. picture = ((!ASnoSVG && isIE) ? pictures[index] : pictures[0]);
  1133. // for (index = len-1; index >=0; index--) {
  1134. // picture = pictures[index];
  1135. if (!ASnoSVG) {
  1136. initialized = false;
  1137. var sscr = picture.getAttribute("sscr");
  1138. if ((sscr != null) && (sscr != "")) { //sscr from editor
  1139. try {
  1140. parseShortScript(sscr);
  1141. } catch (e) {}
  1142. } else {
  1143. src = picture.getAttribute("script"); //script from showplot
  1144. if ((src!=null) && (src != "")) {
  1145. try {
  1146. with (Math) eval(src);
  1147. } catch(err) {alert(err+"\n"+src)}
  1148. }
  1149. }
  1150. } else {
  1151. if (picture.getAttribute("sscr")!='') {
  1152. n = document.createElement('img');
  1153. n.setAttribute("style",picture.getAttribute("style"));
  1154. n.setAttribute("src",AScgiloc+'?sscr='+encodeURIComponent(picture.getAttribute("sscr")));
  1155. pn = picture.parentNode;
  1156. pn.replaceChild(n,picture);
  1157. }
  1158. }
  1159. }
  1160. }
  1161. //modified by David Lippman from original in AsciiSVG.js by Peter Jipsen
  1162. //added min/max type: 0:nothing, 1:arrow, 2:open dot, 3:closed dot
  1163. function plot(fun,x_min,x_max,points,id,min_type,max_type) {
  1164. var pth = [];
  1165. var f = function(x) { return x }, g = fun;
  1166. var name = null;
  1167. if (typeof fun=="string")
  1168. eval("g = function(x){ with(Math) return "+mathjs(fun)+" }");
  1169. else if (typeof fun=="object") {
  1170. eval("f = function(t){ with(Math) return "+mathjs(fun[0])+" }");
  1171. eval("g = function(t){ with(Math) return "+mathjs(fun[1])+" }");
  1172. }
  1173. if (typeof x_min=="string") { name = x_min; x_min = xmin }
  1174. else name = id;
  1175. var min = (x_min==null?xmin:x_min);
  1176. var max = (x_max==null?xmax:x_max);
  1177. if (max <= min) { return null;}
  1178. //else {
  1179. var inc = max-min-0.000001*(max-min);
  1180. inc = (points==null?inc/200:inc/points);
  1181. var gt;
  1182. //alert(typeof g(min))
  1183. for (var t = min; t <= max; t += inc) {
  1184. gt = g(t);
  1185. if (!(isNaN(gt)||Math.abs(gt)=="Infinity")) pth[pth.length] = [f(t), gt];
  1186. }
  1187. path(pth,name);
  1188. if (min_type == 1) {
  1189. arrowhead(pth[1],pth[0]);
  1190. } else if (min_type == 2) {
  1191. dot(pth[0], "open");
  1192. } else if (min_type == 3) {
  1193. dot(pth[0], "closed");
  1194. }
  1195. if (max_type == 1) {
  1196. arrowhead(pth[pth.length-2],pth[pth.length-1]);
  1197. } else if (max_type == 2) {
  1198. dot(pth[pth.length-1], "open");
  1199. } else if (max_type == 3) {
  1200. dot(pth[pth.length-1], "closed");
  1201. }
  1202. return p;
  1203. //}
  1204. }
  1205. //end ASCIIsvgAddon.js dump
  1206. function updateCoords(ind) {
  1207. switchTo("picture"+(ind+1));
  1208. var gx=getX(), gy=getY();
  1209. if ((xmax-gx)*xunitlength > 6*fontsize || (gy-ymin)*yunitlength > 2*fontsize)
  1210. text([xmax,ymin],"("+gx.toFixed(2)+", "+gy.toFixed(2)+")",
  1211. "aboveleft","AScoord"+ind,"");
  1212. else text([xmax,ymin]," ","aboveleft","AScoord"+ind,"");
  1213. }
  1214. function updateCoords0() {updateCoords(0)}
  1215. function updateCoords1() {updateCoords(1)}
  1216. function updateCoords2() {updateCoords(2)}
  1217. function updateCoords3() {updateCoords(3)}
  1218. function updateCoords4() {updateCoords(4)}
  1219. function updateCoords5() {updateCoords(5)}
  1220. function updateCoords6() {updateCoords(6)}
  1221. function updateCoords7() {updateCoords(7)}
  1222. function updateCoords8() {updateCoords(8)}
  1223. function updateCoords9() {updateCoords(9)}
  1224. ASfn = [function() {updatePicture(0)},
  1225. function() {updatePicture(1)},
  1226. function() {updatePicture(2)},
  1227. function() {updatePicture(3)},
  1228. function() {updatePicture(4)},
  1229. function() {updatePicture(5)},
  1230. function() {updatePicture(6)},
  1231. function() {updatePicture(7)},
  1232. function() {updatePicture(8)},
  1233. function() {updatePicture(9)}];
  1234. ASupdateCoords = [function() {updateCoords(0)},
  1235. function() {updateCoords(1)},
  1236. function() {updateCoords(2)},
  1237. function() {updateCoords(3)},
  1238. function() {updateCoords(4)},
  1239. function() {updateCoords(5)},
  1240. function() {updateCoords(6)},
  1241. function() {updateCoords(7)},
  1242. function() {updateCoords(8)},
  1243. function() {updateCoords(9)}];
  1244. // GO1.1 Generic onload by Brothercake
  1245. // http://www.brothercake.com/
  1246. //onload function
  1247. function generic()
  1248. {
  1249. drawPictures();
  1250. };
  1251. //setup onload function
  1252. if(typeof window.addEventListener != 'undefined')
  1253. {
  1254. //.. gecko, safari, konqueror and standard
  1255. window.addEventListener('load', generic, false);
  1256. }
  1257. else if(typeof document.addEventListener != 'undefined')
  1258. {
  1259. //.. opera 7
  1260. document.addEventListener('load', generic, false);
  1261. }
  1262. else if(typeof window.attachEvent != 'undefined')
  1263. {
  1264. //.. win/ie
  1265. window.attachEvent('onload', generic);
  1266. }
  1267. //** remove this condition to degrade older browsers
  1268. else
  1269. {
  1270. //.. mac/ie5 and anything else that gets this far
  1271. //if there's an existing onload function
  1272. if(typeof window.onload == 'function')
  1273. {
  1274. //store it
  1275. var existing = onload;
  1276. //add new onload handler
  1277. window.onload = function()
  1278. {
  1279. //call existing onload function
  1280. existing();
  1281. //call generic onload function
  1282. generic();
  1283. };
  1284. }
  1285. else
  1286. {
  1287. //setup onload function
  1288. window.onload = generic;
  1289. }
  1290. }
  1291. if (checkIfSVGavailable) {
  1292. checkifSVGavailable = false;
  1293. nd = isSVGavailable();
  1294. ASnoSVG = nd!=null;
  1295. }