source: Dev/branches/rest-dojo-ui/client/dojox/gfx/svg.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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