svg.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.gfx.svg"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.gfx.svg"] = true;
  8. dojo.provide("dojox.gfx.svg");
  9. dojo.require("dojox.gfx._base");
  10. dojo.require("dojox.gfx.shape");
  11. dojo.require("dojox.gfx.path");
  12. (function(){
  13. var d = dojo, g = dojox.gfx, gs = g.shape, svg = g.svg;
  14. svg.useSvgWeb = (typeof window.svgweb != "undefined");
  15. function _createElementNS(ns, nodeType){
  16. // summary:
  17. // Internal helper to deal with creating elements that
  18. // are namespaced. Mainly to get SVG markup output
  19. // working on IE.
  20. if(dojo.doc.createElementNS){
  21. return dojo.doc.createElementNS(ns,nodeType);
  22. }else{
  23. return dojo.doc.createElement(nodeType);
  24. }
  25. }
  26. function _createTextNode(text){
  27. if(svg.useSvgWeb){
  28. return dojo.doc.createTextNode(text, true);
  29. }else{
  30. return dojo.doc.createTextNode(text);
  31. }
  32. }
  33. function _createFragment(){
  34. if(svg.useSvgWeb){
  35. return dojo.doc.createDocumentFragment(true);
  36. }else{
  37. return dojo.doc.createDocumentFragment();
  38. }
  39. }
  40. svg.xmlns = {
  41. xlink: "http://www.w3.org/1999/xlink",
  42. svg: "http://www.w3.org/2000/svg"
  43. };
  44. svg.getRef = function(name){
  45. // summary: returns a DOM Node specified by the name argument or null
  46. // name: String: an SVG external reference
  47. if(!name || name == "none") return null;
  48. if(name.match(/^url\(#.+\)$/)){
  49. return d.byId(name.slice(5, -1)); // Node
  50. }
  51. // alternative representation of a reference
  52. if(name.match(/^#dojoUnique\d+$/)){
  53. // we assume here that a reference was generated by dojox.gfx
  54. return d.byId(name.slice(1)); // Node
  55. }
  56. return null; // Node
  57. };
  58. svg.dasharray = {
  59. solid: "none",
  60. shortdash: [4, 1],
  61. shortdot: [1, 1],
  62. shortdashdot: [4, 1, 1, 1],
  63. shortdashdotdot: [4, 1, 1, 1, 1, 1],
  64. dot: [1, 3],
  65. dash: [4, 3],
  66. longdash: [8, 3],
  67. dashdot: [4, 3, 1, 3],
  68. longdashdot: [8, 3, 1, 3],
  69. longdashdotdot: [8, 3, 1, 3, 1, 3]
  70. };
  71. d.declare("dojox.gfx.svg.Shape", gs.Shape, {
  72. // summary: SVG-specific implementation of dojox.gfx.Shape methods
  73. setFill: function(fill){
  74. // summary: sets a fill object (SVG)
  75. // fill: Object: a fill object
  76. // (see dojox.gfx.defaultLinearGradient,
  77. // dojox.gfx.defaultRadialGradient,
  78. // dojox.gfx.defaultPattern,
  79. // or dojo.Color)
  80. if(!fill){
  81. // don't fill
  82. this.fillStyle = null;
  83. this.rawNode.setAttribute("fill", "none");
  84. this.rawNode.setAttribute("fill-opacity", 0);
  85. return this;
  86. }
  87. var f;
  88. // FIXME: slightly magical. We're using the outer scope's "f", but setting it later
  89. var setter = function(x){
  90. // we assume that we're executing in the scope of the node to mutate
  91. this.setAttribute(x, f[x].toFixed(8));
  92. };
  93. if(typeof(fill) == "object" && "type" in fill){
  94. // gradient
  95. switch(fill.type){
  96. case "linear":
  97. f = g.makeParameters(g.defaultLinearGradient, fill);
  98. var gradient = this._setFillObject(f, "linearGradient");
  99. d.forEach(["x1", "y1", "x2", "y2"], setter, gradient);
  100. break;
  101. case "radial":
  102. f = g.makeParameters(g.defaultRadialGradient, fill);
  103. var gradient = this._setFillObject(f, "radialGradient");
  104. d.forEach(["cx", "cy", "r"], setter, gradient);
  105. break;
  106. case "pattern":
  107. f = g.makeParameters(g.defaultPattern, fill);
  108. var pattern = this._setFillObject(f, "pattern");
  109. d.forEach(["x", "y", "width", "height"], setter, pattern);
  110. break;
  111. }
  112. this.fillStyle = f;
  113. return this;
  114. }
  115. // color object
  116. var f = g.normalizeColor(fill);
  117. this.fillStyle = f;
  118. this.rawNode.setAttribute("fill", f.toCss());
  119. this.rawNode.setAttribute("fill-opacity", f.a);
  120. this.rawNode.setAttribute("fill-rule", "evenodd");
  121. return this; // self
  122. },
  123. setStroke: function(stroke){
  124. // summary:
  125. // sets a stroke object (SVG)
  126. // stroke: Object
  127. // a stroke object (see dojox.gfx.defaultStroke)
  128. var rn = this.rawNode;
  129. if(!stroke){
  130. // don't stroke
  131. this.strokeStyle = null;
  132. rn.setAttribute("stroke", "none");
  133. rn.setAttribute("stroke-opacity", 0);
  134. return this;
  135. }
  136. // normalize the stroke
  137. if(typeof stroke == "string" || d.isArray(stroke) || stroke instanceof d.Color){
  138. stroke = { color: stroke };
  139. }
  140. var s = this.strokeStyle = g.makeParameters(g.defaultStroke, stroke);
  141. s.color = g.normalizeColor(s.color);
  142. // generate attributes
  143. if(s){
  144. rn.setAttribute("stroke", s.color.toCss());
  145. rn.setAttribute("stroke-opacity", s.color.a);
  146. rn.setAttribute("stroke-width", s.width);
  147. rn.setAttribute("stroke-linecap", s.cap);
  148. if(typeof s.join == "number"){
  149. rn.setAttribute("stroke-linejoin", "miter");
  150. rn.setAttribute("stroke-miterlimit", s.join);
  151. }else{
  152. rn.setAttribute("stroke-linejoin", s.join);
  153. }
  154. var da = s.style.toLowerCase();
  155. if(da in svg.dasharray){
  156. da = svg.dasharray[da];
  157. }
  158. if(da instanceof Array){
  159. da = d._toArray(da);
  160. for(var i = 0; i < da.length; ++i){
  161. da[i] *= s.width;
  162. }
  163. if(s.cap != "butt"){
  164. for(var i = 0; i < da.length; i += 2){
  165. da[i] -= s.width;
  166. if(da[i] < 1){ da[i] = 1; }
  167. }
  168. for(var i = 1; i < da.length; i += 2){
  169. da[i] += s.width;
  170. }
  171. }
  172. da = da.join(",");
  173. }
  174. rn.setAttribute("stroke-dasharray", da);
  175. rn.setAttribute("dojoGfxStrokeStyle", s.style);
  176. }
  177. return this; // self
  178. },
  179. _getParentSurface: function(){
  180. var surface = this.parent;
  181. for(; surface && !(surface instanceof g.Surface); surface = surface.parent);
  182. return surface;
  183. },
  184. _setFillObject: function(f, nodeType){
  185. var svgns = svg.xmlns.svg;
  186. this.fillStyle = f;
  187. var surface = this._getParentSurface(),
  188. defs = surface.defNode,
  189. fill = this.rawNode.getAttribute("fill"),
  190. ref = svg.getRef(fill);
  191. if(ref){
  192. fill = ref;
  193. if(fill.tagName.toLowerCase() != nodeType.toLowerCase()){
  194. var id = fill.id;
  195. fill.parentNode.removeChild(fill);
  196. fill = _createElementNS(svgns, nodeType);
  197. fill.setAttribute("id", id);
  198. defs.appendChild(fill);
  199. }else{
  200. while(fill.childNodes.length){
  201. fill.removeChild(fill.lastChild);
  202. }
  203. }
  204. }else{
  205. fill = _createElementNS(svgns, nodeType);
  206. fill.setAttribute("id", g._base._getUniqueId());
  207. defs.appendChild(fill);
  208. }
  209. if(nodeType == "pattern"){
  210. fill.setAttribute("patternUnits", "userSpaceOnUse");
  211. var img = _createElementNS(svgns, "image");
  212. img.setAttribute("x", 0);
  213. img.setAttribute("y", 0);
  214. img.setAttribute("width", f.width .toFixed(8));
  215. img.setAttribute("height", f.height.toFixed(8));
  216. img.setAttributeNS(svg.xmlns.xlink, "xlink:href", f.src);
  217. fill.appendChild(img);
  218. }else{
  219. fill.setAttribute("gradientUnits", "userSpaceOnUse");
  220. for(var i = 0; i < f.colors.length; ++i){
  221. var c = f.colors[i], t = _createElementNS(svgns, "stop"),
  222. cc = c.color = g.normalizeColor(c.color);
  223. t.setAttribute("offset", c.offset.toFixed(8));
  224. t.setAttribute("stop-color", cc.toCss());
  225. t.setAttribute("stop-opacity", cc.a);
  226. fill.appendChild(t);
  227. }
  228. }
  229. this.rawNode.setAttribute("fill", "url(#" + fill.getAttribute("id") +")");
  230. this.rawNode.removeAttribute("fill-opacity");
  231. this.rawNode.setAttribute("fill-rule", "evenodd");
  232. return fill;
  233. },
  234. _applyTransform: function() {
  235. var matrix = this.matrix;
  236. if(matrix){
  237. var tm = this.matrix;
  238. this.rawNode.setAttribute("transform", "matrix(" +
  239. tm.xx.toFixed(8) + "," + tm.yx.toFixed(8) + "," +
  240. tm.xy.toFixed(8) + "," + tm.yy.toFixed(8) + "," +
  241. tm.dx.toFixed(8) + "," + tm.dy.toFixed(8) + ")");
  242. }else{
  243. this.rawNode.removeAttribute("transform");
  244. }
  245. return this;
  246. },
  247. setRawNode: function(rawNode){
  248. // summary:
  249. // assigns and clears the underlying node that will represent this
  250. // shape. Once set, transforms, gradients, etc, can be applied.
  251. // (no fill & stroke by default)
  252. var r = this.rawNode = rawNode;
  253. if(this.shape.type!="image"){
  254. r.setAttribute("fill", "none");
  255. }
  256. r.setAttribute("fill-opacity", 0);
  257. r.setAttribute("stroke", "none");
  258. r.setAttribute("stroke-opacity", 0);
  259. r.setAttribute("stroke-width", 1);
  260. r.setAttribute("stroke-linecap", "butt");
  261. r.setAttribute("stroke-linejoin", "miter");
  262. r.setAttribute("stroke-miterlimit", 4);
  263. },
  264. setShape: function(newShape){
  265. // summary: sets a shape object (SVG)
  266. // newShape: Object: a shape object
  267. // (see dojox.gfx.defaultPath,
  268. // dojox.gfx.defaultPolyline,
  269. // dojox.gfx.defaultRect,
  270. // dojox.gfx.defaultEllipse,
  271. // dojox.gfx.defaultCircle,
  272. // dojox.gfx.defaultLine,
  273. // or dojox.gfx.defaultImage)
  274. this.shape = g.makeParameters(this.shape, newShape);
  275. for(var i in this.shape){
  276. if(i != "type"){
  277. this.rawNode.setAttribute(i, this.shape[i]);
  278. }
  279. }
  280. this.bbox = null;
  281. return this; // self
  282. },
  283. // move family
  284. _moveToFront: function(){
  285. // summary: moves a shape to front of its parent's list of shapes (SVG)
  286. this.rawNode.parentNode.appendChild(this.rawNode);
  287. return this; // self
  288. },
  289. _moveToBack: function(){
  290. // summary: moves a shape to back of its parent's list of shapes (SVG)
  291. this.rawNode.parentNode.insertBefore(this.rawNode, this.rawNode.parentNode.firstChild);
  292. return this; // self
  293. }
  294. });
  295. dojo.declare("dojox.gfx.svg.Group", svg.Shape, {
  296. // summary: a group shape (SVG), which can be used
  297. // to logically group shapes (e.g, to propagate matricies)
  298. constructor: function(){
  299. gs.Container._init.call(this);
  300. },
  301. setRawNode: function(rawNode){
  302. // summary: sets a raw SVG node to be used by this shape
  303. // rawNode: Node: an SVG node
  304. this.rawNode = rawNode;
  305. }
  306. });
  307. svg.Group.nodeType = "g";
  308. dojo.declare("dojox.gfx.svg.Rect", [svg.Shape, gs.Rect], {
  309. // summary: a rectangle shape (SVG)
  310. setShape: function(newShape){
  311. // summary: sets a rectangle shape object (SVG)
  312. // newShape: Object: a rectangle shape object
  313. this.shape = g.makeParameters(this.shape, newShape);
  314. this.bbox = null;
  315. for(var i in this.shape){
  316. if(i != "type" && i != "r"){
  317. this.rawNode.setAttribute(i, this.shape[i]);
  318. }
  319. }
  320. if(this.shape.r){
  321. this.rawNode.setAttribute("ry", this.shape.r);
  322. this.rawNode.setAttribute("rx", this.shape.r);
  323. }
  324. return this; // self
  325. }
  326. });
  327. svg.Rect.nodeType = "rect";
  328. dojo.declare("dojox.gfx.svg.Ellipse", [svg.Shape, gs.Ellipse], {});
  329. svg.Ellipse.nodeType = "ellipse";
  330. dojo.declare("dojox.gfx.svg.Circle", [svg.Shape, gs.Circle], {});
  331. svg.Circle.nodeType = "circle";
  332. dojo.declare("dojox.gfx.svg.Line", [svg.Shape, gs.Line], {});
  333. svg.Line.nodeType = "line";
  334. dojo.declare("dojox.gfx.svg.Polyline", [svg.Shape, gs.Polyline], {
  335. // summary: a polyline/polygon shape (SVG)
  336. setShape: function(points, closed){
  337. // summary: sets a polyline/polygon shape object (SVG)
  338. // points: Object: a polyline/polygon shape object
  339. if(points && points instanceof Array){
  340. // branch
  341. // points: Array: an array of points
  342. this.shape = g.makeParameters(this.shape, { points: points });
  343. if(closed && this.shape.points.length){
  344. this.shape.points.push(this.shape.points[0]);
  345. }
  346. }else{
  347. this.shape = g.makeParameters(this.shape, points);
  348. }
  349. this.bbox = null;
  350. this._normalizePoints();
  351. var attr = [], p = this.shape.points;
  352. for(var i = 0; i < p.length; ++i){
  353. attr.push(p[i].x.toFixed(8), p[i].y.toFixed(8));
  354. }
  355. this.rawNode.setAttribute("points", attr.join(" "));
  356. return this; // self
  357. }
  358. });
  359. svg.Polyline.nodeType = "polyline";
  360. dojo.declare("dojox.gfx.svg.Image", [svg.Shape, gs.Image], {
  361. // summary: an image (SVG)
  362. setShape: function(newShape){
  363. // summary: sets an image shape object (SVG)
  364. // newShape: Object: an image shape object
  365. this.shape = g.makeParameters(this.shape, newShape);
  366. this.bbox = null;
  367. var rawNode = this.rawNode;
  368. for(var i in this.shape){
  369. if(i != "type" && i != "src"){
  370. rawNode.setAttribute(i, this.shape[i]);
  371. }
  372. }
  373. rawNode.setAttribute("preserveAspectRatio", "none");
  374. rawNode.setAttributeNS(svg.xmlns.xlink, "xlink:href", this.shape.src);
  375. return this; // self
  376. }
  377. });
  378. svg.Image.nodeType = "image";
  379. dojo.declare("dojox.gfx.svg.Text", [svg.Shape, gs.Text], {
  380. // summary: an anchored text (SVG)
  381. setShape: function(newShape){
  382. // summary: sets a text shape object (SVG)
  383. // newShape: Object: a text shape object
  384. this.shape = g.makeParameters(this.shape, newShape);
  385. this.bbox = null;
  386. var r = this.rawNode, s = this.shape;
  387. r.setAttribute("x", s.x);
  388. r.setAttribute("y", s.y);
  389. r.setAttribute("text-anchor", s.align);
  390. r.setAttribute("text-decoration", s.decoration);
  391. r.setAttribute("rotate", s.rotated ? 90 : 0);
  392. r.setAttribute("kerning", s.kerning ? "auto" : 0);
  393. r.setAttribute("text-rendering", "optimizeLegibility");
  394. // update the text content
  395. if(r.firstChild){
  396. r.firstChild.nodeValue = s.text;
  397. }else{
  398. r.appendChild(_createTextNode(s.text));
  399. }
  400. return this; // self
  401. },
  402. getTextWidth: function(){
  403. // summary: get the text width in pixels
  404. var rawNode = this.rawNode,
  405. oldParent = rawNode.parentNode,
  406. _measurementNode = rawNode.cloneNode(true);
  407. _measurementNode.style.visibility = "hidden";
  408. // solution to the "orphan issue" in FF
  409. var _width = 0, _text = _measurementNode.firstChild.nodeValue;
  410. oldParent.appendChild(_measurementNode);
  411. // solution to the "orphan issue" in Opera
  412. // (nodeValue == "" hangs firefox)
  413. if(_text!=""){
  414. while(!_width){
  415. //Yang: work around svgweb bug 417 -- http://code.google.com/p/svgweb/issues/detail?id=417
  416. if (_measurementNode.getBBox)
  417. _width = parseInt(_measurementNode.getBBox().width);
  418. else
  419. _width = 68;
  420. }
  421. }
  422. oldParent.removeChild(_measurementNode);
  423. return _width;
  424. }
  425. });
  426. svg.Text.nodeType = "text";
  427. dojo.declare("dojox.gfx.svg.Path", [svg.Shape, g.path.Path], {
  428. // summary: a path shape (SVG)
  429. _updateWithSegment: function(segment){
  430. // summary: updates the bounding box of path with new segment
  431. // segment: Object: a segment
  432. this.inherited(arguments);
  433. if(typeof(this.shape.path) == "string"){
  434. this.rawNode.setAttribute("d", this.shape.path);
  435. }
  436. },
  437. setShape: function(newShape){
  438. // summary: forms a path using a shape (SVG)
  439. // newShape: Object: an SVG path string or a path object (see dojox.gfx.defaultPath)
  440. this.inherited(arguments);
  441. if(this.shape.path){
  442. this.rawNode.setAttribute("d", this.shape.path);
  443. }else{
  444. this.rawNode.removeAttribute("d");
  445. }
  446. return this; // self
  447. }
  448. });
  449. svg.Path.nodeType = "path";
  450. dojo.declare("dojox.gfx.svg.TextPath", [svg.Shape, g.path.TextPath], {
  451. // summary: a textpath shape (SVG)
  452. _updateWithSegment: function(segment){
  453. // summary: updates the bounding box of path with new segment
  454. // segment: Object: a segment
  455. this.inherited(arguments);
  456. this._setTextPath();
  457. },
  458. setShape: function(newShape){
  459. // summary: forms a path using a shape (SVG)
  460. // newShape: Object: an SVG path string or a path object (see dojox.gfx.defaultPath)
  461. this.inherited(arguments);
  462. this._setTextPath();
  463. return this; // self
  464. },
  465. _setTextPath: function(){
  466. if(typeof this.shape.path != "string"){ return; }
  467. var r = this.rawNode;
  468. if(!r.firstChild){
  469. var tp = _createElementNS(svg.xmlns.svg, "textPath"),
  470. tx = _createTextNode("");
  471. tp.appendChild(tx);
  472. r.appendChild(tp);
  473. }
  474. var ref = r.firstChild.getAttributeNS(svg.xmlns.xlink, "href"),
  475. path = ref && svg.getRef(ref);
  476. if(!path){
  477. var surface = this._getParentSurface();
  478. if(surface){
  479. var defs = surface.defNode;
  480. path = _createElementNS(svg.xmlns.svg, "path");
  481. var id = g._base._getUniqueId();
  482. path.setAttribute("id", id);
  483. defs.appendChild(path);
  484. r.firstChild.setAttributeNS(svg.xmlns.xlink, "xlink:href", "#" + id);
  485. }
  486. }
  487. if(path){
  488. path.setAttribute("d", this.shape.path);
  489. }
  490. },
  491. _setText: function(){
  492. var r = this.rawNode;
  493. if(!r.firstChild){
  494. var tp = _createElementNS(svg.xmlns.svg, "textPath"),
  495. tx = _createTextNode("");
  496. tp.appendChild(tx);
  497. r.appendChild(tp);
  498. }
  499. r = r.firstChild;
  500. var t = this.text;
  501. r.setAttribute("alignment-baseline", "middle");
  502. switch(t.align){
  503. case "middle":
  504. r.setAttribute("text-anchor", "middle");
  505. r.setAttribute("startOffset", "50%");
  506. break;
  507. case "end":
  508. r.setAttribute("text-anchor", "end");
  509. r.setAttribute("startOffset", "100%");
  510. break;
  511. default:
  512. r.setAttribute("text-anchor", "start");
  513. r.setAttribute("startOffset", "0%");
  514. break;
  515. }
  516. //r.parentNode.setAttribute("alignment-baseline", "central");
  517. //r.setAttribute("dominant-baseline", "central");
  518. r.setAttribute("baseline-shift", "0.5ex");
  519. r.setAttribute("text-decoration", t.decoration);
  520. r.setAttribute("rotate", t.rotated ? 90 : 0);
  521. r.setAttribute("kerning", t.kerning ? "auto" : 0);
  522. r.firstChild.data = t.text;
  523. }
  524. });
  525. svg.TextPath.nodeType = "text";
  526. dojo.declare("dojox.gfx.svg.Surface", gs.Surface, {
  527. // summary: a surface object to be used for drawings (SVG)
  528. constructor: function(){
  529. gs.Container._init.call(this);
  530. },
  531. destroy: function(){
  532. this.defNode = null; // release the external reference
  533. this.inherited(arguments);
  534. },
  535. setDimensions: function(width, height){
  536. // summary: sets the width and height of the rawNode
  537. // width: String: width of surface, e.g., "100px"
  538. // height: String: height of surface, e.g., "100px"
  539. if(!this.rawNode){ return this; }
  540. this.rawNode.setAttribute("width", width);
  541. this.rawNode.setAttribute("height", height);
  542. return this; // self
  543. },
  544. getDimensions: function(){
  545. // summary: returns an object with properties "width" and "height"
  546. var t = this.rawNode ? {
  547. width: g.normalizedLength(this.rawNode.getAttribute("width")),
  548. height: g.normalizedLength(this.rawNode.getAttribute("height"))} : null;
  549. return t; // Object
  550. }
  551. });
  552. svg.createSurface = function(parentNode, width, height){
  553. // summary: creates a surface (SVG)
  554. // parentNode: Node: a parent node
  555. // width: String: width of surface, e.g., "100px"
  556. // height: String: height of surface, e.g., "100px"
  557. var s = new svg.Surface();
  558. s.rawNode = _createElementNS(svg.xmlns.svg, "svg");
  559. if(width){
  560. s.rawNode.setAttribute("width", width);
  561. }
  562. if(height){
  563. s.rawNode.setAttribute("height", height);
  564. }
  565. var defNode = _createElementNS(svg.xmlns.svg, "defs");
  566. s.rawNode.appendChild(defNode);
  567. s.defNode = defNode;
  568. s._parent = d.byId(parentNode);
  569. s._parent.appendChild(s.rawNode);
  570. return s; // dojox.gfx.Surface
  571. };
  572. // Extenders
  573. var Font = {
  574. _setFont: function(){
  575. // summary: sets a font object (SVG)
  576. var f = this.fontStyle;
  577. // next line doesn't work in Firefox 2 or Opera 9
  578. //this.rawNode.setAttribute("font", dojox.gfx.makeFontString(this.fontStyle));
  579. this.rawNode.setAttribute("font-style", f.style);
  580. this.rawNode.setAttribute("font-variant", f.variant);
  581. this.rawNode.setAttribute("font-weight", f.weight);
  582. this.rawNode.setAttribute("font-size", f.size);
  583. this.rawNode.setAttribute("font-family", f.family);
  584. }
  585. };
  586. var C = gs.Container, Container = {
  587. openBatch: function() {
  588. // summary: starts a new batch, subsequent new child shapes will be held in
  589. // the batch instead of appending to the container directly
  590. this.fragment = _createFragment();
  591. },
  592. closeBatch: function() {
  593. // summary: submits the current batch, append all pending child shapes to DOM
  594. if (this.fragment) {
  595. this.rawNode.appendChild(this.fragment);
  596. delete this.fragment;
  597. }
  598. },
  599. add: function(shape){
  600. // summary: adds a shape to a group/surface
  601. // shape: dojox.gfx.Shape: an VML shape object
  602. if(this != shape.getParent()){
  603. if (this.fragment) {
  604. this.fragment.appendChild(shape.rawNode);
  605. } else {
  606. this.rawNode.appendChild(shape.rawNode);
  607. }
  608. C.add.apply(this, arguments);
  609. }
  610. return this; // self
  611. },
  612. remove: function(shape, silently){
  613. // summary: remove a shape from a group/surface
  614. // shape: dojox.gfx.Shape: an VML shape object
  615. // silently: Boolean?: if true, regenerate a picture
  616. if(this == shape.getParent()){
  617. if(this.rawNode == shape.rawNode.parentNode){
  618. this.rawNode.removeChild(shape.rawNode);
  619. }
  620. if(this.fragment && this.fragment == shape.rawNode.parentNode){
  621. this.fragment.removeChild(shape.rawNode);
  622. }
  623. C.remove.apply(this, arguments);
  624. }
  625. return this; // self
  626. },
  627. clear: function(){
  628. // summary: removes all shapes from a group/surface
  629. var r = this.rawNode;
  630. while(r.lastChild){
  631. r.removeChild(r.lastChild);
  632. }
  633. var defNode = this.defNode;
  634. if(defNode){
  635. while(defNode.lastChild){
  636. defNode.removeChild(defNode.lastChild);
  637. }
  638. r.appendChild(defNode);
  639. }
  640. return C.clear.apply(this, arguments);
  641. },
  642. _moveChildToFront: C._moveChildToFront,
  643. _moveChildToBack: C._moveChildToBack
  644. };
  645. var Creator = {
  646. // summary: SVG shape creators
  647. createObject: function(shapeType, rawShape){
  648. // summary: creates an instance of the passed shapeType class
  649. // shapeType: Function: a class constructor to create an instance of
  650. // rawShape: Object: properties to be passed in to the classes "setShape" method
  651. if(!this.rawNode){ return null; }
  652. var shape = new shapeType(),
  653. node = _createElementNS(svg.xmlns.svg, shapeType.nodeType);
  654. shape.setRawNode(node);
  655. shape.setShape(rawShape);
  656. // rawNode.appendChild() will be done inside this.add(shape) below
  657. this.add(shape);
  658. return shape; // dojox.gfx.Shape
  659. }
  660. };
  661. d.extend(svg.Text, Font);
  662. d.extend(svg.TextPath, Font);
  663. d.extend(svg.Group, Container);
  664. d.extend(svg.Group, gs.Creator);
  665. d.extend(svg.Group, Creator);
  666. d.extend(svg.Surface, Container);
  667. d.extend(svg.Surface, gs.Creator);
  668. d.extend(svg.Surface, Creator);
  669. // some specific override for svgweb + flash
  670. if(svg.useSvgWeb){
  671. // override createSurface()
  672. svg.createSurface = function(parentNode, width, height){
  673. var s = new svg.Surface();
  674. // ensure width / height
  675. if(!width || !height){
  676. var pos = d.position(parentNode);
  677. width = width || pos.w;
  678. height = height || pos.h;
  679. }
  680. // ensure id
  681. parentNode = d.byId(parentNode);
  682. var id = parentNode.id ? parentNode.id+'_svgweb' : g._base._getUniqueId();
  683. // create dynamic svg root
  684. var mockSvg = _createElementNS(svg.xmlns.svg, 'svg');
  685. mockSvg.id = id;
  686. mockSvg.setAttribute('width', width);
  687. mockSvg.setAttribute('height', height);
  688. svgweb.appendChild(mockSvg, parentNode);
  689. // notice: any call to the raw node before flash init will fail.
  690. mockSvg.addEventListener('SVGLoad', function(){
  691. // become loaded
  692. s.rawNode = this;
  693. s.isLoaded = true;
  694. // init defs
  695. var defNode = _createElementNS(svg.xmlns.svg, "defs");
  696. s.rawNode.appendChild(defNode);
  697. s.defNode = defNode;
  698. // notify application
  699. if (s.onLoad)
  700. s.onLoad(s);
  701. }, false);
  702. // flash not loaded yet
  703. s.isLoaded = false;
  704. return s;
  705. };
  706. // override Surface.destroy()
  707. svg.Surface.extend({
  708. destroy: function(){
  709. var mockSvg = this.rawNode;
  710. svgweb.removeChild(mockSvg, mockSvg.parentNode);
  711. }
  712. });
  713. // override connect() & disconnect() for Shape & Surface event processing
  714. var _eventsProcessing = {
  715. connect: function(name, object, method){
  716. // connect events using the mock addEventListener() provided by svgweb
  717. if (name.substring(0, 2)==='on') { name = name.substring(2); }
  718. if (arguments.length == 2) {
  719. method = object;
  720. } else {
  721. method = d.hitch(object, method);
  722. }
  723. this.getEventSource().addEventListener(name, method, false);
  724. return [this, name, method];
  725. },
  726. disconnect: function(token){
  727. // disconnect events using the mock removeEventListener() provided by svgweb
  728. this.getEventSource().removeEventListener(token[1], token[2], false);
  729. delete token[0];
  730. }
  731. };
  732. dojo.extend(svg.Shape, _eventsProcessing);
  733. dojo.extend(svg.Surface, _eventsProcessing);
  734. }
  735. // see if we are required to initilize
  736. if(g.loadAndSwitch === "svg"){
  737. g.switchTo("svg");
  738. delete g.loadAndSwitch;
  739. }
  740. })();
  741. }