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