| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934 | <!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-engine-Vml'>/**</span> * Provides specific methods to draw with VML. */Ext.define('Ext.draw.engine.Vml', {    /* Begin Definitions */    extend: 'Ext.draw.Surface',    requires: ['Ext.draw.Draw', 'Ext.draw.Color', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.Element'],    /* End Definitions */    engine: 'Vml',    map: {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"},    bitesRe: /([clmz]),?([^clmz]*)/gi,    valRe: /-?[^,\s\-]+/g,    fillUrlRe: /^url\(\s*['"]?([^\)]+?)['"]?\s*\)$/i,    pathlike: /^(path|rect)$/,    NonVmlPathRe: /[ahqstv]/ig, // Non-VML Pathing ops    partialPathRe: /[clmz]/g,    fontFamilyRe: /^['"]+|['"]+$/g,    baseVmlCls: Ext.baseCSSPrefix + 'vml-base',    vmlGroupCls: Ext.baseCSSPrefix + 'vml-group',    spriteCls: Ext.baseCSSPrefix + 'vml-sprite',    measureSpanCls: Ext.baseCSSPrefix + 'vml-measure-span',    zoom: 21600,    coordsize: 1000,    coordorigin: '0 0',    zIndexShift: 0,    // VML uses CSS z-index and therefore doesn't need sprites to be kept in zIndex order    orderSpritesByZIndex: false,    // @private    // Convert an SVG standard path into a VML path    path2vml: function (path) {        var me = this,            nonVML = me.NonVmlPathRe,            map = me.map,            val = me.valRe,            zoom = me.zoom,            bites = me.bitesRe,            command = Ext.Function.bind(Ext.draw.Draw.pathToAbsolute, Ext.draw.Draw),            res, pa, p, r, i, ii, j, jj;        if (String(path).match(nonVML)) {            command = Ext.Function.bind(Ext.draw.Draw.path2curve, Ext.draw.Draw);        } else if (!String(path).match(me.partialPathRe)) {            res = String(path).replace(bites, function (all, command, args) {                var vals = [],                    isMove = command.toLowerCase() == "m",                    res = map[command];                args.replace(val, function (value) {                    if (isMove && vals.length === 2) {                        res += vals + map[command == "m" ? "l" : "L"];                        vals = [];                    }                    vals.push(Math.round(value * zoom));                });                return res + vals;            });            return res;        }        pa = command(path);        res = [];        for (i = 0, ii = pa.length; i < ii; i++) {            p = pa[i];            r = pa[i][0].toLowerCase();            if (r == "z") {                r = "x";            }            for (j = 1, jj = p.length; j < jj; j++) {                r += Math.round(p[j] * me.zoom) + (j != jj - 1 ? "," : "");            }            res.push(r);        }        return res.join(" ");    },    // @private - set of attributes which need to be translated from the sprite API to the native browser API    translateAttrs: {        radius: "r",        radiusX: "rx",        radiusY: "ry",        lineWidth: "stroke-width",        fillOpacity: "fill-opacity",        strokeOpacity: "stroke-opacity",        strokeLinejoin: "stroke-linejoin"    },    // @private - Minimun set of defaults for different types of sprites.    minDefaults: {        circle: {            fill: "none",            stroke: null,            "stroke-width": null,            opacity: null,            "fill-opacity": null,            "stroke-opacity": null        },        ellipse: {            cx: 0,            cy: 0,            rx: 0,            ry: 0,            fill: "none",            stroke: null,            "stroke-width": null,            opacity: null,            "fill-opacity": null,            "stroke-opacity": null        },        rect: {            x: 0,            y: 0,            width: 0,            height: 0,            rx: 0,            ry: 0,            fill: "none",            stroke: null,            "stroke-width": null,            opacity: null,            "fill-opacity": null,            "stroke-opacity": null        },        text: {            x: 0,            y: 0,            "text-anchor": "start",            font: '10px "Arial"',            fill: "#000",            stroke: null,            "stroke-width": null,            opacity: null,            "fill-opacity": null,            "stroke-opacity": null        },        path: {            d: "M0,0",            fill: "none",            stroke: null,            "stroke-width": null,            opacity: null,            "fill-opacity": null,            "stroke-opacity": null        },        image: {            x: 0,            y: 0,            width: 0,            height: 0,            preserveAspectRatio: "none",            opacity: null        }    },    // private    onMouseEnter: function (e) {        this.fireEvent("mouseenter", e);    },    // private    onMouseLeave: function (e) {        this.fireEvent("mouseleave", e);    },    // @private - Normalize a delegated single event from the main container to each sprite and sprite group    processEvent: function (name, e) {        var target = e.getTarget(),            surface = this.surface,            sprite;        this.fireEvent(name, e);        sprite = this.items.get(target.id);        if (sprite) {            sprite.fireEvent(name, sprite, e);        }    },    // Create the VML element/elements and append them to the DOM    createSpriteElement: function (sprite) {        var me = this,            attr = sprite.attr,            type = sprite.type,            zoom = me.zoom,            vml = sprite.vml || (sprite.vml = {}),            round = Math.round,            el = (type === 'image') ? me.createNode('image') : me.createNode('shape'),            path, skew, textPath;        el.coordsize = zoom + ' ' + zoom;        el.coordorigin = attr.coordorigin || "0 0";        Ext.get(el).addCls(me.spriteCls);        if (type == "text") {            vml.path = path = me.createNode("path");            path.textpathok = true;            vml.textpath = textPath = me.createNode("textpath");            textPath.on = true;            el.appendChild(textPath);            el.appendChild(path);        }        el.id = sprite.id;        sprite.el = Ext.get(el);        sprite.el.setStyle('zIndex', -me.zIndexShift);        me.el.appendChild(el);        if (type !== 'image') {            skew = me.createNode("skew");            skew.on = true;            el.appendChild(skew);            sprite.skew = skew;        }        sprite.matrix = new Ext.draw.Matrix();        sprite.bbox = {            plain: null,            transform: null        };        this.applyAttrs(sprite);        this.applyTransformations(sprite);                sprite.fireEvent("render", sprite);        return sprite.el;    },    getBBoxText: function (sprite) {        var vml = sprite.vml;        return {            x: vml.X + (vml.bbx || 0) - vml.W / 2,            y: vml.Y - vml.H / 2,            width: vml.W,            height: vml.H        };    },    applyAttrs: function (sprite) {        var me = this,            vml = sprite.vml,            group = sprite.group,            spriteAttr = sprite.attr,            el = sprite.el,            dom = el.dom,            style, name, groups, i, ln, scrubbedAttrs, font, key,            cx, cy, rx, ry;        if (group) {            groups = [].concat(group);            ln = groups.length;            for (i = 0; i < ln; i++) {                group = groups[i];                me.getGroup(group).add(sprite);            }            delete sprite.group;        }        scrubbedAttrs = me.scrubAttrs(sprite) || {};        if (sprite.zIndexDirty) {            me.setZIndex(sprite);        }        // Apply minimum default attributes        Ext.applyIf(scrubbedAttrs, me.minDefaults[sprite.type]);        if (sprite.type == 'image') {            Ext.apply(sprite.attr, {                x: scrubbedAttrs.x,                y: scrubbedAttrs.y,                width: scrubbedAttrs.width,                height: scrubbedAttrs.height            });            el.setStyle({                width: scrubbedAttrs.width + 'px',                height: scrubbedAttrs.height + 'px'            });            dom.src = scrubbedAttrs.src;        }        if (dom.href) {            dom.href = scrubbedAttrs.href;        }        if (dom.title) {            dom.title = scrubbedAttrs.title;        }        if (dom.target) {            dom.target = scrubbedAttrs.target;        }        if (dom.cursor) {            dom.cursor = scrubbedAttrs.cursor;        }        // Change visibility        if (sprite.dirtyHidden) {            (scrubbedAttrs.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);            sprite.dirtyHidden = false;        }        // Update path        if (sprite.dirtyPath) {            if (sprite.type == "circle" || sprite.type == "ellipse") {                cx = scrubbedAttrs.x;                cy = scrubbedAttrs.y;                rx = scrubbedAttrs.rx || scrubbedAttrs.r || 0;                ry = scrubbedAttrs.ry || scrubbedAttrs.r || 0;                dom.path = Ext.String.format("ar{0},{1},{2},{3},{4},{1},{4},{1}",                    Math.round((cx - rx) * me.zoom),                    Math.round((cy - ry) * me.zoom),                    Math.round((cx + rx) * me.zoom),                    Math.round((cy + ry) * me.zoom),                    Math.round(cx * me.zoom));                sprite.dirtyPath = false;            }            else if (sprite.type !== "text" && sprite.type !== 'image') {                sprite.attr.path = scrubbedAttrs.path = me.setPaths(sprite, scrubbedAttrs) || scrubbedAttrs.path;                dom.path = me.path2vml(scrubbedAttrs.path);                sprite.dirtyPath = false;            }        }        // Apply clipping        if ("clip-rect" in scrubbedAttrs) {            me.setClip(sprite, scrubbedAttrs);        }        // Handle text (special handling required)        if (sprite.type == "text") {            me.setTextAttributes(sprite, scrubbedAttrs);        }        // Handle fill and opacity        if (scrubbedAttrs.opacity || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {            me.setFill(sprite, scrubbedAttrs);        }        // Handle stroke (all fills require a stroke element)        if (scrubbedAttrs.stroke || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {            me.setStroke(sprite, scrubbedAttrs);        }        //set styles        style = spriteAttr.style;        if (style) {            el.setStyle(style);        }        sprite.dirty = false;    },    setZIndex: function (sprite) {        var me = this,            zIndex = sprite.attr.zIndex,            shift = me.zIndexShift,            items, iLen, item, i;        if (zIndex < shift) {            // This means bad thing happened.            // The algorithm below will guarantee O(n) time.            items = me.items.items;            iLen = items.length;            for (i = 0; i < iLen; i++) {                if ((zIndex = items[i].attr.zIndex) && zIndex < shift) { // zIndex is no longer useful this case                    shift = zIndex;                }            }            me.zIndexShift = shift;            for (i = 0; i < iLen; i++) {                item = items[i];                if (item.el) {                    item.el.setStyle('zIndex', item.attr.zIndex - shift);                }                item.zIndexDirty = false;            }        } else if (sprite.el) {            sprite.el.setStyle('zIndex', zIndex - shift);            sprite.zIndexDirty = false;        }    },    // Normalize all virtualized types into paths.    setPaths: function (sprite, params) {        var spriteAttr = sprite.attr, thickness = sprite.attr['stroke-width'] || 1;        // Clear bbox cache        sprite.bbox.plain = null;        sprite.bbox.transform = null;        if (sprite.type == 'circle') {            spriteAttr.rx = spriteAttr.ry = params.r;            return Ext.draw.Draw.ellipsePath(sprite);        }        else if (sprite.type == 'ellipse') {            spriteAttr.rx = params.rx;            spriteAttr.ry = params.ry;            return Ext.draw.Draw.ellipsePath(sprite);        }        else if (sprite.type == 'rect') {            spriteAttr.rx = spriteAttr.ry = params.r;            return Ext.draw.Draw.rectPath(sprite);        }        else if (sprite.type == 'path' && spriteAttr.path) {            return Ext.draw.Draw.pathToAbsolute(spriteAttr.path);        }        return false;    },    setFill: function (sprite, params) {        var me = this,            el = sprite.el.dom,            fillEl = el.fill,            newfill = false,            opacity, gradient, fillUrl, rotation, angle;        if (!fillEl) {            // NOT an expando (but it sure looks like one)...            fillEl = el.fill = me.createNode("fill");            newfill = true;        }        if (Ext.isArray(params.fill)) {            params.fill = params.fill[0];        }        if (params.fill == "none") {            fillEl.on = false;        }        else {            if (typeof params.opacity == "number") {                fillEl.opacity = params.opacity;            }            if (typeof params["fill-opacity"] == "number") {                fillEl.opacity = params["fill-opacity"];            }            fillEl.on = true;            if (typeof params.fill == "string") {                fillUrl = params.fill.match(me.fillUrlRe);                if (fillUrl) {                    fillUrl = fillUrl[1];                    // If the URL matches one of the registered gradients, render that gradient                    if (fillUrl.charAt(0) == "#") {                        gradient = me.gradientsColl.getByKey(fillUrl.substring(1));                    }                    if (gradient) {                        // VML angle is offset and inverted from standard, and must be adjusted to match rotation transform                        rotation = params.rotation;                        angle = -(gradient.angle + 270 + (rotation ? rotation.degrees : 0)) % 360;                        // IE will flip the angle at 0 degrees...                        if (angle === 0) {                            angle = 180;                        }                        fillEl.angle = angle;                        fillEl.type = "gradient";                        fillEl.method = "sigma";                        if (fillEl.colors) {                            fillEl.colors.value = gradient.colors;                        } else {                            fillEl.colors = gradient.colors;                        }                    }                    // Otherwise treat it as an image                    else {                        fillEl.src = fillUrl;                        fillEl.type = "tile";                    }                }                else {                    fillEl.color = Ext.draw.Color.toHex(params.fill);                    fillEl.src = "";                    fillEl.type = "solid";                }            }        }        if (newfill) {            el.appendChild(fillEl);        }    },    setStroke: function (sprite, params) {        var me = this,            el = sprite.el.dom,            strokeEl = sprite.strokeEl,            newStroke = false,            width, opacity;        if (!strokeEl) {            strokeEl = sprite.strokeEl = me.createNode("stroke");            newStroke = true;        }        if (Ext.isArray(params.stroke)) {            params.stroke = params.stroke[0];        }        if (!params.stroke || params.stroke == "none" || params.stroke == 0 || params["stroke-width"] == 0) {            strokeEl.on = false;        }        else {            strokeEl.on = true;            if (params.stroke && !params.stroke.match(me.fillUrlRe)) {                // VML does NOT support a gradient stroke :(                strokeEl.color = Ext.draw.Color.toHex(params.stroke);            }            strokeEl.dashstyle = params["stroke-dasharray"] ? "dash" : "solid";            strokeEl.joinstyle = params["stroke-linejoin"];            strokeEl.endcap = params["stroke-linecap"] || "round";            strokeEl.miterlimit = params["stroke-miterlimit"] || 8;            width = parseFloat(params["stroke-width"] || 1) * 0.75;            opacity = params["stroke-opacity"] || 1;            // VML Does not support stroke widths under 1, so we're going to fiddle with stroke-opacity instead.            if (Ext.isNumber(width) && width < 1) {                strokeEl.weight = 1;                strokeEl.opacity = opacity * width;            }            else {                strokeEl.weight = width;                strokeEl.opacity = opacity;            }        }        if (newStroke) {            el.appendChild(strokeEl);        }    },    setClip: function (sprite, params) {        var me = this,            el = sprite.el,            clipEl = sprite.clipEl,            rect = String(params["clip-rect"]).split(me.separatorRe);        if (!clipEl) {            clipEl = sprite.clipEl = me.el.insertFirst(Ext.getDoc().dom.createElement("div"));            clipEl.addCls(Ext.baseCSSPrefix + 'vml-sprite');        }        if (rect.length == 4) {            rect[2] = +rect[2] + (+rect[0]);            rect[3] = +rect[3] + (+rect[1]);            clipEl.setStyle("clip", Ext.String.format("rect({1}px {2}px {3}px {0}px)", rect[0], rect[1], rect[2], rect[3]));            clipEl.setSize(me.el.width, me.el.height);        }        else {            clipEl.setStyle("clip", "");        }    },    setTextAttributes: function (sprite, params) {        var me = this,            vml = sprite.vml,            textStyle = vml.textpath.style,            spanCacheStyle = me.span.style,            zoom = me.zoom,            round = Math.round,            fontObj = {                fontSize: "font-size",                fontWeight: "font-weight",                fontStyle: "font-style"            },            fontProp,            paramProp;        if (sprite.dirtyFont) {            if (params.font) {                textStyle.font = spanCacheStyle.font = params.font;            }            if (params["font-family"]) {                textStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(me.fontFamilyRe, "") + '"';                spanCacheStyle.fontFamily = params["font-family"];            }            for (fontProp in fontObj) {                paramProp = params[fontObj[fontProp]];                if (paramProp) {                    textStyle[fontProp] = spanCacheStyle[fontProp] = paramProp;                }            }            me.setText(sprite, params.text);            if (vml.textpath.string) {                me.span.innerHTML = String(vml.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br/>");            }            vml.W = me.span.offsetWidth;            vml.H = me.span.offsetHeight + 2; // TODO handle baseline differences and offset in VML Textpath            // text-anchor emulation            if (params["text-anchor"] == "middle") {                textStyle["v-text-align"] = "center";            }            else if (params["text-anchor"] == "end") {                textStyle["v-text-align"] = "right";                vml.bbx = -Math.round(vml.W / 2);            }            else {                textStyle["v-text-align"] = "left";                vml.bbx = Math.round(vml.W / 2);            }        }        vml.X = params.x;        vml.Y = params.y;        vml.path.v = Ext.String.format("m{0},{1}l{2},{1}", Math.round(vml.X * zoom), Math.round(vml.Y * zoom), Math.round(vml.X * zoom) + 1);        // Clear bbox cache        sprite.bbox.plain = null;        sprite.bbox.transform = null;        sprite.dirtyFont = false;    },    setText: function (sprite, text) {        sprite.vml.textpath.string = Ext.htmlDecode(text);    },    hide: function () {        this.el.hide();    },    show: function () {        this.el.show();    },    hidePrim: function (sprite) {        sprite.el.addCls(Ext.baseCSSPrefix + 'hide-visibility');    },    showPrim: function (sprite) {        sprite.el.removeCls(Ext.baseCSSPrefix + 'hide-visibility');    },    setSize: function (width, height) {        var me = this;        width = width || me.width;        height = height || me.height;        me.width = width;        me.height = height;        if (me.el) {            // Size outer div            if (width != undefined) {                me.el.setWidth(width);            }            if (height != undefined) {                me.el.setHeight(height);            }        }        me.callParent(arguments);    },<span id='Ext-draw-engine-Vml-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,            items,            iLen,            i;                me.callParent();        if (viewBox && (width || height)) {            items = me.items.items;            iLen = items.length;            for (i = 0; i < iLen; i++) {                me.applyTransformations(items[i]);            }        }    },    onAdd: function (item) {        this.callParent(arguments);        if (this.el) {            this.renderItem(item);        }    },    onRemove: function (sprite) {        if (sprite.el) {            sprite.el.remove();            delete sprite.el;        }        this.callParent(arguments);    },    render: function (container) {        var me = this,            doc = Ext.getDoc().dom,            el;        // VML Node factory method (createNode)        if (!me.createNode) {            try {                if (!doc.namespaces.rvml) {                    doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");                }                me.createNode = function (tagName) {                    return doc.createElement("<rvml:" + tagName + ' class="rvml">');                };            } catch (e) {                me.createNode = function (tagName) {                    return doc.createElement("<" + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');                };            }        }        if (!me.el) {            el = doc.createElement("div");            me.el = Ext.get(el);            me.el.addCls(me.baseVmlCls);            // Measuring span (offscrren)            me.span = doc.createElement("span");            Ext.get(me.span).addCls(me.measureSpanCls);            el.appendChild(me.span);            me.el.setSize(me.width || 0, me.height || 0);            container.appendChild(el);            me.el.on({                scope: me,                mouseup: me.onMouseUp,                mousedown: me.onMouseDown,                mouseover: me.onMouseOver,                mouseout: me.onMouseOut,                mousemove: me.onMouseMove,                mouseenter: me.onMouseEnter,                mouseleave: me.onMouseLeave,                click: me.onClick,                dblclick: me.onDblClick            });        }        me.renderAll();    },    renderAll: function () {        this.items.each(this.renderItem, this);    },    redraw: function (sprite) {        sprite.dirty = true;        this.renderItem(sprite);    },    renderItem: function (sprite) {        // Does the surface element exist?        if (!this.el) {            return;        }        // Create sprite element if necessary        if (!sprite.el) {            this.createSpriteElement(sprite);        }        if (sprite.dirty) {            this.applyAttrs(sprite);            if (sprite.dirtyTransform) {                this.applyTransformations(sprite);            }        }    },    rotationCompensation: function (deg, dx, dy) {        var matrix = new Ext.draw.Matrix();        matrix.rotate(-deg, 0.5, 0.5);        return {            x: matrix.x(dx, dy),            y: matrix.y(dx, dy)        };    },    transform: function (sprite, matrixOnly) {        var me = this,            bbox = me.getBBox(sprite, true),            cx = bbox.x + bbox.width * 0.5,            cy = bbox.y + bbox.height * 0.5,            matrix = new Ext.draw.Matrix(),            transforms = sprite.transformations,            transformsLength = transforms.length,            i = 0,            deltaDegrees = 0,            deltaScaleX = 1,            deltaScaleY = 1,            flip = "",            el = sprite.el,            dom = el.dom,            domStyle = dom.style,            zoom = me.zoom,            skew = sprite.skew,            shift = me.viewBoxShift,            deltaX, deltaY, transform, type, compensate, y, fill, newAngle, zoomScaleX, zoomScaleY, newOrigin, offset;        for (; i < transformsLength; i++) {            transform = transforms[i];            type = transform.type;            if (type == "translate") {                matrix.translate(transform.x, transform.y);            }            else if (type == "rotate") {                matrix.rotate(transform.degrees, transform.x, transform.y);                deltaDegrees += transform.degrees;            }            else if (type == "scale") {                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);                deltaScaleX *= transform.x;                deltaScaleY *= transform.y;            }        }        sprite.matrix = matrix.clone();        if (matrixOnly) {            return;        }        if (shift) {            matrix.prepend(shift.scale, 0, 0, shift.scale, shift.dx * shift.scale, shift.dy * shift.scale);        }        // Hide element while we transform        if (sprite.type != "image" && skew) {            skew.origin = "0,0";            // matrix transform via VML skew            skew.matrix = matrix.toString();            // skew.offset = '32767,1' OK            // skew.offset = '32768,1' Crash            // M$, R U kidding??            offset = matrix.offset();            if (offset[0] > 32767) {                offset[0] = 32767;            } else if (offset[0] < -32768) {                offset[0] = -32768;            }            if (offset[1] > 32767) {                offset[1] = 32767;            } else if (offset[1] < -32768) {                offset[1] = -32768;            }            skew.offset = offset;        }        else {            domStyle.filter = matrix.toFilter();            domStyle.left = Math.min(                matrix.x(bbox.x, bbox.y),                matrix.x(bbox.x + bbox.width, bbox.y),                matrix.x(bbox.x, bbox.y + bbox.height),                matrix.x(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px';            domStyle.top = Math.min(                matrix.y(bbox.x, bbox.y),                matrix.y(bbox.x + bbox.width, bbox.y),                matrix.y(bbox.x, bbox.y + bbox.height),                matrix.y(bbox.x + bbox.width, bbox.y + bbox.height)) + 'px';        }    },    createItem: function (config) {        return Ext.create('Ext.draw.Sprite', config);    },    getRegion: function () {        return this.el.getRegion();    },    addCls: function (sprite, className) {        if (sprite && sprite.el) {            sprite.el.addCls(className);        }    },    removeCls: function (sprite, className) {        if (sprite && sprite.el) {            sprite.el.removeCls(className);        }    },<span id='Ext-draw-engine-Vml-method-addGradient'>    /**</span>     * Adds a definition to this Surface for a linear gradient. We convert the gradient definition     * to its corresponding VML attributes and store it for later use by individual sprites.     * @param {Object} gradient     */    addGradient: function (gradient) {        var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')),            colors = [],            stops = Ext.create('Ext.util.MixedCollection'),            keys,            items,            iLen,            key,            item,            i;        // Build colors string        stops.addAll(gradient.stops);        stops.sortByKey("ASC", function (a, b) {            a = parseInt(a, 10);            b = parseInt(b, 10);            return a > b ? 1 : (a < b ? -1 : 0);        });        keys = stops.keys;        items = stops.items;        iLen = keys.length;        for (i = 0; i < iLen; i++) {            key = keys[i];            item = items[i];            colors.push(key + '% ' + item.color);        }        gradients.add(gradient.id, {            colors: colors.join(","),            angle: gradient.angle        });    },    destroy: function () {        var me = this;        me.callParent(arguments);        if (me.el) {            me.el.remove();        }        delete me.el;    }});</pre></body></html>
 |