Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / draw / Component.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.draw.Component
17  * @extends Ext.Component
18  *
19  * The Draw Component is a surface in which sprites can be rendered. The Draw Component
20  * manages and holds a `Surface` instance: an interface that has
21  * an SVG or VML implementation depending on the browser capabilities and where
22  * Sprites can be appended.
23  * {@img Ext.draw.Component/Ext.draw.Component.png Ext.draw.Component component}
24  * One way to create a draw component is:
25  * 
26  *     var drawComponent = Ext.create('Ext.draw.Component', {
27  *         viewBox: false,
28  *         items: [{
29  *             type: 'circle',
30  *             fill: '#79BB3F',
31  *             radius: 100,
32  *             x: 100,
33  *             y: 100
34  *         }]
35  *     });
36  *   
37  *     Ext.create('Ext.Window', {
38  *         width: 215,
39  *         height: 235,
40  *         layout: 'fit',
41  *         items: [drawComponent]
42  *     }).show();
43  * 
44  * In this case we created a draw component and added a sprite to it.
45  * The *type* of the sprite is *circle* so if you run this code you'll see a yellow-ish
46  * circle in a Window. When setting `viewBox` to `false` we are responsible for setting the object's position and
47  * dimensions accordingly. 
48  * 
49  * You can also add sprites by using the surface's add method:
50  *    
51  *     drawComponent.surface.add({
52  *         type: 'circle',
53  *         fill: '#79BB3F',
54  *         radius: 100,
55  *         x: 100,
56  *         y: 100
57  *     });
58  *  
59  * For more information on Sprites, the core elements added to a draw component's surface,
60  * refer to the Ext.draw.Sprite documentation.
61  */
62 Ext.define('Ext.draw.Component', {
63
64     /* Begin Definitions */
65
66     alias: 'widget.draw',
67
68     extend: 'Ext.Component',
69
70     requires: [
71         'Ext.draw.Surface',
72         'Ext.layout.component.Draw'
73     ],
74
75     /* End Definitions */
76
77     /**
78      * @cfg {Array} enginePriority
79      * Defines the priority order for which Surface implementation to use. The first
80      * one supported by the current environment will be used.
81      */
82     enginePriority: ['Svg', 'Vml'],
83
84     baseCls: Ext.baseCSSPrefix + 'surface',
85
86     componentLayout: 'draw',
87
88     /**
89      * @cfg {Boolean} viewBox
90      * Turn on view box support which will scale and position items in the draw component to fit to the component while
91      * maintaining aspect ratio. Note that this scaling can override other sizing settings on yor items. Defaults to true.
92      */
93     viewBox: true,
94
95     /**
96      * @cfg {Boolean} autoSize
97      * Turn on autoSize support which will set the bounding div's size to the natural size of the contents. Defaults to false.
98      */
99     autoSize: false,
100     
101     /**
102      * @cfg {Array} gradients (optional) Define a set of gradients that can be used as `fill` property in sprites.
103      * The gradients array is an array of objects with the following properties:
104      *
105      * <ul>
106      * <li><strong>id</strong> - string - The unique name of the gradient.</li>
107      * <li><strong>angle</strong> - number, optional - The angle of the gradient in degrees.</li>
108      * <li><strong>stops</strong> - object - An object with numbers as keys (from 0 to 100) and style objects
109      * as values</li>
110      * </ul>
111      * 
112      
113      For example:
114      
115      <pre><code>
116         gradients: [{
117             id: 'gradientId',
118             angle: 45,
119             stops: {
120                 0: {
121                     color: '#555'
122                 },
123                 100: {
124                     color: '#ddd'
125                 }
126             }
127         },  {
128             id: 'gradientId2',
129             angle: 0,
130             stops: {
131                 0: {
132                     color: '#590'
133                 },
134                 20: {
135                     color: '#599'
136                 },
137                 100: {
138                     color: '#ddd'
139                 }
140             }
141         }]
142      </code></pre>
143      
144      Then the sprites can use `gradientId` and `gradientId2` by setting the fill attributes to those ids, for example:
145      
146      <pre><code>
147         sprite.setAttributes({
148             fill: 'url(#gradientId)'
149         }, true);
150      </code></pre>
151      
152      */
153
154     initComponent: function() {
155         this.callParent(arguments);
156
157         this.addEvents(
158             'mousedown',
159             'mouseup',
160             'mousemove',
161             'mouseenter',
162             'mouseleave',
163             'click'
164         );
165     },
166
167     /**
168      * @private
169      *
170      * Create the Surface on initial render
171      */
172     onRender: function() {
173         var me = this,
174             viewBox = me.viewBox,
175             autoSize = me.autoSize,
176             bbox, items, width, height, x, y;
177         me.callParent(arguments);
178
179         me.createSurface();
180
181         items = me.surface.items;
182
183         if (viewBox || autoSize) {
184             bbox = items.getBBox();
185             width = bbox.width;
186             height = bbox.height;
187             x = bbox.x;
188             y = bbox.y;
189             if (me.viewBox) {
190                 me.surface.setViewBox(x, y, width, height);
191             }
192             else {
193                 // AutoSized
194                 me.autoSizeSurface();
195             }
196         }
197     },
198
199     //@private
200     autoSizeSurface: function() {
201         var me = this,
202             items = me.surface.items,
203             bbox = items.getBBox(),
204             width = bbox.width,
205             height = bbox.height;
206         items.setAttributes({
207             translate: {
208                 x: -bbox.x,
209                 //Opera has a slight offset in the y axis.
210                 y: -bbox.y + (+Ext.isOpera)
211             }
212         }, true);
213         if (me.rendered) {
214             me.setSize(width, height);
215             me.surface.setSize(width, height);
216         }
217         else {
218             me.surface.setSize(width, height);
219         }
220         me.el.setSize(width, height);
221     },
222
223     /**
224      * Create the Surface instance. Resolves the correct Surface implementation to
225      * instantiate based on the 'enginePriority' config. Once the Surface instance is
226      * created you can use the handle to that instance to add sprites. For example:
227      *
228      <pre><code>
229         drawComponent.surface.add(sprite);
230      </code></pre>
231      */
232     createSurface: function() {
233         var surface = Ext.draw.Surface.create(Ext.apply({}, {
234                 width: this.width,
235                 height: this.height,
236                 renderTo: this.el
237             }, this.initialConfig));
238         this.surface = surface;
239
240         function refire(eventName) {
241             return function(e) {
242                 this.fireEvent(eventName, e);
243             };
244         }
245
246         surface.on({
247             scope: this,
248             mouseup: refire('mouseup'),
249             mousedown: refire('mousedown'),
250             mousemove: refire('mousemove'),
251             mouseenter: refire('mouseenter'),
252             mouseleave: refire('mouseleave'),
253             click: refire('click')
254         });
255     },
256
257
258     /**
259      * @private
260      * 
261      * Clean up the Surface instance on component destruction
262      */
263     onDestroy: function() {
264         var surface = this.surface;
265         if (surface) {
266             surface.destroy();
267         }
268         this.callParent(arguments);
269     }
270
271 });
272