Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Surface.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-draw-Surface'>/**
19 </span> * A Surface is an interface to render methods inside a draw {@link Ext.draw.Component}.
20  * A Surface contains methods to render sprites, get bounding boxes of sprites, add
21  * sprites to the canvas, initialize other graphic components, etc. One of the most used
22  * methods for this class is the `add` method, to add Sprites to the surface.
23  *
24  * Most of the Surface methods are abstract and they have a concrete implementation
25  * in VML or SVG engines.
26  *
27  * A Surface instance can be accessed as a property of a draw component. For example:
28  *
29  *     drawComponent.surface.add({
30  *         type: 'circle',
31  *         fill: '#ffc',
32  *         radius: 100,
33  *         x: 100,
34  *         y: 100
35  *     });
36  *
37  * The configuration object passed in the `add` method is the same as described in the {@link Ext.draw.Sprite}
38  * class documentation.
39  *
40  * # Listeners
41  *
42  * You can also add event listeners to the surface using the `Observable` listener syntax. Supported events are:
43  *
44  * - mousedown
45  * - mouseup
46  * - mouseover
47  * - mouseout
48  * - mousemove
49  * - mouseenter
50  * - mouseleave
51  * - click
52  *
53  * For example:
54  *
55  *     drawComponent.surface.on({
56  *        'mousemove': function() {
57  *             console.log('moving the mouse over the surface');
58  *         }
59  *     });
60  *
61  * # Example
62  *
63  *     var drawComponent = Ext.create('Ext.draw.Component', {
64  *         width: 800,
65  *         height: 600,
66  *         renderTo: document.body
67  *     }), surface = drawComponent.surface;
68  *
69  *     surface.add([{
70  *         type: 'circle',
71  *         radius: 10,
72  *         fill: '#f00',
73  *         x: 10,
74  *         y: 10,
75  *         group: 'circles'
76  *     }, {
77  *         type: 'circle',
78  *         radius: 10,
79  *         fill: '#0f0',
80  *         x: 50,
81  *         y: 50,
82  *         group: 'circles'
83  *     }, {
84  *         type: 'circle',
85  *         radius: 10,
86  *         fill: '#00f',
87  *         x: 100,
88  *         y: 100,
89  *         group: 'circles'
90  *     }, {
91  *         type: 'rect',
92  *         width: 20,
93  *         height: 20,
94  *         fill: '#f00',
95  *         x: 10,
96  *         y: 10,
97  *         group: 'rectangles'
98  *     }, {
99  *         type: 'rect',
100  *         width: 20,
101  *         height: 20,
102  *         fill: '#0f0',
103  *         x: 50,
104  *         y: 50,
105  *         group: 'rectangles'
106  *     }, {
107  *         type: 'rect',
108  *         width: 20,
109  *         height: 20,
110  *         fill: '#00f',
111  *         x: 100,
112  *         y: 100,
113  *         group: 'rectangles'
114  *     }]);
115  *
116  *     // Get references to my groups
117  *     circles = surface.getGroup('circles');
118  *     rectangles = surface.getGroup('rectangles');
119  *
120  *     // Animate the circles down
121  *     circles.animate({
122  *         duration: 1000,
123  *         to: {
124  *             translate: {
125  *                 y: 200
126  *             }
127  *         }
128  *     });
129  *
130  *     // Animate the rectangles across
131  *     rectangles.animate({
132  *         duration: 1000,
133  *         to: {
134  *             translate: {
135  *                 x: 200
136  *             }
137  *         }
138  *     });
139  */
140 Ext.define('Ext.draw.Surface', {
141
142     /* Begin Definitions */
143
144     mixins: {
145         observable: 'Ext.util.Observable'
146     },
147
148     requires: ['Ext.draw.CompositeSprite'],
149     uses: ['Ext.draw.engine.Svg', 'Ext.draw.engine.Vml'],
150
151     separatorRe: /[, ]+/,
152
153     statics: {
154 <span id='Ext-draw-Surface-static-method-create'>        /**
155 </span>         * Creates and returns a new concrete Surface instance appropriate for the current environment.
156          * @param {Object} config Initial configuration for the Surface instance
157          * @param {String[]} enginePriority (Optional) order of implementations to use; the first one that is
158          * available in the current environment will be used. Defaults to `['Svg', 'Vml']`.
159          * @return {Object} The created Surface or false.
160          * @static
161          */
162         create: function(config, enginePriority) {
163             enginePriority = enginePriority || ['Svg', 'Vml'];
164
165             var i = 0,
166                 len = enginePriority.length,
167                 surfaceClass;
168
169             for (; i &lt; len; i++) {
170                 if (Ext.supports[enginePriority[i]]) {
171                     return Ext.create('Ext.draw.engine.' + enginePriority[i], config);
172                 }
173             }
174             return false;
175         }
176     },
177
178     /* End Definitions */
179
180     // @private
181     availableAttrs: {
182         blur: 0,
183         &quot;clip-rect&quot;: &quot;0 0 1e9 1e9&quot;,
184         cursor: &quot;default&quot;,
185         cx: 0,
186         cy: 0,
187         'dominant-baseline': 'auto',
188         fill: &quot;none&quot;,
189         &quot;fill-opacity&quot;: 1,
190         font: '10px &quot;Arial&quot;',
191         &quot;font-family&quot;: '&quot;Arial&quot;',
192         &quot;font-size&quot;: &quot;10&quot;,
193         &quot;font-style&quot;: &quot;normal&quot;,
194         &quot;font-weight&quot;: 400,
195         gradient: &quot;&quot;,
196         height: 0,
197         hidden: false,
198         href: &quot;http://sencha.com/&quot;,
199         opacity: 1,
200         path: &quot;M0,0&quot;,
201         radius: 0,
202         rx: 0,
203         ry: 0,
204         scale: &quot;1 1&quot;,
205         src: &quot;&quot;,
206         stroke: &quot;#000&quot;,
207         &quot;stroke-dasharray&quot;: &quot;&quot;,
208         &quot;stroke-linecap&quot;: &quot;butt&quot;,
209         &quot;stroke-linejoin&quot;: &quot;butt&quot;,
210         &quot;stroke-miterlimit&quot;: 0,
211         &quot;stroke-opacity&quot;: 1,
212         &quot;stroke-width&quot;: 1,
213         target: &quot;_blank&quot;,
214         text: &quot;&quot;,
215         &quot;text-anchor&quot;: &quot;middle&quot;,
216         title: &quot;Ext Draw&quot;,
217         width: 0,
218         x: 0,
219         y: 0,
220         zIndex: 0
221     },
222
223 <span id='Ext-draw-Surface-cfg-height'>    /**
224 </span>     * @cfg {Number} height
225      * The height of this component in pixels (defaults to auto).
226      */
227 <span id='Ext-draw-Surface-cfg-width'>    /**
228 </span>     * @cfg {Number} width
229      * The width of this component in pixels (defaults to auto).
230      */
231
232     container: undefined,
233     height: 352,
234     width: 512,
235     x: 0,
236     y: 0,
237
238 <span id='Ext-draw-Surface-property-orderSpritesByZIndex'>    /**
239 </span>     * @private Flag indicating that the surface implementation requires sprites to be maintained
240      * in order of their zIndex. Impls that don't require this can set it to false.
241      */
242     orderSpritesByZIndex: true,
243
244
245 <span id='Ext-draw-Surface-method-constructor'>    /**
246 </span>     * Creates new Surface.
247      * @param {Object} config (optional) Config object.
248      */
249     constructor: function(config) {
250         var me = this;
251         config = config || {};
252         Ext.apply(me, config);
253
254         me.domRef = Ext.getDoc().dom;
255
256         me.customAttributes = {};
257
258         me.addEvents(
259             'mousedown',
260             'mouseup',
261             'mouseover',
262             'mouseout',
263             'mousemove',
264             'mouseenter',
265             'mouseleave',
266             'click'
267         );
268
269         me.mixins.observable.constructor.call(me);
270
271         me.getId();
272         me.initGradients();
273         me.initItems();
274         if (me.renderTo) {
275             me.render(me.renderTo);
276             delete me.renderTo;
277         }
278         me.initBackground(config.background);
279     },
280
281     // @private called to initialize components in the surface
282     // this is dependent on the underlying implementation.
283     initSurface: Ext.emptyFn,
284
285     // @private called to setup the surface to render an item
286     //this is dependent on the underlying implementation.
287     renderItem: Ext.emptyFn,
288
289     // @private
290     renderItems: Ext.emptyFn,
291
292     // @private
293     setViewBox: function (x, y, width, height) {
294         if (isFinite(x) &amp;&amp; isFinite(y) &amp;&amp; isFinite(width) &amp;&amp; isFinite(height)) {
295             this.viewBox = {x: x, y: y, width: width, height: height};
296             this.applyViewBox();
297         }
298     },
299
300 <span id='Ext-draw-Surface-method-addCls'>    /**
301 </span>     * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
302      *
303      * For example:
304      *
305      *     drawComponent.surface.addCls(sprite, 'x-visible');
306      *
307      * @param {Object} sprite The sprite to add the class to.
308      * @param {String/String[]} className The CSS class to add, or an array of classes
309      * @method
310      */
311     addCls: Ext.emptyFn,
312
313 <span id='Ext-draw-Surface-method-removeCls'>    /**
314 </span>     * Removes one or more CSS classes from the element.
315      *
316      * For example:
317      *
318      *     drawComponent.surface.removeCls(sprite, 'x-visible');
319      *
320      * @param {Object} sprite The sprite to remove the class from.
321      * @param {String/String[]} className The CSS class to remove, or an array of classes
322      * @method
323      */
324     removeCls: Ext.emptyFn,
325
326 <span id='Ext-draw-Surface-method-setStyle'>    /**
327 </span>     * Sets CSS style attributes to an element.
328      *
329      * For example:
330      *
331      *     drawComponent.surface.setStyle(sprite, {
332      *         'cursor': 'pointer'
333      *     });
334      *
335      * @param {Object} sprite The sprite to add, or an array of classes to
336      * @param {Object} styles An Object with CSS styles.
337      * @method
338      */
339     setStyle: Ext.emptyFn,
340
341     // @private
342     initGradients: function() {
343         var gradients = this.gradients;
344         if (gradients) {
345             Ext.each(gradients, this.addGradient, this);
346         }
347     },
348
349     // @private
350     initItems: function() {
351         var items = this.items;
352         this.items = Ext.create('Ext.draw.CompositeSprite');
353         this.groups = Ext.create('Ext.draw.CompositeSprite');
354         if (items) {
355             this.add(items);
356         }
357     },
358
359     // @private
360     initBackground: function(config) {
361         var me = this,
362             width = me.width,
363             height = me.height,
364             gradientId, gradient, backgroundSprite;
365         if (config) {
366             if (config.gradient) {
367                 gradient = config.gradient;
368                 gradientId = gradient.id;
369                 me.addGradient(gradient);
370                 me.background = me.add({
371                     type: 'rect',
372                     x: 0,
373                     y: 0,
374                     width: width,
375                     height: height,
376                     fill: 'url(#' + gradientId + ')'
377                 });
378             } else if (config.fill) {
379                 me.background = me.add({
380                     type: 'rect',
381                     x: 0,
382                     y: 0,
383                     width: width,
384                     height: height,
385                     fill: config.fill
386                 });
387             } else if (config.image) {
388                 me.background = me.add({
389                     type: 'image',
390                     x: 0,
391                     y: 0,
392                     width: width,
393                     height: height,
394                     src: config.image
395                 });
396             }
397         }
398     },
399
400 <span id='Ext-draw-Surface-method-setSize'>    /**
401 </span>     * Sets the size of the surface. Accomodates the background (if any) to fit the new size too.
402      *
403      * For example:
404      *
405      *     drawComponent.surface.setSize(500, 500);
406      *
407      * This method is generally called when also setting the size of the draw Component.
408      *
409      * @param {Number} w The new width of the canvas.
410      * @param {Number} h The new height of the canvas.
411      */
412     setSize: function(w, h) {
413         if (this.background) {
414             this.background.setAttributes({
415                 width: w,
416                 height: h,
417                 hidden: false
418             }, true);
419         }
420         this.applyViewBox();
421     },
422
423     // @private
424     scrubAttrs: function(sprite) {
425         var i,
426             attrs = {},
427             exclude = {},
428             sattr = sprite.attr;
429         for (i in sattr) {
430             // Narrow down attributes to the main set
431             if (this.translateAttrs.hasOwnProperty(i)) {
432                 // Translated attr
433                 attrs[this.translateAttrs[i]] = sattr[i];
434                 exclude[this.translateAttrs[i]] = true;
435             }
436             else if (this.availableAttrs.hasOwnProperty(i) &amp;&amp; !exclude[i]) {
437                 // Passtrhough attr
438                 attrs[i] = sattr[i];
439             }
440         }
441         return attrs;
442     },
443
444     // @private
445     onClick: function(e) {
446         this.processEvent('click', e);
447     },
448
449     // @private
450     onMouseUp: function(e) {
451         this.processEvent('mouseup', e);
452     },
453
454     // @private
455     onMouseDown: function(e) {
456         this.processEvent('mousedown', e);
457     },
458
459     // @private
460     onMouseOver: function(e) {
461         this.processEvent('mouseover', e);
462     },
463
464     // @private
465     onMouseOut: function(e) {
466         this.processEvent('mouseout', e);
467     },
468
469     // @private
470     onMouseMove: function(e) {
471         this.fireEvent('mousemove', e);
472     },
473
474     // @private
475     onMouseEnter: Ext.emptyFn,
476
477     // @private
478     onMouseLeave: Ext.emptyFn,
479
480 <span id='Ext-draw-Surface-method-addGradient'>    /**
481 </span>     * Adds a gradient definition to the Surface. Note that in some surface engines, adding
482      * a gradient via this method will not take effect if the surface has already been rendered.
483      * Therefore, it is preferred to pass the gradients as an item to the surface config, rather
484      * than calling this method, especially if the surface is rendered immediately (e.g. due to
485      * 'renderTo' in its config). For more information on how to create gradients in the Chart
486      * configuration object please refer to {@link Ext.chart.Chart}.
487      *
488      * The gradient object to be passed into this method is composed by:
489      *
490      * - **id** - string - The unique name of the gradient.
491      * - **angle** - number, optional - The angle of the gradient in degrees.
492      * - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values.
493      *
494      * For example:
495      *
496      *    drawComponent.surface.addGradient({
497      *        id: 'gradientId',
498      *        angle: 45,
499      *        stops: {
500      *            0: {
501      *                color: '#555'
502      *            },
503      *            100: {
504      *                color: '#ddd'
505      *            }
506      *        }
507      *    });
508      *
509      * @method
510      */
511     addGradient: Ext.emptyFn,
512
513 <span id='Ext-draw-Surface-method-add'>    /**
514 </span>     * Adds a Sprite to the surface. See {@link Ext.draw.Sprite} for the configuration object to be
515      * passed into this method.
516      *
517      * For example:
518      *
519      *     drawComponent.surface.add({
520      *         type: 'circle',
521      *         fill: '#ffc',
522      *         radius: 100,
523      *         x: 100,
524      *         y: 100
525      *     });
526      *
527      */
528     add: function() {
529         var args = Array.prototype.slice.call(arguments),
530             sprite,
531             index;
532
533         var hasMultipleArgs = args.length &gt; 1;
534         if (hasMultipleArgs || Ext.isArray(args[0])) {
535             var items = hasMultipleArgs ? args : args[0],
536                 results = [],
537                 i, ln, item;
538
539             for (i = 0, ln = items.length; i &lt; ln; i++) {
540                 item = items[i];
541                 item = this.add(item);
542                 results.push(item);
543             }
544
545             return results;
546         }
547         sprite = this.prepareItems(args[0], true)[0];
548         this.insertByZIndex(sprite);
549         this.onAdd(sprite);
550         return sprite;
551     },
552
553 <span id='Ext-draw-Surface-method-insertByZIndex'>    /**
554 </span>     * @private
555      * Inserts a given sprite into the correct position in the items collection, according to
556      * its zIndex. It will be inserted at the end of an existing series of sprites with the same or
557      * lower zIndex. By ensuring sprites are always ordered, this allows surface subclasses to render
558      * the sprites in the correct order for proper z-index stacking.
559      * @param {Ext.draw.Sprite} sprite
560      * @return {Number} the sprite's new index in the list
561      */
562     insertByZIndex: function(sprite) {
563         var me = this,
564             sprites = me.items.items,
565             len = sprites.length,
566             ceil = Math.ceil,
567             zIndex = sprite.attr.zIndex,
568             idx = len,
569             high = idx - 1,
570             low = 0,
571             otherZIndex;
572
573         if (me.orderSpritesByZIndex &amp;&amp; len &amp;&amp; zIndex &lt; sprites[high].attr.zIndex) {
574             // Find the target index via a binary search for speed
575             while (low &lt;= high) {
576                 idx = ceil((low + high) / 2);
577                 otherZIndex = sprites[idx].attr.zIndex;
578                 if (otherZIndex &gt; zIndex) {
579                     high = idx - 1;
580                 }
581                 else if (otherZIndex &lt; zIndex) {
582                     low = idx + 1;
583                 }
584                 else {
585                     break;
586                 }
587             }
588             // Step forward to the end of a sequence of the same or lower z-index
589             while (idx &lt; len &amp;&amp; sprites[idx].attr.zIndex &lt;= zIndex) {
590                 idx++;
591             }
592         }
593
594         me.items.insert(idx, sprite);
595         return idx;
596     },
597
598     onAdd: function(sprite) {
599         var group = sprite.group,
600             draggable = sprite.draggable,
601             groups, ln, i;
602         if (group) {
603             groups = [].concat(group);
604             ln = groups.length;
605             for (i = 0; i &lt; ln; i++) {
606                 group = groups[i];
607                 this.getGroup(group).add(sprite);
608             }
609             delete sprite.group;
610         }
611         if (draggable) {
612             sprite.initDraggable();
613         }
614     },
615
616 <span id='Ext-draw-Surface-method-remove'>    /**
617 </span>     * Removes a given sprite from the surface, optionally destroying the sprite in the process.
618      * You can also call the sprite own `remove` method.
619      *
620      * For example:
621      *
622      *     drawComponent.surface.remove(sprite);
623      *     //or...
624      *     sprite.remove();
625      *
626      * @param {Ext.draw.Sprite} sprite
627      * @param {Boolean} destroySprite
628      * @return {Number} the sprite's new index in the list
629      */
630     remove: function(sprite, destroySprite) {
631         if (sprite) {
632             this.items.remove(sprite);
633             this.groups.each(function(item) {
634                 item.remove(sprite);
635             });
636             sprite.onRemove();
637             if (destroySprite === true) {
638                 sprite.destroy();
639             }
640         }
641     },
642
643 <span id='Ext-draw-Surface-method-removeAll'>    /**
644 </span>     * Removes all sprites from the surface, optionally destroying the sprites in the process.
645      *
646      * For example:
647      *
648      *     drawComponent.surface.removeAll();
649      *
650      * @param {Boolean} destroySprites Whether to destroy all sprites when removing them.
651      * @return {Number} The sprite's new index in the list.
652      */
653     removeAll: function(destroySprites) {
654         var items = this.items.items,
655             ln = items.length,
656             i;
657         for (i = ln - 1; i &gt; -1; i--) {
658             this.remove(items[i], destroySprites);
659         }
660     },
661
662     onRemove: Ext.emptyFn,
663
664     onDestroy: Ext.emptyFn,
665
666 <span id='Ext-draw-Surface-method-applyViewBox'>    /**
667 </span>     * @private Using the current viewBox property and the surface's width and height, calculate the
668      * appropriate viewBoxShift that will be applied as a persistent transform to all sprites.
669      */
670     applyViewBox: function() {
671         var me = this,
672             viewBox = me.viewBox,
673             width = me.width,
674             height = me.height,
675             viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,
676             relativeHeight, relativeWidth, size;
677
678         if (viewBox &amp;&amp; (width || height)) {
679             viewBoxX = viewBox.x;
680             viewBoxY = viewBox.y;
681             viewBoxWidth = viewBox.width;
682             viewBoxHeight = viewBox.height;
683             relativeHeight = height / viewBoxHeight;
684             relativeWidth = width / viewBoxWidth;
685
686             if (viewBoxWidth * relativeHeight &lt; width) {
687                 viewBoxX -= (width - viewBoxWidth * relativeHeight) / 2 / relativeHeight;
688             }
689             if (viewBoxHeight * relativeWidth &lt; height) {
690                 viewBoxY -= (height - viewBoxHeight * relativeWidth) / 2 / relativeWidth;
691             }
692
693             size = 1 / Math.min(viewBoxWidth, relativeHeight);
694
695             me.viewBoxShift = {
696                 dx: -viewBoxX,
697                 dy: -viewBoxY,
698                 scale: size
699             };
700         }
701     },
702
703     transformToViewBox: function (x, y) {
704         if (this.viewBoxShift) {
705             var me = this, shift = me.viewBoxShift;
706             return [x * shift.scale - shift.dx, y * shift.scale - shift.dy];
707         } else {
708             return [x, y];
709         }
710     },
711
712     // @private
713     applyTransformations: function(sprite) {
714             sprite.bbox.transform = 0;
715             this.transform(sprite);
716
717         var me = this,
718             dirty = false,
719             attr = sprite.attr;
720
721         if (attr.translation.x != null || attr.translation.y != null) {
722             me.translate(sprite);
723             dirty = true;
724         }
725         if (attr.scaling.x != null || attr.scaling.y != null) {
726             me.scale(sprite);
727             dirty = true;
728         }
729         if (attr.rotation.degrees != null) {
730             me.rotate(sprite);
731             dirty = true;
732         }
733         if (dirty) {
734             sprite.bbox.transform = 0;
735             this.transform(sprite);
736             sprite.transformations = [];
737         }
738     },
739
740     // @private
741     rotate: function (sprite) {
742         var bbox,
743             deg = sprite.attr.rotation.degrees,
744             centerX = sprite.attr.rotation.x,
745             centerY = sprite.attr.rotation.y;
746         if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
747             bbox = this.getBBox(sprite);
748             centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
749             centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
750         }
751         sprite.transformations.push({
752             type: &quot;rotate&quot;,
753             degrees: deg,
754             x: centerX,
755             y: centerY
756         });
757     },
758
759     // @private
760     translate: function(sprite) {
761         var x = sprite.attr.translation.x || 0,
762             y = sprite.attr.translation.y || 0;
763         sprite.transformations.push({
764             type: &quot;translate&quot;,
765             x: x,
766             y: y
767         });
768     },
769
770     // @private
771     scale: function(sprite) {
772         var bbox,
773             x = sprite.attr.scaling.x || 1,
774             y = sprite.attr.scaling.y || 1,
775             centerX = sprite.attr.scaling.centerX,
776             centerY = sprite.attr.scaling.centerY;
777
778         if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {
779             bbox = this.getBBox(sprite);
780             centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;
781             centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;
782         }
783         sprite.transformations.push({
784             type: &quot;scale&quot;,
785             x: x,
786             y: y,
787             centerX: centerX,
788             centerY: centerY
789         });
790     },
791
792     // @private
793     rectPath: function (x, y, w, h, r) {
794         if (r) {
795             return [[&quot;M&quot;, x + r, y], [&quot;l&quot;, w - r * 2, 0], [&quot;a&quot;, r, r, 0, 0, 1, r, r], [&quot;l&quot;, 0, h - r * 2], [&quot;a&quot;, r, r, 0, 0, 1, -r, r], [&quot;l&quot;, r * 2 - w, 0], [&quot;a&quot;, r, r, 0, 0, 1, -r, -r], [&quot;l&quot;, 0, r * 2 - h], [&quot;a&quot;, r, r, 0, 0, 1, r, -r], [&quot;z&quot;]];
796         }
797         return [[&quot;M&quot;, x, y], [&quot;l&quot;, w, 0], [&quot;l&quot;, 0, h], [&quot;l&quot;, -w, 0], [&quot;z&quot;]];
798     },
799
800     // @private
801     ellipsePath: function (x, y, rx, ry) {
802         if (ry == null) {
803             ry = rx;
804         }
805         return [[&quot;M&quot;, x, y], [&quot;m&quot;, 0, -ry], [&quot;a&quot;, rx, ry, 0, 1, 1, 0, 2 * ry], [&quot;a&quot;, rx, ry, 0, 1, 1, 0, -2 * ry], [&quot;z&quot;]];
806     },
807
808     // @private
809     getPathpath: function (el) {
810         return el.attr.path;
811     },
812
813     // @private
814     getPathcircle: function (el) {
815         var a = el.attr;
816         return this.ellipsePath(a.x, a.y, a.radius, a.radius);
817     },
818
819     // @private
820     getPathellipse: function (el) {
821         var a = el.attr;
822         return this.ellipsePath(a.x, a.y,
823                                 a.radiusX || (a.width / 2) || 0,
824                                 a.radiusY || (a.height / 2) || 0);
825     },
826
827     // @private
828     getPathrect: function (el) {
829         var a = el.attr;
830         return this.rectPath(a.x, a.y, a.width, a.height, a.r);
831     },
832
833     // @private
834     getPathimage: function (el) {
835         var a = el.attr;
836         return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);
837     },
838
839     // @private
840     getPathtext: function (el) {
841         var bbox = this.getBBoxText(el);
842         return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
843     },
844
845     createGroup: function(id) {
846         var group = this.groups.get(id);
847         if (!group) {
848             group = Ext.create('Ext.draw.CompositeSprite', {
849                 surface: this
850             });
851             group.id = id || Ext.id(null, 'ext-surface-group-');
852             this.groups.add(group);
853         }
854         return group;
855     },
856
857 <span id='Ext-draw-Surface-method-getGroup'>    /**
858 </span>     * Returns a new group or an existent group associated with the current surface.
859      * The group returned is a {@link Ext.draw.CompositeSprite} group.
860      *
861      * For example:
862      *
863      *     var spriteGroup = drawComponent.surface.getGroup('someGroupId');
864      *
865      * @param {String} id The unique identifier of the group.
866      * @return {Object} The {@link Ext.draw.CompositeSprite}.
867      */
868     getGroup: function(id) {
869         if (typeof id == &quot;string&quot;) {
870             var group = this.groups.get(id);
871             if (!group) {
872                 group = this.createGroup(id);
873             }
874         } else {
875             group = id;
876         }
877         return group;
878     },
879
880     // @private
881     prepareItems: function(items, applyDefaults) {
882         items = [].concat(items);
883         // Make sure defaults are applied and item is initialized
884         var item, i, ln;
885         for (i = 0, ln = items.length; i &lt; ln; i++) {
886             item = items[i];
887             if (!(item instanceof Ext.draw.Sprite)) {
888                 // Temporary, just take in configs...
889                 item.surface = this;
890                 items[i] = this.createItem(item);
891             } else {
892                 item.surface = this;
893             }
894         }
895         return items;
896     },
897
898 <span id='Ext-draw-Surface-method-setText'>    /**
899 </span>     * Changes the text in the sprite element. The sprite must be a `text` sprite.
900      * This method can also be called from {@link Ext.draw.Sprite}.
901      *
902      * For example:
903      *
904      *     var spriteGroup = drawComponent.surface.setText(sprite, 'my new text');
905      *
906      * @param {Object} sprite The Sprite to change the text.
907      * @param {String} text The new text to be set.
908      * @method
909      */
910     setText: Ext.emptyFn,
911
912     //@private Creates an item and appends it to the surface. Called
913     //as an internal method when calling `add`.
914     createItem: Ext.emptyFn,
915
916 <span id='Ext-draw-Surface-method-getId'>    /**
917 </span>     * Retrieves the id of this component.
918      * Will autogenerate an id if one has not already been set.
919      */
920     getId: function() {
921         return this.id || (this.id = Ext.id(null, 'ext-surface-'));
922     },
923
924 <span id='Ext-draw-Surface-method-destroy'>    /**
925 </span>     * Destroys the surface. This is done by removing all components from it and
926      * also removing its reference to a DOM element.
927      *
928      * For example:
929      *
930      *      drawComponent.surface.destroy();
931      */
932     destroy: function() {
933         delete this.domRef;
934         this.removeAll();
935     }
936 });</pre>
937 </body>
938 </html>