| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000 | <!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 {@link Ext.draw.Component}. * * Most of the Surface methods are abstract and they have a concrete implementation * in {@link Ext.draw.engine.Vml VML} or {@link Ext.draw.engine.Svg SVG} engines. * * A Surface contains methods to render {@link Ext.draw.Sprite sprites}, get bounding * boxes of sprites, add sprites to the canvas, initialize other graphic components, etc. * * ## Adding sprites to surface * * One of the most used methods for this class is the {@link #add} method, to add Sprites to * the surface. For example: * *     drawComponent.surface.add({ *         type: 'circle', *         fill: '#ffc', *         radius: 100, *         x: 100, *         y: 100 *     }); * * The configuration object passed in the `add` method is the same as described in the * {@link Ext.draw.Sprite} class documentation. * * Sprites can also be added to surface by setting their surface config at creation time: * *     var sprite = Ext.create('Ext.draw.Sprite', { *         type: 'circle', *         fill: '#ff0', *         surface: drawComponent.surface, *         radius: 5 *     }); * * In order to properly apply properties and render the sprite we have to * `show` the sprite setting the option `redraw` to `true`: * *     sprite.show(true); * */Ext.define('Ext.draw.Surface', {    /* Begin Definitions */    mixins: {        observable: 'Ext.util.Observable'    },    requires: ['Ext.draw.CompositeSprite'],    uses: ['Ext.draw.engine.Svg', 'Ext.draw.engine.Vml', 'Ext.draw.engine.SvgExporter', 'Ext.draw.engine.ImageExporter'],    separatorRe: /[, ]+/,    statics: {<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 {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'];            var i = 0,                len = enginePriority.length,                surfaceClass;            for (; i < len; i++) {                if (Ext.supports[enginePriority[i]] !== false) {                    return Ext.create('Ext.draw.engine.' + enginePriority[i], config);                }            }            return false;        },        <span id='Ext-draw-Surface-static-method-save'>        /**</span>         * Exports a {@link Ext.draw.Surface surface} in a different format.         * The surface may be exported to an SVG string, using the         * {@link Ext.draw.engine.SvgExporter}. It may also be exported         * as an image using the {@link Ext.draw.engine.ImageExporter ImageExporter}.         * Note that this requires sending data to a remote server to process         * the SVG into an image, see the {@link Ext.draw.engine.ImageExporter} for         * more details.         * @param {Ext.draw.Surface} surface The surface to export.         * @param {Object} [config] The configuration to be passed to the exporter.         * See the export method for the appropriate exporter for the relevant         * configuration options         * @return {Object} See the return types for the appropriate exporter         * @static         */        save: function(surface, config) {            config = config || {};            var exportTypes = {                    'image/png': 'Image',                    'image/jpeg': 'Image',                    'image/svg+xml': 'Svg'                },                prefix = exportTypes[config.type] || 'Svg',                exporter = Ext.draw.engine[prefix + 'Exporter'];                       return exporter.generate(surface, config);                    }    },    /* End Definitions */    // @private    availableAttrs: {        blur: 0,        "clip-rect": "0 0 1e9 1e9",        cursor: "default",        cx: 0,        cy: 0,        'dominant-baseline': 'auto',        fill: "none",        "fill-opacity": 1,        font: '10px "Arial"',        "font-family": '"Arial"',        "font-size": "10",        "font-style": "normal",        "font-weight": 400,        gradient: "",        height: 0,        hidden: false,        href: "http://sencha.com/",        opacity: 1,        path: "M0,0",        radius: 0,        rx: 0,        ry: 0,        scale: "1 1",        src: "",        stroke: "none",        "stroke-dasharray": "",        "stroke-linecap": "butt",        "stroke-linejoin": "butt",        "stroke-miterlimit": 0,        "stroke-opacity": 1,        "stroke-width": 1,        target: "_blank",        text: "",        "text-anchor": "middle",        title: "Ext Draw",        width: 0,        x: 0,        y: 0,        zIndex: 0    },<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-cfg-items'>    /**</span>     * @cfg {Ext.draw.Sprite[]} items     * Array of sprites or sprite config objects to add initially to the surface.     */<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(<span id='Ext-draw-Surface-event-mousedown'>            /**</span>             * @event             * Fires when a mousedown is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mousedown',<span id='Ext-draw-Surface-event-mouseup'>            /**</span>             * @event             * Fires when a mouseup is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mouseup',<span id='Ext-draw-Surface-event-mouseover'>            /**</span>             * @event             * Fires when a mouseover is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mouseover',<span id='Ext-draw-Surface-event-mouseout'>            /**</span>             * @event             * Fires when a mouseout is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mouseout',<span id='Ext-draw-Surface-event-mousemove'>            /**</span>             * @event             * Fires when a mousemove is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mousemove',<span id='Ext-draw-Surface-event-mouseenter'>            /**</span>             * @event             * Fires when a mouseenter is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mouseenter',<span id='Ext-draw-Surface-event-mouseleave'>            /**</span>             * @event             * Fires when a mouseleave is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'mouseleave',<span id='Ext-draw-Surface-event-click'>            /**</span>             * @event             * Fires when a click is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'click',<span id='Ext-draw-Surface-event-dblclick'>            /**</span>             * @event             * Fires when a dblclick is detected within the surface.             * @param {Ext.EventObject} e An object encapsulating the DOM event.             */            'dblclick'        );        me.mixins.observable.constructor.call(me);        me.getId();        me.initGradients();        me.initItems();        if (me.renderTo) {            me.render(me.renderTo);            delete me.renderTo;        }        me.initBackground(config.background);    },    // @private called to initialize components in the surface    // this is dependent on the underlying implementation.    initSurface: Ext.emptyFn,    // @private called to setup the surface to render an item    //this is dependent on the underlying implementation.    renderItem: Ext.emptyFn,    // @private    renderItems: Ext.emptyFn,    // @private    setViewBox: function (x, y, width, height) {        if (isFinite(x) && isFinite(y) && isFinite(width) && isFinite(height)) {            this.viewBox = {x: x, y: y, width: width, height: height};            this.applyViewBox();        }    },<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');     *     * @param {Object} sprite The sprite to add the class to.     * @param {String/String[]} className The CSS class to add, or an array of classes     * @method     */    addCls: Ext.emptyFn,<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');     *     * @param {Object} sprite The sprite to remove the class from.     * @param {String/String[]} className The CSS class to remove, or an array of classes     * @method     */    removeCls: Ext.emptyFn,<span id='Ext-draw-Surface-method-setStyle'>    /**</span>     * Sets CSS style attributes to an element.     *     * For example:     *     *     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,    // @private    initGradients: function() {        if (this.hasOwnProperty('gradients')) {            var gradients = this.gradients,                gLen      = gradients.length,                fn        = this.addGradient,                g;            if (gradients) {                for (g = 0; g < gLen; g++) {                    if (fn.call(this, gradients[g], g, gLen) === false) {                        break;                    }                }            }        }    },    // @private    initItems: function() {        var items = this.items;        this.items = new Ext.draw.CompositeSprite();        this.items.autoDestroy = true;        this.groups = new Ext.draw.CompositeSprite();        if (items) {            this.add(items);        }    },    // @private    initBackground: function(config) {        var me = this,            width = me.width,            height = me.height,            gradientId, gradient, backgroundSprite;        if (Ext.isString(config)) {            config = {                fill : config            };        }        if (config) {            if (config.gradient) {                gradient = config.gradient;                gradientId = gradient.id;                me.addGradient(gradient);                me.background = me.add({                    type: 'rect',                    x: 0,                    y: 0,                    width: width,                    height: height,                    fill: 'url(#' + gradientId + ')',                    zIndex: -1                });            } else if (config.fill) {                me.background = me.add({                    type: 'rect',                    x: 0,                    y: 0,                    width: width,                    height: height,                    fill: config.fill,                    zIndex: -1                });            } else if (config.image) {                me.background = me.add({                    type: 'image',                    x: 0,                    y: 0,                    width: width,                    height: height,                    src: config.image,                    zIndex: -1                });            }            // prevent me.background to jeopardize me.items.getBBox            me.background.bboxExcluded = true;        }    },<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);     *     * 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.     */    setSize: function(w, h) {        this.applyViewBox();    },    // @private    scrubAttrs: function(sprite) {        var i,            attrs = {},            exclude = {},            sattr = sprite.attr;        for (i in sattr) {            // Narrow down attributes to the main set            if (this.translateAttrs.hasOwnProperty(i)) {                // Translated attr                attrs[this.translateAttrs[i]] = sattr[i];                exclude[this.translateAttrs[i]] = true;            }            else if (this.availableAttrs.hasOwnProperty(i) && !exclude[i]) {                // Passtrhough attr                attrs[i] = sattr[i];            }        }        return attrs;    },    // @private    onClick: function(e) {        this.processEvent('click', e);    },        // @private    onDblClick: function(e) {        this.processEvent('dblclick', e);    },    // @private    onMouseUp: function(e) {        this.processEvent('mouseup', e);    },    // @private    onMouseDown: function(e) {        this.processEvent('mousedown', e);    },    // @private    onMouseOver: function(e) {        this.processEvent('mouseover', e);    },    // @private    onMouseOut: function(e) {        this.processEvent('mouseout', e);    },    // @private    onMouseMove: function(e) {        this.fireEvent('mousemove', e);    },    // @private    onMouseEnter: Ext.emptyFn,    // @private    onMouseLeave: Ext.emptyFn,<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     * 'renderTo' in its config). For more information on how to create gradients in the Chart     * 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'     *            }     *        }     *    });     *     * @param {Object} gradient A gradient config.     * @method     */    addGradient: Ext.emptyFn,<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:     *     *     drawComponent.surface.add({     *         type: 'circle',     *         fill: '#ffc',     *         radius: 100,     *         x: 100,     *         y: 100     *     });     *     * @param {Ext.draw.Sprite[]/Ext.draw.Sprite...} args One or more Sprite objects or configs.     * @return {Ext.draw.Sprite[]/Ext.draw.Sprite} The sprites added.     */    add: function() {        var args = Array.prototype.slice.call(arguments),            sprite,            index,            hasMultipleArgs = args.length > 1,            items,            results,            i,            ln,            item;                    if (hasMultipleArgs || Ext.isArray(args[0])) {            items = hasMultipleArgs ? args : args[0];            results = [];            for (i = 0, ln = items.length; i < ln; i++) {                item = items[i];                item = this.add(item);                results.push(item);            }            return results;        }        sprite = this.prepareItems(args[0], true)[0];        this.insertByZIndex(sprite);        this.onAdd(sprite);        return sprite;    },<span id='Ext-draw-Surface-method-insertByZIndex'>    /**</span>     * @private     * 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     */    insertByZIndex: function(sprite) {        var me = this,            sprites = me.items.items,            len = sprites.length,            ceil = Math.ceil,            zIndex = sprite.attr.zIndex,            idx = len,            high = idx - 1,            low = 0,            otherZIndex;        if (me.orderSpritesByZIndex && len && zIndex < sprites[high].attr.zIndex) {            // Find the target index via a binary search for speed            while (low <= high) {                idx = ceil((low + high) / 2);                otherZIndex = sprites[idx].attr.zIndex;                if (otherZIndex > zIndex) {                    high = idx - 1;                }                else if (otherZIndex < zIndex) {                    low = idx + 1;                }                else {                    break;                }            }            // Step forward to the end of a sequence of the same or lower z-index            while (idx < len && sprites[idx].attr.zIndex <= zIndex) {                idx++;            }        }        me.items.insert(idx, sprite);        return idx;    },    onAdd: function(sprite) {        var group = sprite.group,            draggable = sprite.draggable,            groups, ln, i;        if (group) {            groups = [].concat(group);            ln = groups.length;            for (i = 0; i < ln; i++) {                group = groups[i];                this.getGroup(group).add(sprite);            }            delete sprite.group;        }        if (draggable) {            sprite.initDraggable();        }    },<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();     *     * @param {Ext.draw.Sprite} sprite     * @param {Boolean} destroySprite     */    remove: function(sprite, destroySprite) {        if (sprite) {            this.items.remove(sprite);            var groups = [].concat(this.groups.items),                gLen   = groups.length,                g;            for (g = 0; g < gLen; g++) {                groups[g].remove(sprite);            }            sprite.onRemove();            if (destroySprite === true) {                sprite.destroy();            }        }    },<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();     *     * @param {Boolean} destroySprites Whether to destroy all sprites when removing them.     */    removeAll: function(destroySprites) {        var items = this.items.items,            ln = items.length,            i;        for (i = ln - 1; i > -1; i--) {            this.remove(items[i], destroySprites);        }    },    onRemove: Ext.emptyFn,    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 || 1, // Avoid problems in division            height = me.height || 1,            viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,            relativeHeight, relativeWidth, size;        if (viewBox && (width || height)) {            viewBoxX = viewBox.x;            viewBoxY = viewBox.y;            viewBoxWidth = viewBox.width;            viewBoxHeight = viewBox.height;            relativeHeight = height / viewBoxHeight;            relativeWidth = width / viewBoxWidth;            size = Math.min(relativeWidth, relativeHeight);            if (viewBoxWidth * size < width) {                viewBoxX -= (width - viewBoxWidth * size) / 2 / size;            }            if (viewBoxHeight * size < height) {                viewBoxY -= (height - viewBoxHeight * size) / 2 / size;            }            me.viewBoxShift = {                dx: -viewBoxX,                dy: -viewBoxY,                scale: size            };                        if (me.background) {                me.background.setAttributes(Ext.apply({}, {                    x: viewBoxX,                    y: viewBoxY,                    width: width / size,                    height: height / size                }, { hidden: false }), true);            }        } else {            if (me.background && width && height) {                me.background.setAttributes(Ext.apply({x: 0, y: 0, width: width, height: height}, { hidden: false }), true);            }        }    },    getBBox: function (sprite, isWithoutTransform) {        var realPath = this["getPath" + sprite.type](sprite);        if (isWithoutTransform) {            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);            return sprite.bbox.plain;        }        if (sprite.dirtyTransform) {            this.applyTransformations(sprite, true);        }        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));        return sprite.bbox.transform;    },        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, onlyMatrix) {        if (sprite.type == 'text') {            // TODO: getTextBBox function always take matrix into account no matter whether `isWithoutTransform` is true. Fix that.            sprite.bbox.transform = 0;            this.transform(sprite, false);        }        sprite.dirtyTransform = false;                var me = this,            attr = sprite.attr;        if (attr.translation.x != null || attr.translation.y != null) {            me.translate(sprite);        }        if (attr.scaling.x != null || attr.scaling.y != null) {            me.scale(sprite);        }        if (attr.rotation.degrees != null) {            me.rotate(sprite);        }                sprite.bbox.transform = 0;        this.transform(sprite, onlyMatrix);        sprite.transformations = [];    },    // @private    rotate: function (sprite) {        var bbox,            deg = sprite.attr.rotation.degrees,            centerX = sprite.attr.rotation.x,            centerY = sprite.attr.rotation.y;        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {            bbox = this.getBBox(sprite, true);            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;        }        sprite.transformations.push({            type: "rotate",            degrees: deg,            x: centerX,            y: centerY        });    },    // @private    translate: function(sprite) {        var x = sprite.attr.translation.x || 0,            y = sprite.attr.translation.y || 0;        sprite.transformations.push({            type: "translate",            x: x,            y: y        });    },    // @private    scale: function(sprite) {        var bbox,            x = sprite.attr.scaling.x || 1,            y = sprite.attr.scaling.y || 1,            centerX = sprite.attr.scaling.centerX,            centerY = sprite.attr.scaling.centerY;        if (!Ext.isNumber(centerX) || !Ext.isNumber(centerY)) {            bbox = this.getBBox(sprite, true);            centerX = !Ext.isNumber(centerX) ? bbox.x + bbox.width / 2 : centerX;            centerY = !Ext.isNumber(centerY) ? bbox.y + bbox.height / 2 : centerY;        }        sprite.transformations.push({            type: "scale",            x: x,            y: y,            centerX: centerX,            centerY: centerY        });    },    // @private    rectPath: function (x, y, w, h, r) {        if (r) {            return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];        }        return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];    },    // @private    ellipsePath: function (x, y, rx, ry) {        if (ry == null) {            ry = rx;        }        return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];    },    // @private    getPathpath: function (el) {        return el.attr.path;    },    // @private    getPathcircle: function (el) {        var a = el.attr;        return this.ellipsePath(a.x, a.y, a.radius, a.radius);    },    // @private    getPathellipse: function (el) {        var a = el.attr;        return this.ellipsePath(a.x, a.y,                                a.radiusX || (a.width / 2) || 0,                                a.radiusY || (a.height / 2) || 0);    },    // @private    getPathrect: function (el) {        var a = el.attr;        return this.rectPath(a.x || 0, a.y || 0, a.width || 0, a.height || 0, a.r || 0);    },    // @private    getPathimage: function (el) {        var a = el.attr;        return this.rectPath(a.x || 0, a.y || 0, a.width, a.height);    },    // @private    getPathtext: function (el) {        var bbox = this.getBBoxText(el);        return this.rectPath(bbox.x, bbox.y, bbox.width, bbox.height);    },    createGroup: function(id) {        var group = this.groups.get(id);        if (!group) {            group = new Ext.draw.CompositeSprite({                surface: this            });            group.id = id || Ext.id(null, 'ext-surface-group-');            this.groups.add(group);        }        return group;    },<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');     *     * @param {String} id The unique identifier of the group.     * @return {Object} The {@link Ext.draw.CompositeSprite}.     */    getGroup: function(id) {        var group;        if (typeof id == "string") {            group = this.groups.get(id);            if (!group) {                group = this.createGroup(id);            }        } else {            group = id;        }        return group;    },    // @private    prepareItems: function(items, applyDefaults) {        items = [].concat(items);        // Make sure defaults are applied and item is initialized        var item, i, ln;        for (i = 0, ln = items.length; i < ln; i++) {            item = items[i];            if (!(item instanceof Ext.draw.Sprite)) {                // Temporary, just take in configs...                item.surface = this;                items[i] = this.createItem(item);            } else {                item.surface = this;            }        }        return items;    },<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');     *     * @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>     * Retrieves the id of this component.     * Will autogenerate an id if one has not already been set.     */    getId: function() {        return this.id || (this.id = Ext.id(null, 'ext-surface-'));    },<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.     *     * For example:     *     *      drawComponent.surface.destroy();     */    destroy: function() {        var me = this;        delete me.domRef;        if (me.background) {            me.background.destroy();        }        me.removeAll(true);        Ext.destroy(me.groups.items);    }});</pre></body></html>
 |