<!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-Sprite'>/**
</span> * A Sprite is an object rendered in a Drawing surface.
 *
 * ## Types
 *
 * The following sprite types are supported:
 *
 * ### Rect
 *
 * Rectangle requires `width` and `height` attributes:
 *
 *     @example
 *     Ext.create('Ext.draw.Component', {
 *         renderTo: Ext.getBody(),
 *         width: 200,
 *         height: 200,
 *         items: [{
 *             type: 'rect',
 *             width: 100,
 *             height: 50,
 *             radius: 10,
 *             fill: 'green',
 *             opacity: 0.5,
 *             stroke: 'red',
 *             'stroke-width': 2
 *         }]
 *     });
 *
 * ### Circle
 *
 * Circle requires `x`, `y` and `radius` attributes:
 *
 *     @example
 *     Ext.create('Ext.draw.Component', {
 *         renderTo: Ext.getBody(),
 *         width: 200,
 *         height: 200,
 *         items: [{
 *             type: 'circle',
 *             radius: 90,
 *             x: 100,
 *             y: 100,
 *             fill: 'blue',
 *         }]
 *     });
 *
 * ### Ellipse
 *
 * Ellipse requires `x`, `y`, `radiusX` and `radiusY` attributes:
 *
 *     @example
 *     Ext.create('Ext.draw.Component', {
 *         renderTo: Ext.getBody(),
 *         width: 200,
 *         height: 200,
 *         items: [{
 *             type: &quot;ellipse&quot;,
 *             radiusX: 100,
 *             radiusY: 50,
 *             x: 100,
 *             y: 100,
 *             fill: 'red'
 *         }]
 *     });
 *
 * ### Path
 *
 * Path requires the `path` attribute:
 *
 *     @example
 *     Ext.create('Ext.draw.Component', {
 *         renderTo: Ext.getBody(),
 *         width: 200,
 *         height: 200,
 *         items: [{
 *             type: &quot;path&quot;,
 *             path: &quot;M-66.6 26C-66.6 26 -75 22 -78.2 18.4C-81.4 14.8 -80.948 19.966 &quot; +
 *                   &quot;-85.8 19.6C-91.647 19.159 -90.6 3.2 -90.6 3.2L-94.6 10.8C-94.6 &quot; +
 *                   &quot;10.8 -95.8 25.2 -87.8 22.8C-83.893 21.628 -82.6 23.2 -84.2 &quot; +
 *                   &quot;24C-85.8 24.8 -78.6 25.2 -81.4 26.8C-84.2 28.4 -69.8 23.2 -72.2 &quot; +
 *                   &quot;33.6L-66.6 26z&quot;,
 *             fill: &quot;purple&quot;
 *         }]
 *     });
 *
 * ### Text
 *
 * Text requires the `text` attribute:
 *
 *     @example
 *     Ext.create('Ext.draw.Component', {
 *         renderTo: Ext.getBody(),
 *         width: 200,
 *         height: 200,
 *         items: [{
 *             type: &quot;text&quot;,
 *             text: &quot;Hello, Sprite!&quot;,
 *             fill: &quot;green&quot;,
 *             font: &quot;18px monospace&quot;
 *         }]
 *     });
 *
 * ### Image
 *
 * Image requires `width`, `height` and `src` attributes:
 *
 *     @example
 *     Ext.create('Ext.draw.Component', {
 *         renderTo: Ext.getBody(),
 *         width: 200,
 *         height: 200,
 *         items: [{
 *             type: &quot;image&quot;,
 *             src: &quot;http://www.sencha.com/img/apple-touch-icon.png&quot;,
 *             width: 200,
 *             height: 200
 *         }]
 *     });
 *
 * ## Creating and adding a Sprite to a Surface
 *
 * See {@link Ext.draw.Surface} documentation.
 *
 * ## Transforming sprites
 *
 * See {@link #setAttributes} method documentation for examples on how to translate, scale and rotate the sprites.
 *
 */
Ext.define('Ext.draw.Sprite', {

    /* Begin Definitions */

    mixins: {
        observable: 'Ext.util.Observable',
        animate: 'Ext.util.Animate'
    },

    requires: ['Ext.draw.SpriteDD'],

    /* End Definitions */

<span id='Ext-draw-Sprite-cfg-type'>    /**
</span>     * @cfg {String} type The type of the sprite.
     * Possible options are 'circle', 'ellipse', 'path', 'rect', 'text', 'image'.
     *
     * See {@link Ext.draw.Sprite} class documentation for examples of all types.
     */

<span id='Ext-draw-Sprite-cfg-width'>    /**
</span>     * @cfg {Number} width The width of the rect or image sprite.
     */

<span id='Ext-draw-Sprite-cfg-height'>    /**
</span>     * @cfg {Number} height The height of the rect or image sprite.
     */

<span id='Ext-draw-Sprite-cfg-radius'>    /**
</span>     * @cfg {Number} radius The radius of the circle sprite. Or in case of rect sprite, the border radius.
     */

<span id='Ext-draw-Sprite-cfg-radiusX'>    /**
</span>     * @cfg {Number} radiusX The radius of the ellipse sprite along x-axis.
     */

<span id='Ext-draw-Sprite-cfg-radiusY'>    /**
</span>     * @cfg {Number} radiusY The radius of the ellipse sprite along y-axis.
     */

<span id='Ext-draw-Sprite-cfg-x'>    /**
</span>     * @cfg {Number} x Sprite position along the x-axis.
     */

<span id='Ext-draw-Sprite-cfg-y'>    /**
</span>     * @cfg {Number} y Sprite position along the y-axis.
     */

<span id='Ext-draw-Sprite-cfg-path'>    /**
</span>     * @cfg {String} path The path of the path sprite written in SVG-like path syntax.
     */

<span id='Ext-draw-Sprite-cfg-opacity'>    /**
</span>     * @cfg {Number} opacity The opacity of the sprite. A number between 0 and 1.
     */

<span id='Ext-draw-Sprite-cfg-fill'>    /**
</span>     * @cfg {String} fill The fill color.
     */

<span id='Ext-draw-Sprite-cfg-stroke'>    /**
</span>     * @cfg {String} stroke The stroke color.
     */

<span id='Ext-draw-Sprite-cfg-stroke-width'>    /**
</span>     * @cfg {Number} stroke-width The width of the stroke.
     *
     * Note that this attribute needs to be quoted when used.  Like so:
     *
     *     &quot;stroke-width&quot;: 12,
     */

<span id='Ext-draw-Sprite-cfg-font'>    /**
</span>     * @cfg {String} font Used with text type sprites. The full font description.
     * Uses the same syntax as the CSS font parameter
     */

<span id='Ext-draw-Sprite-cfg-text'>    /**
</span>     * @cfg {String} text The actual text to render in text sprites.
     */

<span id='Ext-draw-Sprite-cfg-src'>    /**
</span>     * @cfg {String} src Path to the image to show in image sprites.
     */

<span id='Ext-draw-Sprite-cfg-group'>    /**
</span>     * @cfg {String/String[]} group The group that this sprite belongs to, or an array of groups.
     * Only relevant when added to a {@link Ext.draw.Surface Surface}.
     */

<span id='Ext-draw-Sprite-cfg-draggable'>    /**
</span>     * @cfg {Boolean} draggable True to make the sprite draggable.
     */

    dirty: false,
    dirtyHidden: false,
    dirtyTransform: false,
    dirtyPath: true,
    dirtyFont: true,
    zIndexDirty: true,

<span id='Ext-draw-Sprite-property-isSprite'>    /**
</span>     * @property {Boolean} isSprite
     * `true` in this class to identify an object as an instantiated Sprite, or subclass thereof.
     */
    isSprite: true,
    zIndex: 0,
    fontProperties: [
        'font',
        'font-size',
        'font-weight',
        'font-style',
        'font-family',
        'text-anchor',
        'text'
    ],
    pathProperties: [
        'x',
        'y',
        'd',
        'path',
        'height',
        'width',
        'radius',
        'r',
        'rx',
        'ry',
        'cx',
        'cy'
    ],
    constructor: function(config) {
        var me = this;
        config = Ext.merge({}, config || {});
        me.id = Ext.id(null, 'ext-sprite-');
        me.transformations = [];
        Ext.copyTo(this, config, 'surface,group,type,draggable');
        //attribute bucket
        me.bbox = {};
        me.attr = {
            zIndex: 0,
            translation: {
                x: null,
                y: null
            },
            rotation: {
                degrees: null,
                x: null,
                y: null
            },
            scaling: {
                x: null,
                y: null,
                cx: null,
                cy: null
            }
        };
        //delete not bucket attributes
        delete config.surface;
        delete config.group;
        delete config.type;
        delete config.draggable;
        me.setAttributes(config);
        me.addEvents(
<span id='Ext-draw-Sprite-event-beforedestroy'>            /**
</span>             * @event
             * Fires before the sprite is destroyed. Return false from an event handler to stop the destroy.
             * @param {Ext.draw.Sprite} this
             */
            'beforedestroy',
<span id='Ext-draw-Sprite-event-destroy'>            /**
</span>             * @event
             * Fires after the sprite is destroyed.
             * @param {Ext.draw.Sprite} this
             */
            'destroy',
<span id='Ext-draw-Sprite-event-render'>            /**
</span>             * @event
             * Fires after the sprite markup is rendered.
             * @param {Ext.draw.Sprite} this
             */
            'render',
<span id='Ext-draw-Sprite-event-mousedown'>            /**
</span>             * @event
             * @inheritdoc Ext.dom.Element#mousedown
             */
            'mousedown',
<span id='Ext-draw-Sprite-event-mouseup'>            /**
</span>             * @event
             * @inheritdoc Ext.dom.Element#mouseup
             */
            'mouseup',
<span id='Ext-draw-Sprite-event-mouseover'>            /**
</span>             * @event
             * @inheritdoc Ext.dom.Element#mouseover
             */
            'mouseover',
<span id='Ext-draw-Sprite-event-mouseout'>            /**
</span>             * @event
             * @inheritdoc Ext.dom.Element#mouseout
             */
            'mouseout',
<span id='Ext-draw-Sprite-event-mousemove'>            /**
</span>             * @event
             * @inheritdoc Ext.dom.Element#mousemove
             */
            'mousemove',
<span id='Ext-draw-Sprite-event-click'>            /**
</span>             * @event
             * @inheritdoc Ext.dom.Element#click
             */
            'click'
        );
        me.mixins.observable.constructor.apply(this, arguments);
    },

<span id='Ext-draw-Sprite-property-dd'>    /**
</span>     * @property {Ext.dd.DragSource} dd
     * If this Sprite is configured {@link #draggable}, this property will contain
     * an instance of {@link Ext.dd.DragSource} which handles dragging the Sprite.
     *
     * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
     * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
     */

    initDraggable: function() {
        var me = this;
        me.draggable = true;
        //create element if it doesn't exist.
        if (!me.el) {
            me.surface.createSpriteElement(me);
        }
        me.dd = new Ext.draw.SpriteDD(me, Ext.isBoolean(me.draggable) ? null : me.draggable);
        me.on('beforedestroy', me.dd.destroy, me.dd);
    },

<span id='Ext-draw-Sprite-method-setAttributes'>    /**
</span>     * Change the attributes of the sprite.
     *
     * ## Translation
     *
     * For translate, the configuration object contains x and y attributes that indicate where to
     * translate the object. For example:
     *
     *     sprite.setAttributes({
     *       translate: {
     *        x: 10,
     *        y: 10
     *       }
     *     }, true);
     *
     *
     * ## Rotation
     *
     * For rotation, the configuration object contains x and y attributes for the center of the rotation (which are optional),
     * and a `degrees` attribute that specifies the rotation in degrees. For example:
     *
     *     sprite.setAttributes({
     *       rotate: {
     *        degrees: 90
     *       }
     *     }, true);
     *
     * That example will create a 90 degrees rotation using the centroid of the Sprite as center of rotation, whereas:
     *
     *     sprite.setAttributes({
     *       rotate: {
     *        x: 0,
     *        y: 0,
     *        degrees: 90
     *       }
     *     }, true);
     *
     * will create a rotation around the `(0, 0)` axis.
     *
     *
     * ## Scaling
     *
     * For scaling, the configuration object contains x and y attributes for the x-axis and y-axis scaling. For example:
     *
     *     sprite.setAttributes({
     *       scale: {
     *        x: 10,
     *        y: 3
     *       }
     *     }, true);
     *
     * You can also specify the center of scaling by adding `cx` and `cy` as properties:
     *
     *     sprite.setAttributes({
     *       scale: {
     *        cx: 0,
     *        cy: 0,
     *        x: 10,
     *        y: 3
     *       }
     *     }, true);
     *
     * That last example will scale a sprite taking as centers of scaling the `(0, 0)` coordinate.
     *
     * @param {Object} attrs attributes to be changed on the sprite.
     * @param {Boolean} redraw Flag to immediately draw the change.
     * @return {Ext.draw.Sprite} this
     */
    setAttributes: function(attrs, redraw) {
        var me = this,
            fontProps = me.fontProperties,
            fontPropsLength = fontProps.length,
            pathProps = me.pathProperties,
            pathPropsLength = pathProps.length,
            hasSurface = !!me.surface,
            custom = hasSurface &amp;&amp; me.surface.customAttributes || {},
            spriteAttrs = me.attr,
            dirtyBBox = false,
            attr, i, newTranslation, translation, newRotate, rotation, newScaling, scaling;

        attrs = Ext.apply({}, attrs);
        for (attr in custom) {
            if (attrs.hasOwnProperty(attr) &amp;&amp; typeof custom[attr] == &quot;function&quot;) {
                Ext.apply(attrs, custom[attr].apply(me, [].concat(attrs[attr])));
            }
        }

        // Flag a change in hidden
        if (!!attrs.hidden !== !!spriteAttrs.hidden) {
            me.dirtyHidden = true;
        }

        // Flag path change
        for (i = 0; i &lt; pathPropsLength; i++) {
            attr = pathProps[i];
            if (attr in attrs &amp;&amp; attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyPath = true;
                dirtyBBox = true;
                break;
            }
        }

        // Flag zIndex change
        if ('zIndex' in attrs) {
            me.zIndexDirty = true;
        }

        // Flag font/text change
        if ('text' in attrs) {
            me.dirtyFont = true;
            dirtyBBox = true;
        }

        for (i = 0; i &lt; fontPropsLength; i++) {
            attr = fontProps[i];
            if (attr in attrs &amp;&amp; attrs[attr] !== spriteAttrs[attr]) {
                me.dirtyFont = true;
                dirtyBBox = true;
                break;
            }
        }

        newTranslation = attrs.translation || attrs.translate;
        delete attrs.translate;
        delete attrs.translation;
        translation = spriteAttrs.translation;
        if (newTranslation) {
            if (('x' in newTranslation &amp;&amp; newTranslation.x !== translation.x) ||
                ('y' in newTranslation &amp;&amp; newTranslation.y !== translation.y)) {
                me.dirtyTransform = true;
                translation.x = newTranslation.x;
                translation.y = newTranslation.y;
            }
        }

        newRotate = attrs.rotation || attrs.rotate;
        rotation = spriteAttrs.rotation;
        delete attrs.rotate;
        delete attrs.rotation;
        if (newRotate) {
            if (('x' in newRotate &amp;&amp; newRotate.x !== rotation.x) ||
                ('y' in newRotate &amp;&amp; newRotate.y !== rotation.y) ||
                ('degrees' in newRotate &amp;&amp; newRotate.degrees !== rotation.degrees)) {
                me.dirtyTransform = true;
                rotation.x = newRotate.x;
                rotation.y = newRotate.y;
                rotation.degrees = newRotate.degrees;
            }
        }

        newScaling = attrs.scaling || attrs.scale;
        scaling = spriteAttrs.scaling;
        delete attrs.scale;
        delete attrs.scaling;
        if (newScaling) {
            if (('x' in newScaling &amp;&amp; newScaling.x !== scaling.x) ||
                ('y' in newScaling &amp;&amp; newScaling.y !== scaling.y) ||
                ('cx' in newScaling &amp;&amp; newScaling.cx !== scaling.cx) ||
                ('cy' in newScaling &amp;&amp; newScaling.cy !== scaling.cy)) {
                me.dirtyTransform = true;
                scaling.x = newScaling.x;
                scaling.y = newScaling.y;
                scaling.cx = newScaling.cx;
                scaling.cy = newScaling.cy;
            }
        }

        // If the bbox is changed, then the bbox based transforms should be invalidated.
        if (!me.dirtyTransform &amp;&amp; dirtyBBox) {
            if (spriteAttrs.scaling.x === null ||
                spriteAttrs.scaling.y === null ||
                spriteAttrs.rotation.y === null ||
                spriteAttrs.rotation.y === null) {
                me.dirtyTransform = true;
            }
        }

        Ext.apply(spriteAttrs, attrs);
        me.dirty = true;

        if (redraw === true &amp;&amp; hasSurface) {
            me.redraw();
        }
        return this;
    },

<span id='Ext-draw-Sprite-method-getBBox'>    /**
</span>     * Retrieves the bounding box of the sprite.
     * This will be returned as an object with x, y, width, and height properties.
     * @return {Object} bbox
     */
    getBBox: function() {
        return this.surface.getBBox(this);
    },

    setText: function(text) {
        return this.surface.setText(this, text);
    },

<span id='Ext-draw-Sprite-method-hide'>    /**
</span>     * Hides the sprite.
     * @param {Boolean} redraw Flag to immediately draw the change.
     * @return {Ext.draw.Sprite} this
     */
    hide: function(redraw) {
        this.setAttributes({
            hidden: true
        }, redraw);
        return this;
    },

<span id='Ext-draw-Sprite-method-show'>    /**
</span>     * Shows the sprite.
     * @param {Boolean} redraw Flag to immediately draw the change.
     * @return {Ext.draw.Sprite} this
     */
    show: function(redraw) {
        this.setAttributes({
            hidden: false
        }, redraw);
        return this;
    },

<span id='Ext-draw-Sprite-method-remove'>    /**
</span>     * Removes the sprite.
     * @return {Boolean} True if sprite was successfully removed.
     * False when there was no surface to remove it from.
     */
    remove: function() {
        if (this.surface) {
            this.surface.remove(this);
            return true;
        }
        return false;
    },

    onRemove: function() {
        this.surface.onRemove(this);
    },

<span id='Ext-draw-Sprite-method-destroy'>    /**
</span>     * Removes the sprite and clears all listeners.
     */
    destroy: function() {
        var me = this;
        if (me.fireEvent('beforedestroy', me) !== false) {
            me.remove();
            me.surface.onDestroy(me);
            me.clearListeners();
            me.fireEvent('destroy');
        }
    },

<span id='Ext-draw-Sprite-method-redraw'>    /**
</span>     * Redraws the sprite.
     * @return {Ext.draw.Sprite} this
     */
    redraw: function() {
        this.surface.renderItem(this);
        return this;
    },

<span id='Ext-draw-Sprite-method-setStyle'>    /**
</span>     * Wrapper for setting style properties, also takes single object parameter of multiple styles.
     * @param {String/Object} property The style property to be set, or an object of multiple styles.
     * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
     * @return {Ext.draw.Sprite} this
     */
    setStyle: function() {
        this.el.setStyle.apply(this.el, arguments);
        return this;
    },

<span id='Ext-draw-Sprite-method-addCls'>    /**
</span>     * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.  Note this method
     * is severly limited in VML.
     * @param {String/String[]} className The CSS class to add, or an array of classes
     * @return {Ext.draw.Sprite} this
     */
    addCls: function(obj) {
        this.surface.addCls(this, obj);
        return this;
    },

<span id='Ext-draw-Sprite-method-removeCls'>    /**
</span>     * Removes one or more CSS classes from the element.
     * @param {String/String[]} className The CSS class to remove, or an array of classes.  Note this method
     * is severly limited in VML.
     * @return {Ext.draw.Sprite} this
     */
    removeCls: function(obj) {
        this.surface.removeCls(this, obj);
        return this;
    }
});
</pre>
</body>
</html>