source: Dev/trunk/src/client/dojox/gfx/shape.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 32.4 KB
Line 
1define(["./_base", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/kernel", "dojo/_base/sniff",
2        "dojo/on", "dojo/_base/array", "dojo/dom-construct", "dojo/_base/Color", "./matrix" ],
3        function(g, lang, declare, kernel, has, on, arr, domConstruct, Color, matrixLib){
4
5        var shape = g.shape = {
6                // summary:
7                //              This module contains the core graphics Shape API.
8                //              Different graphics renderer implementation modules (svg, canvas, vml, silverlight, etc.) extend this
9                //              basic api to provide renderer-specific implementations for each shape.
10        };
11
12        shape.Shape = declare("dojox.gfx.shape.Shape", null, {
13                // summary:
14                //              a Shape object, which knows how to apply
15                //              graphical attributes and transformations
16       
17                constructor: function(){
18                        // rawNode: Node
19                        //              underlying graphics-renderer-specific implementation object (if applicable)
20                        this.rawNode = null;
21
22                        // shape: Object
23                        //              an abstract shape object
24                        //              (see dojox/gfx.defaultPath,
25                        //              dojox/gfx.defaultPolyline,
26                        //              dojox/gfx.defaultRect,
27                        //              dojox/gfx.defaultEllipse,
28                        //              dojox/gfx.defaultCircle,
29                        //              dojox/gfx.defaultLine,
30                        //              or dojox/gfx.defaultImage)
31                        this.shape = null;
32       
33                        // matrix: dojox/gfx/matrix.Matrix2D
34                        //              a transformation matrix
35                        this.matrix = null;
36       
37                        // fillStyle: dojox/gfx.Fill
38                        //              a fill object
39                        //              (see dojox/gfx.defaultLinearGradient,
40                        //              dojox/gfx.defaultRadialGradient,
41                        //              dojox/gfx.defaultPattern,
42                        //              or dojo/Color)
43                        this.fillStyle = null;
44       
45                        // strokeStyle: dojox/gfx.Stroke
46                        //              a stroke object
47                        //              (see dojox/gfx.defaultStroke)
48                        this.strokeStyle = null;
49       
50                        // bbox: dojox/gfx.Rectangle
51                        //              a bounding box of this shape
52                        //              (see dojox/gfx.defaultRect)
53                        this.bbox = null;
54       
55                        // virtual group structure
56       
57                        // parent: Object
58                        //              a parent or null
59                        //              (see dojox/gfx/shape.Surface,
60                        //              or dojox/gfx.Group)
61                        this.parent = null;
62       
63                        // parentMatrix: dojox/gfx/matrix.Matrix2D
64                        //              a transformation matrix inherited from the parent
65                        this.parentMatrix = null;
66
67                        if(has("gfxRegistry")){
68                                var uid = shape.register(this);
69                                this.getUID = function(){
70                                        return uid;
71                                }
72                        }
73                },
74               
75                destroy: function(){
76                        // summary:
77                        //              Releases all internal resources owned by this shape. Once this method has been called,
78                        //              the instance is considered destroyed and should not be used anymore.
79                        if(has("gfxRegistry")){
80                                shape.dispose(this);
81                        }
82                        if(this.rawNode && "__gfxObject__" in this.rawNode){
83                                this.rawNode.__gfxObject__ = null;
84                        }
85                        this.rawNode = null;
86                },
87       
88                // trivial getters
89       
90                getNode: function(){
91                        // summary:
92                        //              Different graphics rendering subsystems implement shapes in different ways.  This
93                        //              method provides access to the underlying graphics subsystem object.  Clients calling this
94                        //              method and using the return value must be careful not to try sharing or using the underlying node
95                        //              in a general way across renderer implementation.
96                        //              Returns the underlying graphics Node, or null if no underlying graphics node is used by this shape.
97                        return this.rawNode; // Node
98                },
99                getShape: function(){
100                        // summary:
101                        //              returns the current Shape object or null
102                        //              (see dojox/gfx.defaultPath,
103                        //              dojox/gfx.defaultPolyline,
104                        //              dojox/gfx.defaultRect,
105                        //              dojox/gfx.defaultEllipse,
106                        //              dojox/gfx.defaultCircle,
107                        //              dojox/gfx.defaultLine,
108                        //              or dojox/gfx.defaultImage)
109                        return this.shape; // Object
110                },
111                getTransform: function(){
112                        // summary:
113                        //              Returns the current transformation matrix applied to this Shape or null
114                        return this.matrix;     // dojox/gfx/matrix.Matrix2D
115                },
116                getFill: function(){
117                        // summary:
118                        //              Returns the current fill object or null
119                        //              (see dojox/gfx.defaultLinearGradient,
120                        //              dojox/gfx.defaultRadialGradient,
121                        //              dojox/gfx.defaultPattern,
122                        //              or dojo/Color)
123                        return this.fillStyle;  // Object
124                },
125                getStroke: function(){
126                        // summary:
127                        //              Returns the current stroke object or null
128                        //              (see dojox/gfx.defaultStroke)
129                        return this.strokeStyle;        // Object
130                },
131                getParent: function(){
132                        // summary:
133                        //              Returns the parent Shape, Group or null if this Shape is unparented.
134                        //              (see dojox/gfx/shape.Surface,
135                        //              or dojox/gfx.Group)
136                        return this.parent;     // Object
137                },
138                getBoundingBox: function(){
139                        // summary:
140                        //              Returns the bounding box Rectangle for this shape or null if a BoundingBox cannot be
141                        //              calculated for the shape on the current renderer or for shapes with no geometric area (points).
142                        //              A bounding box is a rectangular geometric region
143                        //              defining the X and Y extent of the shape.
144                        //              (see dojox/gfx.defaultRect)
145                        //              Note that this method returns a direct reference to the attribute of this instance. Therefore you should
146                        //              not modify its value directly but clone it instead.
147                        return this.bbox;       // dojox/gfx.Rectangle
148                },
149                getTransformedBoundingBox: function(){
150                        // summary:
151                        //              returns an array of four points or null
152                        //              four points represent four corners of the untransformed bounding box
153                        var b = this.getBoundingBox();
154                        if(!b){
155                                return null;    // null
156                        }
157                        var m = this._getRealMatrix(),
158                                gm = matrixLib;
159                        return [        // Array
160                                        gm.multiplyPoint(m, b.x, b.y),
161                                        gm.multiplyPoint(m, b.x + b.width, b.y),
162                                        gm.multiplyPoint(m, b.x + b.width, b.y + b.height),
163                                        gm.multiplyPoint(m, b.x, b.y + b.height)
164                                ];
165                },
166                getEventSource: function(){
167                        // summary:
168                        //              returns a Node, which is used as
169                        //              a source of events for this shape
170                       
171                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
172                        return this.rawNode;    // Node
173                },
174       
175                // empty settings
176               
177                setClip: function(clip){
178                        // summary:
179                        //              sets the clipping area of this shape.
180                        // description:
181                        //              The clipping area defines the shape area that will be effectively visible. Everything that
182                        //              would be drawn outside of the clipping area will not be rendered.
183                        //              The possible clipping area types are rectangle, ellipse, polyline and path, but all are not
184                        //              supported by all the renderers. vml only supports rectangle clipping, while the gfx silverlight renderer does not
185                        //              support path clipping.
186                        //              The clip parameter defines the clipping area geometry, and should be an object with the following properties:
187                        //
188                        //              - {x:Number, y:Number, width:Number, height:Number} for rectangular clip
189                        //              - {cx:Number, cy:Number, rx:Number, ry:Number} for ellipse clip
190                        //              - {points:Array} for polyline clip
191                        //              - {d:String} for a path clip.
192                        //
193                        //              The clip geometry coordinates are expressed in the coordinate system used to draw the shape. In other
194                        //              words, the clipping area is defined in the shape parent coordinate system and the shape transform is automatically applied.
195                        // example:
196                        //              The following example shows how to clip a gfx image with all the possible clip geometry: a rectangle,
197                        //              an ellipse, a circle (using the ellipse geometry), a polyline and a path:
198                        //
199                        //      |       surface.createImage({src:img, width:200,height:200}).setClip({x:10,y:10,width:50,height:50});
200                        //      |       surface.createImage({src:img, x:100,y:50,width:200,height:200}).setClip({cx:200,cy:100,rx:20,ry:30});
201                        //      |       surface.createImage({src:img, x:0,y:350,width:200,height:200}).setClip({cx:100,cy:425,rx:60,ry:60});
202                        //      |       surface.createImage({src:img, x:300,y:0,width:200,height:200}).setClip({points:[350,0,450,50,380,130,300,110]});
203                        //      |       surface.createImage({src:img, x:300,y:350,width:200,height:200}).setClip({d:"M 350,350 C314,414 317,557 373,450.0000 z"});
204
205                        // clip: Object
206                        //              an object that defines the clipping geometry, or null to remove clip.
207                       
208                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
209                        this.clip = clip;
210                },
211               
212                getClip: function(){
213                        return this.clip;
214                },
215       
216                setShape: function(shape){
217                        // summary:
218                        //              sets a shape object
219                        //              (the default implementation simply ignores it)
220                        // shape: Object
221                        //              a shape object
222                        //              (see dojox/gfx.defaultPath,
223                        //              dojox/gfx.defaultPolyline,
224                        //              dojox/gfx.defaultRect,
225                        //              dojox/gfx.defaultEllipse,
226                        //              dojox/gfx.defaultCircle,
227                        //              dojox/gfx.defaultLine,
228                        //              or dojox/gfx.defaultImage)
229                       
230                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
231                        this.shape = g.makeParameters(this.shape, shape);
232                        this.bbox = null;
233                        return this;    // self
234                },
235                setFill: function(fill){
236                        // summary:
237                        //              sets a fill object
238                        //              (the default implementation simply ignores it)
239                        // fill: Object
240                        //              a fill object
241                        //              (see dojox/gfx.defaultLinearGradient,
242                        //              dojox/gfx.defaultRadialGradient,
243                        //              dojox/gfx.defaultPattern,
244                        //              or dojo/_base/Color)
245                       
246                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
247                        if(!fill){
248                                // don't fill
249                                this.fillStyle = null;
250                                return this;    // self
251                        }
252                        var f = null;
253                        if(typeof(fill) == "object" && "type" in fill){
254                                // gradient or pattern
255                                switch(fill.type){
256                                        case "linear":
257                                                f = g.makeParameters(g.defaultLinearGradient, fill);
258                                                break;
259                                        case "radial":
260                                                f = g.makeParameters(g.defaultRadialGradient, fill);
261                                                break;
262                                        case "pattern":
263                                                f = g.makeParameters(g.defaultPattern, fill);
264                                                break;
265                                }
266                        }else{
267                                // color object
268                                f = g.normalizeColor(fill);
269                        }
270                        this.fillStyle = f;
271                        return this;    // self
272                },
273                setStroke: function(stroke){
274                        // summary:
275                        //              sets a stroke object
276                        //              (the default implementation simply ignores it)
277                        // stroke: Object
278                        //              a stroke object
279                        //              (see dojox/gfx.defaultStroke)
280                       
281                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
282                        if(!stroke){
283                                // don't stroke
284                                this.strokeStyle = null;
285                                return this;    // self
286                        }
287                        // normalize the stroke
288                        if(typeof stroke == "string" || lang.isArray(stroke) || stroke instanceof Color){
289                                stroke = {color: stroke};
290                        }
291                        var s = this.strokeStyle = g.makeParameters(g.defaultStroke, stroke);
292                        s.color = g.normalizeColor(s.color);
293                        return this;    // self
294                },
295                setTransform: function(matrix){
296                        // summary:
297                        //              sets a transformation matrix
298                        // matrix: dojox/gfx/matrix.Matrix2D
299                        //              a matrix or a matrix-like object
300                        //              (see an argument of dojox/gfx/matrix.Matrix2D
301                        //              constructor for a list of acceptable arguments)
302                       
303                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
304                        this.matrix = matrixLib.clone(matrix ? matrixLib.normalize(matrix) : matrixLib.identity);
305                        return this._applyTransform();  // self
306                },
307       
308                _applyTransform: function(){
309                        // summary:
310                        //              physically sets a matrix
311                       
312                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
313                        return this;    // self
314                },
315       
316                // z-index
317       
318                moveToFront: function(){
319                        // summary:
320                        //              moves a shape to front of its parent's list of shapes
321                        var p = this.getParent();
322                        if(p){
323                                p._moveChildToFront(this);
324                                this._moveToFront();    // execute renderer-specific action
325                        }
326                        return this;    // self
327                },
328                moveToBack: function(){
329                        // summary:
330                        //              moves a shape to back of its parent's list of shapes
331                        var p = this.getParent();
332                        if(p){
333                                p._moveChildToBack(this);
334                                this._moveToBack();     // execute renderer-specific action
335                        }
336                        return this;
337                },
338                _moveToFront: function(){
339                        // summary:
340                        //              renderer-specific hook, see dojox/gfx/shape.Shape.moveToFront()
341                       
342                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
343                },
344                _moveToBack: function(){
345                        // summary:
346                        //              renderer-specific hook, see dojox/gfx/shape.Shape.moveToFront()
347                       
348                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
349                },
350       
351                // apply left & right transformation
352       
353                applyRightTransform: function(matrix){
354                        // summary:
355                        //              multiplies the existing matrix with an argument on right side
356                        //              (this.matrix * matrix)
357                        // matrix: dojox/gfx/matrix.Matrix2D
358                        //              a matrix or a matrix-like object
359                        //              (see an argument of dojox/gfx/matrix.Matrix2D
360                        //              constructor for a list of acceptable arguments)
361                        return matrix ? this.setTransform([this.matrix, matrix]) : this;        // self
362                },
363                applyLeftTransform: function(matrix){
364                        // summary:
365                        //              multiplies the existing matrix with an argument on left side
366                        //              (matrix * this.matrix)
367                        // matrix: dojox/gfx/matrix.Matrix2D
368                        //              a matrix or a matrix-like object
369                        //              (see an argument of dojox/gfx/matrix.Matrix2D
370                        //              constructor for a list of acceptable arguments)
371                        return matrix ? this.setTransform([matrix, this.matrix]) : this;        // self
372                },
373                applyTransform: function(matrix){
374                        // summary:
375                        //              a shortcut for dojox/gfx/shape.Shape.applyRightTransform
376                        // matrix: dojox/gfx/matrix.Matrix2D
377                        //              a matrix or a matrix-like object
378                        //              (see an argument of dojox/gfx/matrix.Matrix2D
379                        //              constructor for a list of acceptable arguments)
380                        return matrix ? this.setTransform([this.matrix, matrix]) : this;        // self
381                },
382       
383                // virtual group methods
384       
385                removeShape: function(silently){
386                        // summary:
387                        //              removes the shape from its parent's list of shapes
388                        // silently: Boolean
389                        //              if true, do not redraw a picture yet
390                        if(this.parent){
391                                this.parent.remove(this, silently);
392                        }
393                        return this;    // self
394                },
395                _setParent: function(parent, matrix){
396                        // summary:
397                        //              sets a parent
398                        // parent: Object
399                        //              a parent or null
400                        //              (see dojox/gfx/shape.Surface,
401                        //              or dojox/gfx.Group)
402                        // matrix: dojox/gfx/matrix.Matrix2D
403                        //              a 2D matrix or a matrix-like object
404                        this.parent = parent;
405                        return this._updateParentMatrix(matrix);        // self
406                },
407                _updateParentMatrix: function(matrix){
408                        // summary:
409                        //              updates the parent matrix with new matrix
410                        // matrix: dojox/gfx/Matrix2D
411                        //              a 2D matrix or a matrix-like object
412                        this.parentMatrix = matrix ? matrixLib.clone(matrix) : null;
413                        return this._applyTransform();  // self
414                },
415                _getRealMatrix: function(){
416                        // summary:
417                        //              returns the cumulative ('real') transformation matrix
418                        //              by combining the shape's matrix with its parent's matrix
419                        var m = this.matrix;
420                        var p = this.parent;
421                        while(p){
422                                if(p.matrix){
423                                        m = matrixLib.multiply(p.matrix, m);
424                                }
425                                p = p.parent;
426                        }
427                        return m;       // dojox/gfx/matrix.Matrix2D
428                }
429        });
430       
431        shape._eventsProcessing = {
432                on: function(type, listener){
433                        //      summary:
434                        //              Connects an event to this shape.
435
436                        return on(this.getEventSource(), type, shape.fixCallback(this, g.fixTarget, listener));
437                },
438
439                connect: function(name, object, method){
440                        // summary:
441                        //              connects a handler to an event on this shape
442                       
443                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
444                        // redirect to fixCallback to normalize events and add the gfxTarget to the event. The latter
445                        // is done by dojox/gfx.fixTarget which is defined by each renderer
446                        if(name.substring(0, 2) == "on"){
447                                name = name.substring(2);
448                        }
449                        return this.on(name, method ? lang.hitch(object, method) : object);
450                },
451
452                disconnect: function(token){
453                        // summary:
454                        //              connects a handler by token from an event on this shape
455                       
456                        // COULD BE RE-IMPLEMENTED BY THE RENDERER!
457       
458                        return token.remove();
459                }
460        };
461       
462        shape.fixCallback = function(gfxElement, fixFunction, scope, method){
463                // summary:
464                //              Wraps the callback to allow for tests and event normalization
465                //              before it gets invoked. This is where 'fixTarget' is invoked.
466                // tags:
467                //      private
468                // gfxElement: Object
469                //              The GFX object that triggers the action (ex.:
470                //              dojox/gfx.Surface and dojox/gfx/shape.Shape). A new event property
471                //              'gfxTarget' is added to the event to reference this object.
472                //              for easy manipulation of GFX objects by the event handlers.
473                // fixFunction: Function
474                //              The function that implements the logic to set the 'gfxTarget'
475                //              property to the event. It should be 'dojox/gfx.fixTarget' for
476                //              most of the cases
477                // scope: Object
478                //              Optional. The scope to be used when invoking 'method'. If
479                //              omitted, a global scope is used.
480                // method: Function|String
481                //              The original callback to be invoked.
482                if(!method){
483                        method = scope;
484                        scope = null;
485                }
486                if(lang.isString(method)){
487                        scope = scope || kernel.global;
488                        if(!scope[method]){ throw(['dojox.gfx.shape.fixCallback: scope["', method, '"] is null (scope="', scope, '")'].join('')); }
489                        return function(e){ 
490                                return fixFunction(e,gfxElement) ? scope[method].apply(scope, arguments || []) : undefined; }; // Function
491                }
492                return !scope
493                        ? function(e){
494                                return fixFunction(e,gfxElement) ? method.apply(scope, arguments) : undefined; }
495                        : function(e){
496                                return fixFunction(e,gfxElement) ? method.apply(scope, arguments || []) : undefined; }; // Function
497        };
498        lang.extend(shape.Shape, shape._eventsProcessing);
499       
500        shape.Container = {
501                // summary:
502                //              a container of shapes, which can be used
503                //              as a foundation for renderer-specific groups, or as a way
504                //              to logically group shapes (e.g, to propagate matricies)
505       
506                _init: function() {
507                        // children: Array
508                        //              a list of children
509                        this.children = [];
510                        this._batch = 0;
511                },
512       
513                // group management
514       
515                openBatch: function() {
516                        // summary:
517                        //              starts a new batch, subsequent new child shapes will be held in
518                        //              the batch instead of appending to the container directly.
519                        // description:
520                        //              Because the canvas renderer has no DOM hierarchy, the canvas implementation differs
521                        //              such that it suspends the repaint requests for this container until the current batch is closed by a call to closeBatch().
522                        return this;
523                },
524                closeBatch: function() {
525                        // summary:
526                        //              submits the current batch, append all pending child shapes to DOM
527                        // description:
528                        //              On canvas, this method flushes the pending redraws queue.
529                        return this;
530                },
531                add: function(shape){
532                        // summary:
533                        //              adds a shape to the list
534                        // shape: dojox/gfx/shape.Shape
535                        //              the shape to add to the list
536                        var oldParent = shape.getParent();
537                        if(oldParent){
538                                oldParent.remove(shape, true);
539                        }
540                        this.children.push(shape);
541                        return shape._setParent(this, this._getRealMatrix());   // self
542                },
543                remove: function(shape, silently){
544                        // summary:
545                        //              removes a shape from the list
546                        // shape: dojox/gfx/shape.Shape
547                        //              the shape to remove
548                        // silently: Boolean
549                        //              if true, do not redraw a picture yet
550                        for(var i = 0; i < this.children.length; ++i){
551                                if(this.children[i] == shape){
552                                        if(silently){
553                                                // skip for now
554                                        }else{
555                                                shape.parent = null;
556                                                shape.parentMatrix = null;
557                                        }
558                                        this.children.splice(i, 1);
559                                        break;
560                                }
561                        }
562                        return this;    // self
563                },
564                clear: function(/*Boolean?*/ destroy){
565                        // summary:
566                        //              removes all shapes from a group/surface.
567                        // destroy: Boolean
568                        //              Indicates whether the children should be destroyed. Optional.
569                        var shape;
570                        for(var i = 0; i < this.children.length;++i){
571                                shape = this.children[i];
572                                shape.parent = null;
573                                shape.parentMatrix = null;
574                                if(destroy){
575                                        shape.destroy();
576                                }
577                        }
578                        this.children = [];
579                        return this;    // self
580                },
581                getBoundingBox: function(){
582                        // summary:
583                        //              Returns the bounding box Rectangle for this shape.
584                        if(this.children){
585                                // if this is a composite shape, then sum up all the children
586                                var result = null;
587                                arr.forEach(this.children, function(shape){
588                                        var bb = shape.getBoundingBox();
589                                        if(bb){
590                                                var ct = shape.getTransform();
591                                                if(ct){
592                                                        bb = matrixLib.multiplyRectangle(ct, bb);
593                                                }
594                                                if(result){
595                                                        // merge two bbox
596                                                        result.x = Math.min(result.x, bb.x);
597                                                        result.y = Math.min(result.y, bb.y);
598                                                        result.endX = Math.max(result.endX, bb.x + bb.width);
599                                                        result.endY = Math.max(result.endY, bb.y + bb.height);
600                                                }else{
601                                                        // first bbox
602                                                        result = {
603                                                                x: bb.x,
604                                                                y: bb.y,
605                                                                endX: bb.x + bb.width,
606                                                                endY: bb.y + bb.height
607                                                        };
608                                                }
609                                        }
610                                });
611                                if(result){
612                                        result.width = result.endX - result.x;
613                                        result.height = result.endY - result.y;
614                                }
615                                return result; // dojox/gfx.Rectangle
616                        }
617                        // unknown/empty bounding box, subclass shall override this impl
618                        return null;
619                },
620                // moving child nodes
621                _moveChildToFront: function(shape){
622                        // summary:
623                        //              moves a shape to front of the list of shapes
624                        // shape: dojox/gfx/shape.Shape
625                        //              one of the child shapes to move to the front
626                        for(var i = 0; i < this.children.length; ++i){
627                                if(this.children[i] == shape){
628                                        this.children.splice(i, 1);
629                                        this.children.push(shape);
630                                        break;
631                                }
632                        }
633                        return this;    // self
634                },
635                _moveChildToBack: function(shape){
636                        // summary:
637                        //              moves a shape to back of the list of shapes
638                        // shape: dojox/gfx/shape.Shape
639                        //              one of the child shapes to move to the front
640                        for(var i = 0; i < this.children.length; ++i){
641                                if(this.children[i] == shape){
642                                        this.children.splice(i, 1);
643                                        this.children.unshift(shape);
644                                        break;
645                                }
646                        }
647                        return this;    // self
648                }
649        };
650
651        shape.Surface = declare("dojox.gfx.shape.Surface", null, {
652                // summary:
653                //              a surface object to be used for drawings
654                constructor: function(){
655                        // underlying node
656                        this.rawNode = null;
657                        // the parent node
658                        this._parent = null;
659                        // the list of DOM nodes to be deleted in the case of destruction
660                        this._nodes = [];
661                        // the list of events to be detached in the case of destruction
662                        this._events = [];
663                },
664                destroy: function(){
665                        // summary:
666                        //              destroy all relevant external resources and release all
667                        //              external references to make this object garbage-collectible
668                        arr.forEach(this._nodes, domConstruct.destroy);
669                        this._nodes = [];
670                        arr.forEach(this._events, function(h){ if(h){ h.remove(); } });
671                        this._events = [];
672                        this.rawNode = null;    // recycle it in _nodes, if it needs to be recycled
673                        if(has("ie")){
674                                while(this._parent.lastChild){
675                                        domConstruct.destroy(this._parent.lastChild);
676                                }
677                        }else{
678                                this._parent.innerHTML = "";
679                        }
680                        this._parent = null;
681                },
682                getEventSource: function(){
683                        // summary:
684                        //              returns a node, which can be used to attach event listeners
685                        return this.rawNode; // Node
686                },
687                _getRealMatrix: function(){
688                        // summary:
689                        //              always returns the identity matrix
690                        return null;    // dojox/gfx/Matrix2D
691                },
692                /*=====
693                 setDimensions: function(width, height){
694                         // summary:
695                         //             sets the width and height of the rawNode
696                         // width: String
697                         //             width of surface, e.g., "100px"
698                         // height: String
699                         //             height of surface, e.g., "100px"
700                         return this;   // self
701                 },
702                 getDimensions: function(){
703                         // summary:
704                         //     gets current width and height in pixels
705                         // returns: Object
706                         //     object with properties "width" and "height"
707                 },
708                 =====*/
709                isLoaded: true,
710                onLoad: function(/*dojox/gfx/shape.Surface*/ surface){
711                        // summary:
712                        //              local event, fired once when the surface is created
713                        //              asynchronously, used only when isLoaded is false, required
714                        //              only for Silverlight.
715                },
716                whenLoaded: function(/*Object|Null*/ context, /*Function|String*/ method){
717                        var f = lang.hitch(context, method);
718                        if(this.isLoaded){
719                                f(this);
720                        }else{
721                                on.once(this, "load", function(surface){
722                                        f(surface);
723                                });
724                        }
725                }
726        });
727        lang.extend(shape.Surface, shape._eventsProcessing);
728
729        /*=====
730        g.Point = declare("dojox/gfx.Point", null, {
731                // summary:
732                //              2D point for drawings - {x, y}
733                // description:
734                //              Do not use this object directly!
735                //              Use the naked object instead: {x: 1, y: 2}.
736        });
737
738        g.Rectangle = declare("dojox.gfx.Rectangle", null, {
739                // summary:
740                //              rectangle - {x, y, width, height}
741                // description:
742                //              Do not use this object directly!
743                //              Use the naked object instead: {x: 1, y: 2, width: 100, height: 200}.
744        });
745         =====*/
746
747
748        shape.Rect = declare("dojox.gfx.shape.Rect", shape.Shape, {
749                // summary:
750                //              a generic rectangle
751                constructor: function(rawNode){
752                        // rawNode: Node
753                        //              The underlying graphics system object (typically a DOM Node)
754                        this.shape = g.getDefault("Rect");
755                        this.rawNode = rawNode;
756                },
757                getBoundingBox: function(){
758                        // summary:
759                        //              returns the bounding box (its shape in this case)
760                        return this.shape;      // dojox/gfx.Rectangle
761                }
762        });
763       
764        shape.Ellipse = declare("dojox.gfx.shape.Ellipse", shape.Shape, {
765                // summary:
766                //              a generic ellipse
767                constructor: function(rawNode){
768                        // rawNode: Node
769                        //              a DOM Node
770                        this.shape = g.getDefault("Ellipse");
771                        this.rawNode = rawNode;
772                },
773                getBoundingBox: function(){
774                        // summary:
775                        //              returns the bounding box
776                        if(!this.bbox){
777                                var shape = this.shape;
778                                this.bbox = {x: shape.cx - shape.rx, y: shape.cy - shape.ry,
779                                        width: 2 * shape.rx, height: 2 * shape.ry};
780                        }
781                        return this.bbox;       // dojox/gfx.Rectangle
782                }
783        });
784       
785        shape.Circle = declare("dojox.gfx.shape.Circle", shape.Shape, {
786                // summary:
787                //              a generic circle
788                constructor: function(rawNode){
789                        // rawNode: Node
790                        //              a DOM Node
791                        this.shape = g.getDefault("Circle");
792                        this.rawNode = rawNode;
793                },
794                getBoundingBox: function(){
795                        // summary:
796                        //              returns the bounding box
797                        if(!this.bbox){
798                                var shape = this.shape;
799                                this.bbox = {x: shape.cx - shape.r, y: shape.cy - shape.r,
800                                        width: 2 * shape.r, height: 2 * shape.r};
801                        }
802                        return this.bbox;       // dojox/gfx.Rectangle
803                }
804        });
805       
806        shape.Line = declare("dojox.gfx.shape.Line", shape.Shape, {
807                // summary:
808                //              a generic line (do not instantiate it directly)
809                constructor: function(rawNode){
810                        // rawNode: Node
811                        //              a DOM Node
812                        this.shape = g.getDefault("Line");
813                        this.rawNode = rawNode;
814                },
815                getBoundingBox: function(){
816                        // summary:
817                        //              returns the bounding box
818                        if(!this.bbox){
819                                var shape = this.shape;
820                                this.bbox = {
821                                        x:              Math.min(shape.x1, shape.x2),
822                                        y:              Math.min(shape.y1, shape.y2),
823                                        width:  Math.abs(shape.x2 - shape.x1),
824                                        height: Math.abs(shape.y2 - shape.y1)
825                                };
826                        }
827                        return this.bbox;       // dojox/gfx.Rectangle
828                }
829        });
830       
831        shape.Polyline = declare("dojox.gfx.shape.Polyline", shape.Shape, {
832                // summary:
833                //              a generic polyline/polygon (do not instantiate it directly)
834                constructor: function(rawNode){
835                        // rawNode: Node
836                        //              a DOM Node
837                        this.shape = g.getDefault("Polyline");
838                        this.rawNode = rawNode;
839                },
840                setShape: function(points, closed){
841                        // summary:
842                        //              sets a polyline/polygon shape object
843                        // points: Object|Array
844                        //              a polyline/polygon shape object, or an array of points
845                        // closed: Boolean
846                        //              close the polyline to make a polygon
847                        if(points && points instanceof Array){
848                                this.inherited(arguments, [{points: points}]);
849                                if(closed && this.shape.points.length){
850                                        this.shape.points.push(this.shape.points[0]);
851                                }
852                        }else{
853                                this.inherited(arguments, [points]);
854                        }
855                        return this;    // self
856                },
857                _normalizePoints: function(){
858                        // summary:
859                        //              normalize points to array of {x:number, y:number}
860                        var p = this.shape.points, l = p && p.length;
861                        if(l && typeof p[0] == "number"){
862                                var points = [];
863                                for(var i = 0; i < l; i += 2){
864                                        points.push({x: p[i], y: p[i + 1]});
865                                }
866                                this.shape.points = points;
867                        }
868                },
869                getBoundingBox: function(){
870                        // summary:
871                        //              returns the bounding box
872                        if(!this.bbox && this.shape.points.length){
873                                var p = this.shape.points;
874                                var l = p.length;
875                                var t = p[0];
876                                var bbox = {l: t.x, t: t.y, r: t.x, b: t.y};
877                                for(var i = 1; i < l; ++i){
878                                        t = p[i];
879                                        if(bbox.l > t.x) bbox.l = t.x;
880                                        if(bbox.r < t.x) bbox.r = t.x;
881                                        if(bbox.t > t.y) bbox.t = t.y;
882                                        if(bbox.b < t.y) bbox.b = t.y;
883                                }
884                                this.bbox = {
885                                        x:              bbox.l,
886                                        y:              bbox.t,
887                                        width:  bbox.r - bbox.l,
888                                        height: bbox.b - bbox.t
889                                };
890                        }
891                        return this.bbox;       // dojox/gfx.Rectangle
892                }
893        });
894       
895        shape.Image = declare("dojox.gfx.shape.Image", shape.Shape, {
896                // summary:
897                //              a generic image (do not instantiate it directly)
898                constructor: function(rawNode){
899                        // rawNode: Node
900                        //              a DOM Node
901                        this.shape = g.getDefault("Image");
902                        this.rawNode = rawNode;
903                },
904                getBoundingBox: function(){
905                        // summary:
906                        //              returns the bounding box (its shape in this case)
907                        return this.shape;      // dojox/gfx.Rectangle
908                },
909                setStroke: function(){
910                        // summary:
911                        //              ignore setting a stroke style
912                        return this;    // self
913                },
914                setFill: function(){
915                        // summary:
916                        //              ignore setting a fill style
917                        return this;    // self
918                }
919        });
920       
921        shape.Text = declare(shape.Shape, {
922                // summary:
923                //              a generic text (do not instantiate it directly)
924                constructor: function(rawNode){
925                        // rawNode: Node
926                        //              a DOM Node
927                        this.fontStyle = null;
928                        this.shape = g.getDefault("Text");
929                        this.rawNode = rawNode;
930                },
931                getFont: function(){
932                        // summary:
933                        //              returns the current font object or null
934                        return this.fontStyle;  // Object
935                },
936                setFont: function(newFont){
937                        // summary:
938                        //              sets a font for text
939                        // newFont: Object
940                        //              a font object (see dojox/gfx.defaultFont) or a font string
941                        this.fontStyle = typeof newFont == "string" ? g.splitFontString(newFont) :
942                                g.makeParameters(g.defaultFont, newFont);
943                        this._setFont();
944                        return this;    // self
945                },
946                getBoundingBox: function(){
947                        var bbox = null, s = this.getShape();
948                        if(s.text){
949                                bbox = g._base._computeTextBoundingBox(this);
950                        }
951                        return bbox;
952                }
953        });
954       
955        shape.Creator = {
956                // summary:
957                //              shape creators
958                createShape: function(shape){
959                        // summary:
960                        //              creates a shape object based on its type; it is meant to be used
961                        //              by group-like objects
962                        // shape: Object
963                        //              a shape descriptor object
964                        // returns: dojox/gfx/shape.Shape | Null
965                        //      a fully instantiated surface-specific Shape object
966                        switch(shape.type){
967                                case g.defaultPath.type:                return this.createPath(shape);
968                                case g.defaultRect.type:                return this.createRect(shape);
969                                case g.defaultCircle.type:          return this.createCircle(shape);
970                                case g.defaultEllipse.type:         return this.createEllipse(shape);
971                                case g.defaultLine.type:                return this.createLine(shape);
972                                case g.defaultPolyline.type:    return this.createPolyline(shape);
973                                case g.defaultImage.type:               return this.createImage(shape);
974                                case g.defaultText.type:                return this.createText(shape);
975                                case g.defaultTextPath.type:    return this.createTextPath(shape);
976                        }
977                        return null;
978                },
979                createGroup: function(){
980                        // summary:
981                        //              creates a group shape
982                        return this.createObject(g.Group);      // dojox/gfx/Group
983                },
984                createRect: function(rect){
985                        // summary:
986                        //              creates a rectangle shape
987                        // rect: Object
988                        //              a path object (see dojox/gfx.defaultRect)
989                        return this.createObject(g.Rect, rect); // dojox/gfx/shape.Rect
990                },
991                createEllipse: function(ellipse){
992                        // summary:
993                        //              creates an ellipse shape
994                        // ellipse: Object
995                        //              an ellipse object (see dojox/gfx.defaultEllipse)
996                        return this.createObject(g.Ellipse, ellipse);   // dojox/gfx/shape.Ellipse
997                },
998                createCircle: function(circle){
999                        // summary:
1000                        //              creates a circle shape
1001                        // circle: Object
1002                        //              a circle object (see dojox/gfx.defaultCircle)
1003                        return this.createObject(g.Circle, circle);     // dojox/gfx/shape.Circle
1004                },
1005                createLine: function(line){
1006                        // summary:
1007                        //              creates a line shape
1008                        // line: Object
1009                        //              a line object (see dojox/gfx.defaultLine)
1010                        return this.createObject(g.Line, line); // dojox/gfx/shape.Line
1011                },
1012                createPolyline: function(points){
1013                        // summary:
1014                        //              creates a polyline/polygon shape
1015                        // points: Object
1016                        //              a points object (see dojox/gfx.defaultPolyline)
1017                        //              or an Array of points
1018                        return this.createObject(g.Polyline, points);   // dojox/gfx/shape.Polyline
1019                },
1020                createImage: function(image){
1021                        // summary:
1022                        //              creates a image shape
1023                        // image: Object
1024                        //              an image object (see dojox/gfx.defaultImage)
1025                        return this.createObject(g.Image, image);       // dojox/gfx/shape.Image
1026                },
1027                createText: function(text){
1028                        // summary:
1029                        //              creates a text shape
1030                        // text: Object
1031                        //              a text object (see dojox/gfx.defaultText)
1032                        return this.createObject(g.Text, text); // dojox/gfx/shape.Text
1033                },
1034                createPath: function(path){
1035                        // summary:
1036                        //              creates a path shape
1037                        // path: Object
1038                        //              a path object (see dojox/gfx.defaultPath)
1039                        return this.createObject(g.Path, path); // dojox/gfx/shape.Path
1040                },
1041                createTextPath: function(text){
1042                        // summary:
1043                        //              creates a text shape
1044                        // text: Object
1045                        //              a textpath object (see dojox/gfx.defaultTextPath)
1046                        return this.createObject(g.TextPath, {}).setText(text); // dojox/gfx/shape.TextPath
1047                },
1048                createObject: function(shapeType, rawShape){
1049                        // summary:
1050                        //              creates an instance of the passed shapeType class
1051                        // shapeType: Function
1052                        //              a class constructor to create an instance of
1053                        // rawShape: Object
1054                        //              properties to be passed in to the classes 'setShape' method
1055       
1056                        // SHOULD BE RE-IMPLEMENTED BY THE RENDERER!
1057                        return null;    // dojox/gfx/shape.Shape
1058                }
1059        };
1060       
1061        /*=====
1062         lang.extend(shape.Surface, shape.Container);
1063         lang.extend(shape.Surface, shape.Creator);
1064
1065         g.Group = declare(shape.Shape, {
1066                // summary:
1067                //              a group shape, which can be used
1068                //              to logically group shapes (e.g, to propagate matricies)
1069        });
1070        lang.extend(g.Group, shape.Container);
1071        lang.extend(g.Group, shape.Creator);
1072
1073        g.Rect     = shape.Rect;
1074        g.Circle   = shape.Circle;
1075        g.Ellipse  = shape.Ellipse;
1076        g.Line     = shape.Line;
1077        g.Polyline = shape.Polyline;
1078        g.Text     = shape.Text;
1079        g.Surface  = shape.Surface;
1080        =====*/
1081
1082        return shape;
1083});
Note: See TracBrowser for help on using the repository browser.