Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Surface.html
index 2a260b8..03afb1b 100644 (file)
@@ -1,8 +1,22 @@
-<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-draw.Surface'>/**
-</span> * @class Ext.draw.Surface
- * @extends Object
- *
- * A Surface is an interface to render methods inside a draw {@link Ext.draw.Component}.
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>The source code</title>
+  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
+  <style type="text/css">
+    .highlight { display: block; background-color: #ddd; }
+  </style>
+  <script type="text/javascript">
+    function highlight() {
+      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
+    }
+  </script>
+</head>
+<body onload="prettyPrint(); highlight();">
+  <pre class="prettyprint lang-js"><span id='Ext-draw-Surface'>/**
+</span> * A Surface is an interface to render methods inside a draw {@link Ext.draw.Component}.
  * A Surface contains methods to render sprites, get bounding boxes of sprites, add
  * sprites to the canvas, initialize other graphic components, etc. One of the most used
  * methods for this class is the `add` method, to add Sprites to the surface.
@@ -23,7 +37,7 @@
  * The configuration object passed in the `add` method is the same as described in the {@link Ext.draw.Sprite}
  * class documentation.
  *
- * ### Listeners
+ * # Listeners
  *
  * You can also add event listeners to the surface using the `Observable` listener syntax. Supported events are:
  *
  *
  * For example:
  *
-        drawComponent.surface.on({
-           'mousemove': function() {
-                console.log('moving the mouse over the surface');   
-            }
-        });
+ *     drawComponent.surface.on({
+ *        'mousemove': function() {
+ *             console.log('moving the mouse over the surface');
+ *         }
+ *     });
+ *
+ * # Example
+ *
+ *     var drawComponent = Ext.create('Ext.draw.Component', {
+ *         width: 800,
+ *         height: 600,
+ *         renderTo: document.body
+ *     }), surface = drawComponent.surface;
+ *
+ *     surface.add([{
+ *         type: 'circle',
+ *         radius: 10,
+ *         fill: '#f00',
+ *         x: 10,
+ *         y: 10,
+ *         group: 'circles'
+ *     }, {
+ *         type: 'circle',
+ *         radius: 10,
+ *         fill: '#0f0',
+ *         x: 50,
+ *         y: 50,
+ *         group: 'circles'
+ *     }, {
+ *         type: 'circle',
+ *         radius: 10,
+ *         fill: '#00f',
+ *         x: 100,
+ *         y: 100,
+ *         group: 'circles'
+ *     }, {
+ *         type: 'rect',
+ *         width: 20,
+ *         height: 20,
+ *         fill: '#f00',
+ *         x: 10,
+ *         y: 10,
+ *         group: 'rectangles'
+ *     }, {
+ *         type: 'rect',
+ *         width: 20,
+ *         height: 20,
+ *         fill: '#0f0',
+ *         x: 50,
+ *         y: 50,
+ *         group: 'rectangles'
+ *     }, {
+ *         type: 'rect',
+ *         width: 20,
+ *         height: 20,
+ *         fill: '#00f',
+ *         x: 100,
+ *         y: 100,
+ *         group: 'rectangles'
+ *     }]);
+ *
+ *     // Get references to my groups
+ *     circles = surface.getGroup('circles');
+ *     rectangles = surface.getGroup('rectangles');
+ *
+ *     // Animate the circles down
+ *     circles.animate({
+ *         duration: 1000,
+ *         to: {
+ *             translate: {
+ *                 y: 200
+ *             }
+ *         }
+ *     });
+ *
+ *     // Animate the rectangles across
+ *     rectangles.animate({
+ *         duration: 1000,
+ *         to: {
+ *             translate: {
+ *                 x: 200
+ *             }
+ *         }
+ *     });
  */
 Ext.define('Ext.draw.Surface', {
 
@@ -58,12 +151,13 @@ Ext.define('Ext.draw.Surface', {
     separatorRe: /[, ]+/,
 
     statics: {
-<span id='Ext-draw.Surface-method-create'>        /**
-</span>         * Create and return a new concrete Surface instance appropriate for the current environment.
+<span id='Ext-draw-Surface-static-method-create'>        /**
+</span>         * Creates and returns a new concrete Surface instance appropriate for the current environment.
          * @param {Object} config Initial configuration for the Surface instance
-         * @param {Array} enginePriority Optional order of implementations to use; the first one that is
-         *                available in the current environment will be used. Defaults to
-         *                &lt;code&gt;['Svg', 'Vml']&lt;/code&gt;.
+         * @param {String[]} enginePriority (Optional) order of implementations to use; the first one that is
+         * available in the current environment will be used. Defaults to `['Svg', 'Vml']`.
+         * @return {Object} The created Surface or false.
+         * @static
          */
         create: function(config, enginePriority) {
             enginePriority = enginePriority || ['Svg', 'Vml'];
@@ -126,29 +220,39 @@ Ext.define('Ext.draw.Surface', {
         zIndex: 0
     },
 
-<span id='Ext-draw.Surface-cfg-height'> /**
-</span>  * @cfg {Number} height
-  * The height of this component in pixels (defaults to auto).
-  * &lt;b&gt;Note&lt;/b&gt; to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
-  */
-<span id='Ext-draw.Surface-cfg-width'> /**
-</span>  * @cfg {Number} width
-  * The width of this component in pixels (defaults to auto).
-  * &lt;b&gt;Note&lt;/b&gt; to express this dimension as a percentage or offset see {@link Ext.Component#anchor}.
-  */
+<span id='Ext-draw-Surface-cfg-height'>    /**
+</span>     * @cfg {Number} height
+     * The height of this component in pixels (defaults to auto).
+     */
+<span id='Ext-draw-Surface-cfg-width'>    /**
+</span>     * @cfg {Number} width
+     * The width of this component in pixels (defaults to auto).
+     */
+
     container: undefined,
     height: 352,
     width: 512,
     x: 0,
     y: 0,
 
+<span id='Ext-draw-Surface-property-orderSpritesByZIndex'>    /**
+</span>     * @private Flag indicating that the surface implementation requires sprites to be maintained
+     * in order of their zIndex. Impls that don't require this can set it to false.
+     */
+    orderSpritesByZIndex: true,
+
+
+<span id='Ext-draw-Surface-method-constructor'>    /**
+</span>     * Creates new Surface.
+     * @param {Object} config (optional) Config object.
+     */
     constructor: function(config) {
         var me = this;
         config = config || {};
         Ext.apply(me, config);
 
         me.domRef = Ext.getDoc().dom;
-        
+
         me.customAttributes = {};
 
         me.addEvents(
@@ -186,43 +290,51 @@ Ext.define('Ext.draw.Surface', {
     renderItems: Ext.emptyFn,
 
     // @private
-    setViewBox: Ext.emptyFn,
+    setViewBox: function (x, y, width, height) {
+        if (isFinite(x) &amp;&amp; isFinite(y) &amp;&amp; isFinite(width) &amp;&amp; isFinite(height)) {
+            this.viewBox = {x: x, y: y, width: width, height: height};
+            this.applyViewBox();
+        }
+    },
 
-<span id='Ext-draw.Surface-property-addCls'>    /**
+<span id='Ext-draw-Surface-method-addCls'>    /**
 </span>     * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
      *
      * For example:
      *
-     *          drawComponent.surface.addCls(sprite, 'x-visible');
-     *      
+     *     drawComponent.surface.addCls(sprite, 'x-visible');
+     *
      * @param {Object} sprite The sprite to add the class to.
-     * @param {String/Array} className The CSS class to add, or an array of classes
+     * @param {String/String[]} className The CSS class to add, or an array of classes
+     * @method
      */
     addCls: Ext.emptyFn,
 
-<span id='Ext-draw.Surface-property-removeCls'>    /**
+<span id='Ext-draw-Surface-method-removeCls'>    /**
 </span>     * Removes one or more CSS classes from the element.
      *
      * For example:
      *
-     *      drawComponent.surface.removeCls(sprite, 'x-visible');
-     *      
+     *     drawComponent.surface.removeCls(sprite, 'x-visible');
+     *
      * @param {Object} sprite The sprite to remove the class from.
-     * @param {String/Array} className The CSS class to remove, or an array of classes
+     * @param {String/String[]} className The CSS class to remove, or an array of classes
+     * @method
      */
     removeCls: Ext.emptyFn,
 
-<span id='Ext-draw.Surface-property-setStyle'>    /**
+<span id='Ext-draw-Surface-method-setStyle'>    /**
 </span>     * Sets CSS style attributes to an element.
      *
      * For example:
      *
-     *      drawComponent.surface.setStyle(sprite, {
-     *          'cursor': 'pointer'
-     *      });
-     *      
+     *     drawComponent.surface.setStyle(sprite, {
+     *         'cursor': 'pointer'
+     *     });
+     *
      * @param {Object} sprite The sprite to add, or an array of classes to
      * @param {Object} styles An Object with CSS styles.
+     * @method
      */
     setStyle: Ext.emptyFn,
 
@@ -243,20 +355,19 @@ Ext.define('Ext.draw.Surface', {
             this.add(items);
         }
     },
-    
+
     // @private
     initBackground: function(config) {
-        var gradientId, 
-            gradient,
-            backgroundSprite,
-            width = this.width,
-            height = this.height;
+        var me = this,
+            width = me.width,
+            height = me.height,
+            gradientId, gradient, backgroundSprite;
         if (config) {
             if (config.gradient) {
                 gradient = config.gradient;
                 gradientId = gradient.id;
-                this.addGradient(gradient);
-                this.background = this.add({
+                me.addGradient(gradient);
+                me.background = me.add({
                     type: 'rect',
                     x: 0,
                     y: 0,
@@ -265,7 +376,7 @@ Ext.define('Ext.draw.Surface', {
                     fill: 'url(#' + gradientId + ')'
                 });
             } else if (config.fill) {
-                this.background = this.add({
+                me.background = me.add({
                     type: 'rect',
                     x: 0,
                     y: 0,
@@ -274,7 +385,7 @@ Ext.define('Ext.draw.Surface', {
                     fill: config.fill
                 });
             } else if (config.image) {
-                this.background = this.add({
+                me.background = me.add({
                     type: 'image',
                     x: 0,
                     y: 0,
@@ -285,16 +396,16 @@ Ext.define('Ext.draw.Surface', {
             }
         }
     },
-    
-<span id='Ext-draw.Surface-method-setSize'>    /**
+
+<span id='Ext-draw-Surface-method-setSize'>    /**
 </span>     * Sets the size of the surface. Accomodates the background (if any) to fit the new size too.
      *
      * For example:
      *
-     *      drawComponent.surface.setSize(500, 500);
+     *     drawComponent.surface.setSize(500, 500);
      *
      * This method is generally called when also setting the size of the draw Component.
-     * 
+     *
      * @param {Number} w The new width of the canvas.
      * @param {Number} h The new height of the canvas.
      */
@@ -306,6 +417,7 @@ Ext.define('Ext.draw.Surface', {
                 hidden: false
             }, true);
         }
+        this.applyViewBox();
     },
 
     // @private
@@ -314,7 +426,7 @@ Ext.define('Ext.draw.Surface', {
             attrs = {},
             exclude = {},
             sattr = sprite.attr;
-        for (i in sattr) {    
+        for (i in sattr) {
             // Narrow down attributes to the main set
             if (this.translateAttrs.hasOwnProperty(i)) {
                 // Translated attr
@@ -365,8 +477,8 @@ Ext.define('Ext.draw.Surface', {
     // @private
     onMouseLeave: Ext.emptyFn,
 
-<span id='Ext-draw.Surface-property-addGradient'>    /**
-</span>     * Add a gradient definition to the Surface. Note that in some surface engines, adding
+<span id='Ext-draw-Surface-method-addGradient'>    /**
+</span>     * Adds a gradient definition to the Surface. Note that in some surface engines, adding
      * a gradient via this method will not take effect if the surface has already been rendered.
      * Therefore, it is preferred to pass the gradients as an item to the surface config, rather
      * than calling this method, especially if the surface is rendered immediately (e.g. due to
@@ -374,31 +486,33 @@ Ext.define('Ext.draw.Surface', {
      * configuration object please refer to {@link Ext.chart.Chart}.
      *
      * The gradient object to be passed into this method is composed by:
-     * 
-     * 
-     *  - **id** - string - The unique name of the gradient.
-     *  - **angle** - number, optional - The angle of the gradient in degrees.
-     *  - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values.
-     * 
-     *
-     For example:
-                drawComponent.surface.addGradient({
-                    id: 'gradientId',
-                    angle: 45,
-                    stops: {
-                        0: {
-                            color: '#555'
-                        },
-                        100: {
-                            color: '#ddd'
-                        }
-                    }
-                });
+     *
+     * - **id** - string - The unique name of the gradient.
+     * - **angle** - number, optional - The angle of the gradient in degrees.
+     * - **stops** - object - An object with numbers as keys (from 0 to 100) and style objects as values.
+     *
+     * For example:
+     *
+     *    drawComponent.surface.addGradient({
+     *        id: 'gradientId',
+     *        angle: 45,
+     *        stops: {
+     *            0: {
+     *                color: '#555'
+     *            },
+     *            100: {
+     *                color: '#ddd'
+     *            }
+     *        }
+     *    });
+     *
+     * @method
      */
     addGradient: Ext.emptyFn,
 
-<span id='Ext-draw.Surface-method-add'>    /**
-</span>     * Add a Sprite to the surface. See {@link Ext.draw.Sprite} for the configuration object to be passed into this method.
+<span id='Ext-draw-Surface-method-add'>    /**
+</span>     * Adds a Sprite to the surface. See {@link Ext.draw.Sprite} for the configuration object to be
+     * passed into this method.
      *
      * For example:
      *
@@ -410,7 +524,7 @@ Ext.define('Ext.draw.Surface', {
      *         y: 100
      *     });
      *
-    */
+     */
     add: function() {
         var args = Array.prototype.slice.call(arguments),
             sprite,
@@ -431,38 +545,53 @@ Ext.define('Ext.draw.Surface', {
             return results;
         }
         sprite = this.prepareItems(args[0], true)[0];
-        this.normalizeSpriteCollection(sprite);
+        this.insertByZIndex(sprite);
         this.onAdd(sprite);
         return sprite;
     },
 
-<span id='Ext-draw.Surface-method-normalizeSpriteCollection'>    /**
+<span id='Ext-draw-Surface-method-insertByZIndex'>    /**
 </span>     * @private
-     * Insert or move a given sprite into the correct position in the items
-     * MixedCollection, according to its zIndex. Will be inserted at the end of
-     * an existing series of sprites with the same or lower zIndex. If the sprite
-     * is already positioned within an appropriate zIndex group, it will not be moved.
-     * This ordering can be used by subclasses to assist in rendering the sprites in
-     * the correct order for proper z-index stacking.
+     * Inserts a given sprite into the correct position in the items collection, according to
+     * its zIndex. It will be inserted at the end of an existing series of sprites with the same or
+     * lower zIndex. By ensuring sprites are always ordered, this allows surface subclasses to render
+     * the sprites in the correct order for proper z-index stacking.
      * @param {Ext.draw.Sprite} sprite
      * @return {Number} the sprite's new index in the list
      */
-    normalizeSpriteCollection: function(sprite) {
-        var items = this.items,
+    insertByZIndex: function(sprite) {
+        var me = this,
+            sprites = me.items.items,
+            len = sprites.length,
+            ceil = Math.ceil,
             zIndex = sprite.attr.zIndex,
-            idx = items.indexOf(sprite);
-
-        if (idx &lt; 0 || (idx &gt; 0 &amp;&amp; items.getAt(idx - 1).attr.zIndex &gt; zIndex) ||
-                (idx &lt; items.length - 1 &amp;&amp; items.getAt(idx + 1).attr.zIndex &lt; zIndex)) {
-            items.removeAt(idx);
-            idx = items.findIndexBy(function(otherSprite) {
-                return otherSprite.attr.zIndex &gt; zIndex;
-            });
-            if (idx &lt; 0) {
-                idx = items.length;
+            idx = len,
+            high = idx - 1,
+            low = 0,
+            otherZIndex;
+
+        if (me.orderSpritesByZIndex &amp;&amp; len &amp;&amp; zIndex &lt; sprites[high].attr.zIndex) {
+            // Find the target index via a binary search for speed
+            while (low &lt;= high) {
+                idx = ceil((low + high) / 2);
+                otherZIndex = sprites[idx].attr.zIndex;
+                if (otherZIndex &gt; zIndex) {
+                    high = idx - 1;
+                }
+                else if (otherZIndex &lt; zIndex) {
+                    low = idx + 1;
+                }
+                else {
+                    break;
+                }
+            }
+            // Step forward to the end of a sequence of the same or lower z-index
+            while (idx &lt; len &amp;&amp; sprites[idx].attr.zIndex &lt;= zIndex) {
+                idx++;
             }
-            items.insert(idx, sprite);
         }
+
+        me.items.insert(idx, sprite);
         return idx;
     },
 
@@ -484,16 +613,16 @@ Ext.define('Ext.draw.Surface', {
         }
     },
 
-<span id='Ext-draw.Surface-method-remove'>    /**
-</span>     * Remove a given sprite from the surface, optionally destroying the sprite in the process.
+<span id='Ext-draw-Surface-method-remove'>    /**
+</span>     * Removes a given sprite from the surface, optionally destroying the sprite in the process.
      * You can also call the sprite own `remove` method.
      *
      * For example:
      *
-     *      drawComponent.surface.remove(sprite);
-     *      //or...
-     *      sprite.remove();
-     *      
+     *     drawComponent.surface.remove(sprite);
+     *     //or...
+     *     sprite.remove();
+     *
      * @param {Ext.draw.Sprite} sprite
      * @param {Boolean} destroySprite
      * @return {Number} the sprite's new index in the list
@@ -511,13 +640,13 @@ Ext.define('Ext.draw.Surface', {
         }
     },
 
-<span id='Ext-draw.Surface-method-removeAll'>    /**
-</span>     * Remove all sprites from the surface, optionally destroying the sprites in the process.
+<span id='Ext-draw-Surface-method-removeAll'>    /**
+</span>     * Removes all sprites from the surface, optionally destroying the sprites in the process.
      *
      * For example:
      *
-     *      drawComponent.surface.removeAll();
-     *      
+     *     drawComponent.surface.removeAll();
+     *
      * @param {Boolean} destroySprites Whether to destroy all sprites when removing them.
      * @return {Number} The sprite's new index in the list.
      */
@@ -534,6 +663,52 @@ Ext.define('Ext.draw.Surface', {
 
     onDestroy: Ext.emptyFn,
 
+<span id='Ext-draw-Surface-method-applyViewBox'>    /**
+</span>     * @private Using the current viewBox property and the surface's width and height, calculate the
+     * appropriate viewBoxShift that will be applied as a persistent transform to all sprites.
+     */
+    applyViewBox: function() {
+        var me = this,
+            viewBox = me.viewBox,
+            width = me.width,
+            height = me.height,
+            viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,
+            relativeHeight, relativeWidth, size;
+
+        if (viewBox &amp;&amp; (width || height)) {
+            viewBoxX = viewBox.x;
+            viewBoxY = viewBox.y;
+            viewBoxWidth = viewBox.width;
+            viewBoxHeight = viewBox.height;
+            relativeHeight = height / viewBoxHeight;
+            relativeWidth = width / viewBoxWidth;
+
+            if (viewBoxWidth * relativeHeight &lt; width) {
+                viewBoxX -= (width - viewBoxWidth * relativeHeight) / 2 / relativeHeight;
+            }
+            if (viewBoxHeight * relativeWidth &lt; height) {
+                viewBoxY -= (height - viewBoxHeight * relativeWidth) / 2 / relativeWidth;
+            }
+
+            size = 1 / Math.min(viewBoxWidth, relativeHeight);
+
+            me.viewBoxShift = {
+                dx: -viewBoxX,
+                dy: -viewBoxY,
+                scale: size
+            };
+        }
+    },
+
+    transformToViewBox: function (x, y) {
+        if (this.viewBoxShift) {
+            var me = this, shift = me.viewBoxShift;
+            return [x * shift.scale - shift.dx, y * shift.scale - shift.dy];
+        } else {
+            return [x, y];
+        }
+    },
+
     // @private
     applyTransformations: function(sprite) {
             sprite.bbox.transform = 0;
@@ -644,7 +819,9 @@ Ext.define('Ext.draw.Surface', {
     // @private
     getPathellipse: function (el) {
         var a = el.attr;
-        return this.ellipsePath(a.x, a.y, a.radiusX, a.radiusY);
+        return this.ellipsePath(a.x, a.y,
+                                a.radiusX || (a.width / 2) || 0,
+                                a.radiusY || (a.height / 2) || 0);
     },
 
     // @private
@@ -677,14 +854,14 @@ Ext.define('Ext.draw.Surface', {
         return group;
     },
 
-<span id='Ext-draw.Surface-method-getGroup'>    /**
+<span id='Ext-draw-Surface-method-getGroup'>    /**
 </span>     * Returns a new group or an existent group associated with the current surface.
      * The group returned is a {@link Ext.draw.CompositeSprite} group.
      *
      * For example:
      *
-     *      var spriteGroup = drawComponent.surface.getGroup('someGroupId');
-     *      
+     *     var spriteGroup = drawComponent.surface.getGroup('someGroupId');
+     *
      * @param {String} id The unique identifier of the group.
      * @return {Object} The {@link Ext.draw.CompositeSprite}.
      */
@@ -717,25 +894,26 @@ Ext.define('Ext.draw.Surface', {
         }
         return items;
     },
-    
-<span id='Ext-draw.Surface-property-setText'>    /**
+
+<span id='Ext-draw-Surface-method-setText'>    /**
 </span>     * Changes the text in the sprite element. The sprite must be a `text` sprite.
      * This method can also be called from {@link Ext.draw.Sprite}.
      *
      * For example:
      *
-     *      var spriteGroup = drawComponent.surface.setText(sprite, 'my new text');
-     *      
+     *     var spriteGroup = drawComponent.surface.setText(sprite, 'my new text');
+     *
      * @param {Object} sprite The Sprite to change the text.
      * @param {String} text The new text to be set.
+     * @method
      */
     setText: Ext.emptyFn,
-    
+
     //@private Creates an item and appends it to the surface. Called
     //as an internal method when calling `add`.
     createItem: Ext.emptyFn,
 
-<span id='Ext-draw.Surface-method-getId'>    /**
+<span id='Ext-draw-Surface-method-getId'>    /**
 </span>     * Retrieves the id of this component.
      * Will autogenerate an id if one has not already been set.
      */
@@ -743,7 +921,7 @@ Ext.define('Ext.draw.Surface', {
         return this.id || (this.id = Ext.id(null, 'ext-surface-'));
     },
 
-<span id='Ext-draw.Surface-method-destroy'>    /**
+<span id='Ext-draw-Surface-method-destroy'>    /**
 </span>     * Destroys the surface. This is done by removing all components from it and
      * also removing its reference to a DOM element.
      *
@@ -755,4 +933,6 @@ Ext.define('Ext.draw.Surface', {
         delete this.domRef;
         this.removeAll();
     }
-});</pre></pre></body></html>
\ No newline at end of file
+});</pre>
+</body>
+</html>