    /** * @class Ext.draw.engine.Svg * @extends Ext.draw.Surface * * SVG engine. */Ext.define('Ext.draw.engine.Svg', {    extend: 'Ext.draw.Surface',    requires: ['Ext.draw.engine.SvgContext'],    isSVG: true,     config: {        /**         * Nothing needs to be done in high precision mode.         */        highPrecision: false    },     getElementConfig: function () {        return {            reference: 'element',            style: {                position: 'absolute'            },            children: [                {                    reference: 'innerElement',                    style: {                        width: '100%',                        height: '100%',                        position: 'relative'                    },                    children: [                        {                            tag: 'svg',                            reference: 'svgElement',                            namespace: "http://www.w3.org/2000/svg",                            width: '100%',                            height: '100%',                            version: 1.1                        }                    ]                }            ]        };    },     constructor: function (config) {        var me = this;        me.callParent([config]);        me.mainGroup = me.createSvgNode("g");        me.defsElement = me.createSvgNode("defs");        // me.svgElement is assigned in element creation of Ext.Component.         me.svgElement.appendChild(me.mainGroup);        me.svgElement.appendChild(me.defsElement);        me.ctx = new Ext.draw.engine.SvgContext(me);    },     /**     * Creates a DOM element under the SVG namespace of the given type.     * @param {String} type The type of the SVG DOM element.     * @return {*} The created element.     */    createSvgNode: function (type) {        var node = document.createElementNS("http://www.w3.org/2000/svg", type);        return Ext.get(node);    },     /**     * @private     * Returns the SVG DOM element at the given position.     * If it does not already exist or is a different element tag,     * it will be created and inserted into the DOM.     * @param {Ext.dom.Element} group The parent DOM element.     * @param {String} tag The SVG element tag.     * @param {Number} position The position of the element in the DOM.     * @return {Ext.dom.Element} The SVG element.     */    getSvgElement: function (group, tag, position) {        var childNodes = group.dom.childNodes,            length = childNodes.length,            element;         if (position < length) {            element = childNodes[position];            if (element.tagName === tag) {                return Ext.get(element);            } else {                Ext.destroy(element);            }        } else if (position > length) {            Ext.raise("Invalid position.");        }         element = Ext.get(this.createSvgNode(tag));        if (position === 0) {            group.insertFirst(element);        } else {            element.insertAfter(Ext.fly(childNodes[position - 1]));        }        element.cache = {};         return element;    },     /**     * @private     * Applies attributes to the given element.     * @param {Ext.dom.Element} element The DOM element to be applied.     * @param {Object} attributes The attributes to apply to the element.     */    setElementAttributes: function (element, attributes) {        var dom = element.dom,            cache = element.cache,            name, value;        for (name in attributes) {            value = attributes[name];            if (cache[name] !== value) {                cache[name] = value;                dom.setAttribute(name, value);            }        }    },     /**     * @private     * Gets the next reference element under the SVG 'defs' tag.     * @param {String} tagName The type of reference element.     * @return {Ext.dom.Element} The reference element.     */    getNextDef: function (tagName) {        return this.getSvgElement(this.defsElement, tagName, this.defsPosition++);    },     /**     * @inheritdoc     */    clearTransform: function () {        var me = this;        me.mainGroup.set({transform: me.matrix.toSvg()});    },     /**     * @inheritdoc     */    clear: function () {        this.ctx.clear();        this.removeSurplusDefs();        this.defsPosition = 0;    },     removeSurplusDefs: function () {        var defsElement = this.defsElement,            defs = defsElement.dom.childNodes,            ln = defs.length,            i;         for (i = ln - 1; i > this.defsPosition; i--) {            defsElement.removeChild(defs[i]);        }    },     /**     * @inheritdoc     */    renderSprite: function (sprite) {        var me = this,            rect = me.getRect(),            ctx = me.ctx;         // This check is simplistic, but should result in a better performance         // compared to !sprite.isVisible() when most surface sprites are visible.         if (sprite.attr.hidden || sprite.attr.globalAlpha === 0) {            // Create an empty group for each hidden sprite,             // so that when these sprites do become visible,             // they don't need groups to be created and don't             // mess up the previous order of elements in the             // document, i.e. sprites rendered in the next             // frame reuse the same elements they used in the             // previous frame.             ctx.save();            ctx.restore();            return;        }         // Each sprite is rendered in its own group ('g' element),         // returned by the `ctx.save` method.         // Essentially, the group _is_ the sprite.         sprite.element = ctx.save();        sprite.preRender(this);        sprite.useAttributes(ctx, rect);        if (false === sprite.render(this, ctx, [0, 0, rect[2], rect[3]])) {            return false;        }        sprite.setDirty(false);        ctx.restore();    },     /**     * @private     */    toSVG: function (size, surfaces) {        var className = Ext.getClassName(this),            svg, surface, rect, i;         svg = '<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"' +            ' width="' + size.width + '"' +            ' height="' + size.height + '">';         for (i = 0; i < surfaces.length; i++) {            surface = surfaces[i];            if (Ext.getClassName(surface) !== className) {                continue;            }            rect = surface.getRect();            svg += '<g transform="translate(' + rect[0] + ',' + rect[1] + ')">';            svg += this.serializeNode(surface.svgElement.dom);            svg += '</g>';        }        svg += '</svg>';         return svg;    },     flatten: function (size, surfaces) {        var svg = '<?xml version="1.0" standalone="yes"?>';         svg += this.toSVG(size, surfaces);         return {            data: 'data:image/svg+xml;utf8,' + encodeURIComponent(svg),            type: 'svg'        };    },     /**     * @private     * Serializes an SVG DOM element and its children recursively into a string.     * @param {Object} node DOM element to serialize.     * @return {String}     */    serializeNode: function (node) {        var result = '',            i, n, attr, child;        if (node.nodeType === document.TEXT_NODE) {            return node.nodeValue;        }        result += '<' + node.nodeName;        if (node.attributes.length) {            for (i = 0, n = node.attributes.length; i < n; i++) {                attr = node.attributes[i];                result += ' ' + attr.name + '="' + attr.value + '"';            }        }        result += '>';        if (node.childNodes && node.childNodes.length) {            for (i = 0, n = node.childNodes.length; i < n; i++) {                child = node.childNodes[i];                result += this.serializeNode(child);            }        }        result += '</' + node.nodeName + '>';        return result;    },     /**     * Destroys the Canvas element and prepares it for Garbage Collection.     */    destroy: function () {        var me = this;         me.ctx.destroy();        me.mainGroup.destroy();        me.defsElement.destroy();         delete me.mainGroup;        delete me.defsElement;        delete me.ctx;         me.callParent();    },     remove: function (sprite, destroySprite) {        if (sprite && sprite.element) {            // If sprite has an associated SVG element, remove it from the surface.             sprite.element.destroy();            sprite.element = null;        }        this.callParent(arguments);    }});