1 if(!dojo._hasResource["dojox.gfx.svg"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.gfx.svg"] = true;
3 dojo.provide("dojox.gfx.svg");
5 dojo.require("dojox.gfx._base");
6 dojo.require("dojox.gfx.shape");
7 dojo.require("dojox.gfx.path");
9 dojox.gfx.svg.xmlns = {
10 xlink: "http://www.w3.org/1999/xlink",
11 svg: "http://www.w3.org/2000/svg"
14 dojox.gfx.svg.getRef = function(name){
15 // summary: returns a DOM Node specified by the name argument or null
16 // name: String: an SVG external reference
17 if(!name || name == "none") return null;
18 if(name.match(/^url\(#.+\)$/)){
19 return dojo.byId(name.slice(5, -1)); // Node
21 // alternative representation of a reference
22 if(name.match(/^#dojoUnique\d+$/)){
23 // we assume here that a reference was generated by dojox.gfx
24 return dojo.byId(name.slice(1)); // Node
29 dojox.gfx.svg.dasharray = {
33 shortdashdot: [4, 1, 1, 1],
34 shortdashdotdot: [4, 1, 1, 1, 1, 1],
38 dashdot: [4, 3, 1, 3],
39 longdashdot: [8, 3, 1, 3],
40 longdashdotdot: [8, 3, 1, 3, 1, 3]
43 dojo.extend(dojox.gfx.Shape, {
44 // summary: SVG-specific implementation of dojox.gfx.Shape methods
46 setFill: function(fill){
47 // summary: sets a fill object (SVG)
48 // fill: Object: a fill object
49 // (see dojox.gfx.defaultLinearGradient,
50 // dojox.gfx.defaultRadialGradient,
51 // dojox.gfx.defaultPattern,
56 this.fillStyle = null;
57 this.rawNode.setAttribute("fill", "none");
58 this.rawNode.setAttribute("fill-opacity", 0);
62 // FIXME: slightly magical. We're using the outer scope's "f", but setting it later
63 var setter = function(x){
64 // we assume that we're executing in the scope of the node to mutate
65 this.setAttribute(x, f[x].toFixed(8));
67 if(typeof(fill) == "object" && "type" in fill){
71 f = dojox.gfx.makeParameters(dojox.gfx.defaultLinearGradient, fill);
72 var gradient = this._setFillObject(f, "linearGradient");
73 dojo.forEach(["x1", "y1", "x2", "y2"], setter, gradient);
76 f = dojox.gfx.makeParameters(dojox.gfx.defaultRadialGradient, fill);
77 var gradient = this._setFillObject(f, "radialGradient");
78 dojo.forEach(["cx", "cy", "r"], setter, gradient);
81 f = dojox.gfx.makeParameters(dojox.gfx.defaultPattern, fill);
82 var pattern = this._setFillObject(f, "pattern");
83 dojo.forEach(["x", "y", "width", "height"], setter, pattern);
90 var f = dojox.gfx.normalizeColor(fill);
92 this.rawNode.setAttribute("fill", f.toCss());
93 this.rawNode.setAttribute("fill-opacity", f.a);
94 this.rawNode.setAttribute("fill-rule", "evenodd");
98 setStroke: function(stroke){
99 // summary: sets a stroke object (SVG)
100 // stroke: Object: a stroke object
101 // (see dojox.gfx.defaultStroke)
105 this.strokeStyle = null;
106 this.rawNode.setAttribute("stroke", "none");
107 this.rawNode.setAttribute("stroke-opacity", 0);
110 // normalize the stroke
111 if(typeof stroke == "string"){
112 stroke = {color: stroke};
114 var s = this.strokeStyle = dojox.gfx.makeParameters(dojox.gfx.defaultStroke, stroke);
115 s.color = dojox.gfx.normalizeColor(s.color);
116 // generate attributes
117 var rn = this.rawNode;
119 rn.setAttribute("stroke", s.color.toCss());
120 rn.setAttribute("stroke-opacity", s.color.a);
121 rn.setAttribute("stroke-width", s.width);
122 rn.setAttribute("stroke-linecap", s.cap);
123 if(typeof s.join == "number"){
124 rn.setAttribute("stroke-linejoin", "miter");
125 rn.setAttribute("stroke-miterlimit", s.join);
127 rn.setAttribute("stroke-linejoin", s.join);
129 var da = s.style.toLowerCase();
130 if(da in dojox.gfx.svg.dasharray){ da = dojox.gfx.svg.dasharray[da]; }
131 if(da instanceof Array){
133 for(var i = 0; i < da.length; ++i){
137 for(var i = 0; i < da.length; i += 2){
139 if(da[i] < 1){ da[i] = 1; }
141 for(var i = 1; i < da.length; i += 2){
147 rn.setAttribute("stroke-dasharray", da);
148 rn.setAttribute("dojoGfxStrokeStyle", s.style);
153 _getParentSurface: function(){
154 var surface = this.parent;
155 for(; surface && !(surface instanceof dojox.gfx.Surface); surface = surface.parent);
159 _setFillObject: function(f, nodeType){
160 var svgns = dojox.gfx.svg.xmlns.svg;
162 var surface = this._getParentSurface(),
163 defs = surface.defNode,
164 fill = this.rawNode.getAttribute("fill"),
165 ref = dojox.gfx.svg.getRef(fill);
168 if(fill.tagName.toLowerCase() != nodeType.toLowerCase()){
170 fill.parentNode.removeChild(fill);
171 fill = document.createElementNS(svgns, nodeType);
172 fill.setAttribute("id", id);
173 defs.appendChild(fill);
175 while(fill.childNodes.length){
176 fill.removeChild(fill.lastChild);
180 fill = document.createElementNS(svgns, nodeType);
181 fill.setAttribute("id", dojox.gfx._base._getUniqueId());
182 defs.appendChild(fill);
184 if(nodeType == "pattern"){
186 fill.setAttributeNS(null, "patternUnits", "userSpaceOnUse");
188 fill.setAttribute("patternUnits", "userSpaceOnUse");
190 var img = document.createElementNS(svgns, "image");
191 img.setAttribute("x", 0);
192 img.setAttribute("y", 0);
193 img.setAttribute("width", f.width .toFixed(8));
194 img.setAttribute("height", f.height.toFixed(8));
195 img.setAttributeNS(dojox.gfx.svg.xmlns.xlink, "href", f.src);
196 fill.appendChild(img);
199 fill.setAttributeNS(null, "gradientUnits", "userSpaceOnUse");
201 fill.setAttribute("gradientUnits", "userSpaceOnUse");
203 for(var i = 0; i < f.colors.length; ++i){
204 var c = f.colors[i], t = document.createElementNS(svgns, "stop"),
205 cc = c.color = dojox.gfx.normalizeColor(c.color);
206 t.setAttribute("offset", c.offset.toFixed(8));
207 t.setAttribute("stop-color", cc.toCss());
208 t.setAttribute("stop-opacity", cc.a);
212 this.rawNode.setAttribute("fill", "url(#" + fill.getAttribute("id") +")");
213 this.rawNode.removeAttribute("fill-opacity");
214 this.rawNode.setAttribute("fill-rule", "evenodd");
218 _applyTransform: function() {
219 var matrix = this.matrix;
221 var tm = this.matrix;
222 this.rawNode.setAttribute("transform", "matrix(" +
223 tm.xx.toFixed(8) + "," + tm.yx.toFixed(8) + "," +
224 tm.xy.toFixed(8) + "," + tm.yy.toFixed(8) + "," +
225 tm.dx.toFixed(8) + "," + tm.dy.toFixed(8) + ")");
227 this.rawNode.removeAttribute("transform");
232 setRawNode: function(rawNode){
234 // assigns and clears the underlying node that will represent this
235 // shape. Once set, transforms, gradients, etc, can be applied.
236 // (no fill & stroke by default)
237 var r = this.rawNode = rawNode;
238 r.setAttribute("fill", "none");
239 r.setAttribute("fill-opacity", 0);
240 r.setAttribute("stroke", "none");
241 r.setAttribute("stroke-opacity", 0);
242 r.setAttribute("stroke-width", 1);
243 r.setAttribute("stroke-linecap", "butt");
244 r.setAttribute("stroke-linejoin", "miter");
245 r.setAttribute("stroke-miterlimit", 4);
248 setShape: function(newShape){
249 // summary: sets a shape object (SVG)
250 // newShape: Object: a shape object
251 // (see dojox.gfx.defaultPath,
252 // dojox.gfx.defaultPolyline,
253 // dojox.gfx.defaultRect,
254 // dojox.gfx.defaultEllipse,
255 // dojox.gfx.defaultCircle,
256 // dojox.gfx.defaultLine,
257 // or dojox.gfx.defaultImage)
258 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
259 for(var i in this.shape){
260 if(i != "type"){ this.rawNode.setAttribute(i, this.shape[i]); }
267 _moveToFront: function(){
268 // summary: moves a shape to front of its parent's list of shapes (SVG)
269 this.rawNode.parentNode.appendChild(this.rawNode);
272 _moveToBack: function(){
273 // summary: moves a shape to back of its parent's list of shapes (SVG)
274 this.rawNode.parentNode.insertBefore(this.rawNode, this.rawNode.parentNode.firstChild);
279 dojo.declare("dojox.gfx.Group", dojox.gfx.Shape, {
280 // summary: a group shape (SVG), which can be used
281 // to logically group shapes (e.g, to propagate matricies)
282 constructor: function(){
283 dojox.gfx.svg.Container._init.call(this);
285 setRawNode: function(rawNode){
286 // summary: sets a raw SVG node to be used by this shape
287 // rawNode: Node: an SVG node
288 this.rawNode = rawNode;
291 dojox.gfx.Group.nodeType = "g";
293 dojo.declare("dojox.gfx.Rect", dojox.gfx.shape.Rect, {
294 // summary: a rectangle shape (SVG)
295 setShape: function(newShape){
296 // summary: sets a rectangle shape object (SVG)
297 // newShape: Object: a rectangle shape object
298 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
300 for(var i in this.shape){
301 if(i != "type" && i != "r"){ this.rawNode.setAttribute(i, this.shape[i]); }
304 this.rawNode.setAttribute("ry", this.shape.r);
305 this.rawNode.setAttribute("rx", this.shape.r);
310 dojox.gfx.Rect.nodeType = "rect";
312 dojox.gfx.Ellipse = dojox.gfx.shape.Ellipse;
313 dojox.gfx.Ellipse.nodeType = "ellipse";
315 dojox.gfx.Circle = dojox.gfx.shape.Circle;
316 dojox.gfx.Circle.nodeType = "circle";
318 dojox.gfx.Line = dojox.gfx.shape.Line;
319 dojox.gfx.Line.nodeType = "line";
321 dojo.declare("dojox.gfx.Polyline", dojox.gfx.shape.Polyline, {
322 // summary: a polyline/polygon shape (SVG)
323 setShape: function(points, closed){
324 // summary: sets a polyline/polygon shape object (SVG)
325 // points: Object: a polyline/polygon shape object
326 if(points && points instanceof Array){
328 // points: Array: an array of points
329 this.shape = dojox.gfx.makeParameters(this.shape, { points: points });
330 if(closed && this.shape.points.length){
331 this.shape.points.push(this.shape.points[0]);
334 this.shape = dojox.gfx.makeParameters(this.shape, points);
337 var attr = [], p = this.shape.points;
338 for(var i = 0; i < p.length; ++i){
339 if(typeof p[i] == "number"){
340 attr.push(p[i].toFixed(8));
342 attr.push(p[i].x.toFixed(8));
343 attr.push(p[i].y.toFixed(8));
346 this.rawNode.setAttribute("points", attr.join(" "));
350 dojox.gfx.Polyline.nodeType = "polyline";
352 dojo.declare("dojox.gfx.Image", dojox.gfx.shape.Image, {
353 // summary: an image (SVG)
354 setShape: function(newShape){
355 // summary: sets an image shape object (SVG)
356 // newShape: Object: an image shape object
357 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
359 var rawNode = this.rawNode;
360 for(var i in this.shape){
361 if(i != "type" && i != "src"){ rawNode.setAttribute(i, this.shape[i]); }
363 rawNode.setAttributeNS(dojox.gfx.svg.xmlns.xlink, "href", this.shape.src);
367 dojox.gfx.Image.nodeType = "image";
369 dojo.declare("dojox.gfx.Text", dojox.gfx.shape.Text, {
370 // summary: an anchored text (SVG)
371 setShape: function(newShape){
372 // summary: sets a text shape object (SVG)
373 // newShape: Object: a text shape object
374 this.shape = dojox.gfx.makeParameters(this.shape, newShape);
376 var r = this.rawNode, s = this.shape;
377 r.setAttribute("x", s.x);
378 r.setAttribute("y", s.y);
379 r.setAttribute("text-anchor", s.align);
380 r.setAttribute("text-decoration", s.decoration);
381 r.setAttribute("rotate", s.rotated ? 90 : 0);
382 r.setAttribute("kerning", s.kerning ? "auto" : 0);
383 r.setAttribute("text-rendering", "optimizeLegibility");
384 r.textContent = s.text;
387 getTextWidth: function(){
388 // summary: get the text width in pixels
389 var rawNode = this.rawNode,
390 oldParent = rawNode.parentNode,
391 _measurementNode = rawNode.cloneNode(true);
392 _measurementNode.style.visibility = "hidden";
394 // solution to the "orphan issue" in FF
395 var _width = 0, _text = _measurementNode.firstChild.nodeValue;
396 oldParent.appendChild(_measurementNode);
398 // solution to the "orphan issue" in Opera
399 // (nodeValue == "" hangs firefox)
402 _width = parseInt(_measurementNode.getBBox().width);
405 oldParent.removeChild(_measurementNode);
409 dojox.gfx.Text.nodeType = "text";
411 dojo.declare("dojox.gfx.Path", dojox.gfx.path.Path, {
412 // summary: a path shape (SVG)
413 _updateWithSegment: function(segment){
414 // summary: updates the bounding box of path with new segment
415 // segment: Object: a segment
416 dojox.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
417 if(typeof(this.shape.path) == "string"){
418 this.rawNode.setAttribute("d", this.shape.path);
421 setShape: function(newShape){
422 // summary: forms a path using a shape (SVG)
423 // newShape: Object: an SVG path string or a path object (see dojox.gfx.defaultPath)
424 dojox.gfx.Path.superclass.setShape.apply(this, arguments);
425 this.rawNode.setAttribute("d", this.shape.path);
429 dojox.gfx.Path.nodeType = "path";
431 dojo.declare("dojox.gfx.TextPath", dojox.gfx.path.TextPath, {
432 // summary: a textpath shape (SVG)
433 _updateWithSegment: function(segment){
434 // summary: updates the bounding box of path with new segment
435 // segment: Object: a segment
436 dojox.gfx.Path.superclass._updateWithSegment.apply(this, arguments);
439 setShape: function(newShape){
440 // summary: forms a path using a shape (SVG)
441 // newShape: Object: an SVG path string or a path object (see dojox.gfx.defaultPath)
442 dojox.gfx.Path.superclass.setShape.apply(this, arguments);
446 _setTextPath: function(){
447 if(typeof this.shape.path != "string"){ return; }
448 var r = this.rawNode;
450 var tp = document.createElementNS(dojox.gfx.svg.xmlns.svg, "textPath"),
451 tx = document.createTextNode("");
455 var ref = r.firstChild.getAttributeNS(dojox.gfx.svg.xmlns.xlink, "href"),
456 path = ref && dojox.gfx.svg.getRef(ref);
458 var surface = this._getParentSurface();
460 var defs = surface.defNode;
461 path = document.createElementNS(dojox.gfx.svg.xmlns.svg, "path");
462 var id = dojox.gfx._base._getUniqueId();
463 path.setAttribute("id", id);
464 defs.appendChild(path);
465 r.firstChild.setAttributeNS(dojox.gfx.svg.xmlns.xlink, "href", "#" + id);
469 path.setAttribute("d", this.shape.path);
472 _setText: function(){
473 var r = this.rawNode;
475 var tp = document.createElementNS(dojox.gfx.svg.xmlns.svg, "textPath"),
476 tx = document.createTextNode("");
482 r.setAttribute("alignment-baseline", "middle");
485 r.setAttribute("text-anchor", "middle");
486 r.setAttribute("startOffset", "50%");
489 r.setAttribute("text-anchor", "end");
490 r.setAttribute("startOffset", "100%");
493 r.setAttribute("text-anchor", "start");
494 r.setAttribute("startOffset", "0%");
497 //r.parentNode.setAttribute("alignment-baseline", "central");
498 //r.setAttribute("dominant-baseline", "central");
499 r.setAttribute("baseline-shift", "0.5ex");
500 r.setAttribute("text-decoration", t.decoration);
501 r.setAttribute("rotate", t.rotated ? 90 : 0);
502 r.setAttribute("kerning", t.kerning ? "auto" : 0);
503 r.firstChild.data = t.text;
506 dojox.gfx.TextPath.nodeType = "text";
508 dojo.declare("dojox.gfx.Surface", dojox.gfx.shape.Surface, {
509 // summary: a surface object to be used for drawings (SVG)
510 constructor: function(){
511 dojox.gfx.svg.Container._init.call(this);
513 setDimensions: function(width, height){
514 // summary: sets the width and height of the rawNode
515 // width: String: width of surface, e.g., "100px"
516 // height: String: height of surface, e.g., "100px"
517 if(!this.rawNode){ return this; }
518 this.rawNode.setAttribute("width", width);
519 this.rawNode.setAttribute("height", height);
522 getDimensions: function(){
523 // summary: returns an object with properties "width" and "height"
524 return this.rawNode ? {width: this.rawNode.getAttribute("width"), height: this.rawNode.getAttribute("height")} : null; // Object
528 dojox.gfx.createSurface = function(parentNode, width, height){
529 // summary: creates a surface (SVG)
530 // parentNode: Node: a parent node
531 // width: String: width of surface, e.g., "100px"
532 // height: String: height of surface, e.g., "100px"
534 var s = new dojox.gfx.Surface();
535 s.rawNode = document.createElementNS(dojox.gfx.svg.xmlns.svg, "svg");
536 s.rawNode.setAttribute("width", width);
537 s.rawNode.setAttribute("height", height);
539 var node = document.createElementNS(dojox.gfx.svg.xmlns.svg, "defs");
540 s.rawNode.appendChild(node);
543 dojo.byId(parentNode).appendChild(s.rawNode);
544 return s; // dojox.gfx.Surface
549 dojox.gfx.svg.Font = {
550 _setFont: function(){
551 // summary: sets a font object (SVG)
552 var f = this.fontStyle;
553 // next line doesn't work in Firefox 2 or Opera 9
554 //this.rawNode.setAttribute("font", dojox.gfx.makeFontString(this.fontStyle));
555 this.rawNode.setAttribute("font-style", f.style);
556 this.rawNode.setAttribute("font-variant", f.variant);
557 this.rawNode.setAttribute("font-weight", f.weight);
558 this.rawNode.setAttribute("font-size", f.size);
559 this.rawNode.setAttribute("font-family", f.family);
563 dojox.gfx.svg.Container = {
565 dojox.gfx.shape.Container._init.call(this);
567 add: function(shape){
568 // summary: adds a shape to a group/surface
569 // shape: dojox.gfx.Shape: an VML shape object
570 if(this != shape.getParent()){
571 this.rawNode.appendChild(shape.rawNode);
572 //dojox.gfx.Group.superclass.add.apply(this, arguments);
573 //this.inherited(arguments);
574 dojox.gfx.shape.Container.add.apply(this, arguments);
578 remove: function(shape, silently){
579 // summary: remove a shape from a group/surface
580 // shape: dojox.gfx.Shape: an VML shape object
581 // silently: Boolean?: if true, regenerate a picture
582 if(this == shape.getParent()){
583 if(this.rawNode == shape.rawNode.parentNode){
584 this.rawNode.removeChild(shape.rawNode);
586 //dojox.gfx.Group.superclass.remove.apply(this, arguments);
587 //this.inherited(arguments);
588 dojox.gfx.shape.Container.remove.apply(this, arguments);
593 // summary: removes all shapes from a group/surface
594 var r = this.rawNode;
596 r.removeChild(r.lastChild);
598 var d = this.defNode;
601 d.removeChild(d.lastChild);
605 //return this.inherited(arguments); // self
606 return dojox.gfx.shape.Container.clear.apply(this, arguments);
608 _moveChildToFront: dojox.gfx.shape.Container._moveChildToFront,
609 _moveChildToBack: dojox.gfx.shape.Container._moveChildToBack
612 dojo.mixin(dojox.gfx.shape.Creator, {
613 // summary: SVG shape creators
614 createObject: function(shapeType, rawShape){
615 // summary: creates an instance of the passed shapeType class
616 // shapeType: Function: a class constructor to create an instance of
617 // rawShape: Object: properties to be passed in to the classes "setShape" method
618 if(!this.rawNode){ return null; }
619 var shape = new shapeType(),
620 node = document.createElementNS(dojox.gfx.svg.xmlns.svg, shapeType.nodeType);
621 shape.setRawNode(node);
622 this.rawNode.appendChild(node);
623 shape.setShape(rawShape);
625 return shape; // dojox.gfx.Shape
629 dojo.extend(dojox.gfx.Text, dojox.gfx.svg.Font);
630 dojo.extend(dojox.gfx.TextPath, dojox.gfx.svg.Font);
632 dojo.extend(dojox.gfx.Group, dojox.gfx.svg.Container);
633 dojo.extend(dojox.gfx.Group, dojox.gfx.shape.Creator);
635 dojo.extend(dojox.gfx.Surface, dojox.gfx.svg.Container);
636 dojo.extend(dojox.gfx.Surface, dojox.gfx.shape.Creator);