PageRenderTime 38ms CodeModel.GetById 26ms app.highlight 7ms RepoModel.GetById 0ms app.codeStats 0ms

/ext-4.0.7/docs/source/Vml.html

https://bitbucket.org/srogerf/javascript
HTML | 963 lines | 875 code | 88 blank | 0 comment | 0 complexity | fd6bd3633651427987f31f8277c0fb20 MD5 | raw file
  1<!DOCTYPE html>
  2<html>
  3<head>
  4  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5  <title>The source code</title>
  6  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8  <style type="text/css">
  9    .highlight { display: block; background-color: #ddd; }
 10  </style>
 11  <script type="text/javascript">
 12    function highlight() {
 13      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
 14    }
 15  </script>
 16</head>
 17<body onload="prettyPrint(); highlight();">
 18  <pre class="prettyprint lang-js"><span id='Ext-draw-engine-Vml'>/**
 19</span> * @class Ext.draw.engine.Vml
 20 * @extends Ext.draw.Surface
 21 * Provides specific methods to draw with VML.
 22 */
 23
 24Ext.define('Ext.draw.engine.Vml', {
 25
 26    /* Begin Definitions */
 27
 28    extend: 'Ext.draw.Surface',
 29
 30    requires: ['Ext.draw.Draw', 'Ext.draw.Color', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.Element'],
 31
 32    /* End Definitions */
 33
 34    engine: 'Vml',
 35
 36    map: {M: &quot;m&quot;, L: &quot;l&quot;, C: &quot;c&quot;, Z: &quot;x&quot;, m: &quot;t&quot;, l: &quot;r&quot;, c: &quot;v&quot;, z: &quot;x&quot;},
 37    bitesRe: /([clmz]),?([^clmz]*)/gi,
 38    valRe: /-?[^,\s-]+/g,
 39    fillUrlRe: /^url\(\s*['&quot;]?([^\)]+?)['&quot;]?\s*\)$/i,
 40    pathlike: /^(path|rect)$/,
 41    NonVmlPathRe: /[ahqstv]/ig, // Non-VML Pathing ops
 42    partialPathRe: /[clmz]/g,
 43    fontFamilyRe: /^['&quot;]+|['&quot;]+$/g,
 44    baseVmlCls: Ext.baseCSSPrefix + 'vml-base',
 45    vmlGroupCls: Ext.baseCSSPrefix + 'vml-group',
 46    spriteCls: Ext.baseCSSPrefix + 'vml-sprite',
 47    measureSpanCls: Ext.baseCSSPrefix + 'vml-measure-span',
 48    zoom: 21600,
 49    coordsize: 1000,
 50    coordorigin: '0 0',
 51
 52    // VML uses CSS z-index and therefore doesn't need sprites to be kept in zIndex order
 53    orderSpritesByZIndex: false,
 54
 55    // @private
 56    // Convert an SVG standard path into a VML path
 57    path2vml: function (path) {
 58        var me = this,
 59            nonVML =  me.NonVmlPathRe,
 60            map = me.map,
 61            val = me.valRe,
 62            zoom = me.zoom,
 63            bites = me.bitesRe,
 64            command = Ext.Function.bind(Ext.draw.Draw.pathToAbsolute, Ext.draw.Draw),
 65            res, pa, p, r, i, ii, j, jj;
 66        if (String(path).match(nonVML)) {
 67            command = Ext.Function.bind(Ext.draw.Draw.path2curve, Ext.draw.Draw);
 68        } else if (!String(path).match(me.partialPathRe)) {
 69            res = String(path).replace(bites, function (all, command, args) {
 70                var vals = [],
 71                    isMove = command.toLowerCase() == &quot;m&quot;,
 72                    res = map[command];
 73                args.replace(val, function (value) {
 74                    if (isMove &amp;&amp; vals[length] == 2) {
 75                        res += vals + map[command == &quot;m&quot; ? &quot;l&quot; : &quot;L&quot;];
 76                        vals = [];
 77                    }
 78                    vals.push(Math.round(value * zoom));
 79                });
 80                return res + vals;
 81            });
 82            return res;
 83        }
 84        pa = command(path);
 85        res = [];
 86        for (i = 0, ii = pa.length; i &lt; ii; i++) {
 87            p = pa[i];
 88            r = pa[i][0].toLowerCase();
 89            if (r == &quot;z&quot;) {
 90                r = &quot;x&quot;;
 91            }
 92            for (j = 1, jj = p.length; j &lt; jj; j++) {
 93                r += Math.round(p[j] * me.zoom) + (j != jj - 1 ? &quot;,&quot; : &quot;&quot;);
 94            }
 95            res.push(r);
 96        }
 97        return res.join(&quot; &quot;);
 98    },
 99
100    // @private - set of attributes which need to be translated from the sprite API to the native browser API
101    translateAttrs: {
102        radius: &quot;r&quot;,
103        radiusX: &quot;rx&quot;,
104        radiusY: &quot;ry&quot;,
105        lineWidth: &quot;stroke-width&quot;,
106        fillOpacity: &quot;fill-opacity&quot;,
107        strokeOpacity: &quot;stroke-opacity&quot;,
108        strokeLinejoin: &quot;stroke-linejoin&quot;
109    },
110
111    // @private - Minimun set of defaults for different types of sprites.
112    minDefaults: {
113        circle: {
114            fill: &quot;none&quot;,
115            stroke: null,
116            &quot;stroke-width&quot;: null,
117            opacity: null,
118            &quot;fill-opacity&quot;: null,
119            &quot;stroke-opacity&quot;: null
120        },
121        ellipse: {
122            cx: 0,
123            cy: 0,
124            rx: 0,
125            ry: 0,
126            fill: &quot;none&quot;,
127            stroke: null,
128            &quot;stroke-width&quot;: null,
129            opacity: null,
130            &quot;fill-opacity&quot;: null,
131            &quot;stroke-opacity&quot;: null
132        },
133        rect: {
134            x: 0,
135            y: 0,
136            width: 0,
137            height: 0,
138            rx: 0,
139            ry: 0,
140            fill: &quot;none&quot;,
141            stroke: null,
142            &quot;stroke-width&quot;: null,
143            opacity: null,
144            &quot;fill-opacity&quot;: null,
145            &quot;stroke-opacity&quot;: null
146        },
147        text: {
148            x: 0,
149            y: 0,
150            &quot;text-anchor&quot;: &quot;start&quot;,
151            font: '10px &quot;Arial&quot;',
152            fill: &quot;#000&quot;,
153            stroke: null,
154            &quot;stroke-width&quot;: null,
155            opacity: null,
156            &quot;fill-opacity&quot;: null,
157            &quot;stroke-opacity&quot;: null
158        },
159        path: {
160            d: &quot;M0,0&quot;,
161            fill: &quot;none&quot;,
162            stroke: null,
163            &quot;stroke-width&quot;: null,
164            opacity: null,
165            &quot;fill-opacity&quot;: null,
166            &quot;stroke-opacity&quot;: null
167        },
168        image: {
169            x: 0,
170            y: 0,
171            width: 0,
172            height: 0,
173            preserveAspectRatio: &quot;none&quot;,
174            opacity: null
175        }
176    },
177
178    // private
179    onMouseEnter: function(e) {
180        this.fireEvent(&quot;mouseenter&quot;, e);
181    },
182
183    // private
184    onMouseLeave: function(e) {
185        this.fireEvent(&quot;mouseleave&quot;, e);
186    },
187
188    // @private - Normalize a delegated single event from the main container to each sprite and sprite group
189    processEvent: function(name, e) {
190        var target = e.getTarget(),
191            surface = this.surface,
192            sprite;
193        this.fireEvent(name, e);
194        sprite = this.items.get(target.id);
195        if (sprite) {
196            sprite.fireEvent(name, sprite, e);
197        }
198    },
199
200    // Create the VML element/elements and append them to the DOM
201    createSpriteElement: function(sprite) {
202        var me = this,
203            attr = sprite.attr,
204            type = sprite.type,
205            zoom = me.zoom,
206            vml = sprite.vml || (sprite.vml = {}),
207            round = Math.round,
208            el = me.createNode('shape'),
209            path, skew, textPath;
210
211        el.coordsize = zoom + ' ' + zoom;
212        el.coordorigin = attr.coordorigin || &quot;0 0&quot;;
213        Ext.get(el).addCls(me.spriteCls);
214        if (type == &quot;text&quot;) {
215            vml.path = path = me.createNode(&quot;path&quot;);
216            path.textpathok = true;
217            vml.textpath = textPath = me.createNode(&quot;textpath&quot;);
218            textPath.on = true;
219            el.appendChild(textPath);
220            el.appendChild(path);
221        }
222        el.id = sprite.id;
223        sprite.el = Ext.get(el);
224        me.el.appendChild(el);
225        if (type !== 'image') {
226            skew = me.createNode(&quot;skew&quot;);
227            skew.on = true;
228            el.appendChild(skew);
229            sprite.skew = skew;
230        }
231        sprite.matrix = Ext.create('Ext.draw.Matrix');
232        sprite.bbox = {
233            plain: null,
234            transform: null
235        };
236        sprite.fireEvent(&quot;render&quot;, sprite);
237        return sprite.el;
238    },
239
240    // @private - Get bounding box for the sprite.  The Sprite itself has the public method.
241    getBBox: function (sprite, isWithoutTransform) {
242        var realPath = this[&quot;getPath&quot; + sprite.type](sprite);
243        if (isWithoutTransform) {
244            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);
245            return sprite.bbox.plain;
246        }
247        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));
248        return sprite.bbox.transform;
249    },
250
251    getBBoxText: function (sprite) {
252        var vml = sprite.vml;
253        return {
254            x: vml.X + (vml.bbx || 0) - vml.W / 2,
255            y: vml.Y - vml.H / 2,
256            width: vml.W,
257            height: vml.H
258        };
259    },
260
261    applyAttrs: function (sprite) {
262        var me = this,
263            vml = sprite.vml,
264            group = sprite.group,
265            spriteAttr = sprite.attr,
266            el = sprite.el,
267            dom = el.dom,
268            style, name, groups, i, ln, scrubbedAttrs, font, key, bbox;
269
270        if (group) {
271            groups = [].concat(group);
272            ln = groups.length;
273            for (i = 0; i &lt; ln; i++) {
274                group = groups[i];
275                me.getGroup(group).add(sprite);
276            }
277            delete sprite.group;
278        }
279        scrubbedAttrs = me.scrubAttrs(sprite) || {};
280
281        if (sprite.zIndexDirty) {
282            me.setZIndex(sprite);
283        }
284
285        // Apply minimum default attributes
286        Ext.applyIf(scrubbedAttrs, me.minDefaults[sprite.type]);
287
288        if (dom.href) {
289            dom.href = scrubbedAttrs.href;
290        }
291        if (dom.title) {
292            dom.title = scrubbedAttrs.title;
293        }
294        if (dom.target) {
295            dom.target = scrubbedAttrs.target;
296        }
297        if (dom.cursor) {
298            dom.cursor = scrubbedAttrs.cursor;
299        }
300
301        // Change visibility
302        if (sprite.dirtyHidden) {
303            (scrubbedAttrs.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
304            sprite.dirtyHidden = false;
305        }
306
307        // Update path
308        if (sprite.dirtyPath) {
309            if (sprite.type == &quot;circle&quot; || sprite.type == &quot;ellipse&quot;) {
310                var cx = scrubbedAttrs.x,
311                    cy = scrubbedAttrs.y,
312                    rx = scrubbedAttrs.rx || scrubbedAttrs.r || 0,
313                    ry = scrubbedAttrs.ry || scrubbedAttrs.r || 0;
314                dom.path = Ext.String.format(&quot;ar{0},{1},{2},{3},{4},{1},{4},{1}&quot;,
315                            Math.round((cx - rx) * me.zoom),
316                            Math.round((cy - ry) * me.zoom),
317                            Math.round((cx + rx) * me.zoom),
318                            Math.round((cy + ry) * me.zoom),
319                            Math.round(cx * me.zoom));
320                sprite.dirtyPath = false;
321            }
322            else if (sprite.type !== &quot;text&quot;) {
323                sprite.attr.path = scrubbedAttrs.path = me.setPaths(sprite, scrubbedAttrs) || scrubbedAttrs.path;
324                dom.path = me.path2vml(scrubbedAttrs.path);
325                sprite.dirtyPath = false;
326            }
327        }
328
329        // Apply clipping
330        if (&quot;clip-rect&quot; in scrubbedAttrs) {
331            me.setClip(sprite, scrubbedAttrs);
332        }
333
334        // Handle text (special handling required)
335        if (sprite.type == &quot;text&quot;) {
336            me.setTextAttributes(sprite, scrubbedAttrs);
337        }
338
339        // Handle fill and opacity
340        if (sprite.type == 'image' || scrubbedAttrs.opacity  || scrubbedAttrs['fill-opacity'] || scrubbedAttrs.fill) {
341            me.setFill(sprite, scrubbedAttrs);
342        }
343
344        // Handle stroke (all fills require a stroke element)
345        if (scrubbedAttrs.stroke || scrubbedAttrs['stroke-opacity'] || scrubbedAttrs.fill) {
346            me.setStroke(sprite, scrubbedAttrs);
347        }
348        
349        //set styles
350        style = spriteAttr.style;
351        if (style) {
352            el.setStyle(style);
353        }
354
355        sprite.dirty = false;
356    },
357
358    setZIndex: function(sprite) {
359        if (sprite.el) {
360            if (sprite.attr.zIndex != undefined) {
361                sprite.el.setStyle('zIndex', sprite.attr.zIndex);
362            }
363            sprite.zIndexDirty = false;
364        }
365    },
366
367    // Normalize all virtualized types into paths.
368    setPaths: function(sprite, params) {
369        var spriteAttr = sprite.attr;
370        // Clear bbox cache
371        sprite.bbox.plain = null;
372        sprite.bbox.transform = null;
373        if (sprite.type == 'circle') {
374            spriteAttr.rx = spriteAttr.ry = params.r;
375            return Ext.draw.Draw.ellipsePath(sprite);
376        }
377        else if (sprite.type == 'ellipse') {
378            spriteAttr.rx = params.rx;
379            spriteAttr.ry = params.ry;
380            return Ext.draw.Draw.ellipsePath(sprite);
381        }
382        else if (sprite.type == 'rect' || sprite.type == 'image') {
383            spriteAttr.rx = spriteAttr.ry = params.r;
384            return Ext.draw.Draw.rectPath(sprite);
385        }
386        else if (sprite.type == 'path' &amp;&amp; spriteAttr.path) {
387            return Ext.draw.Draw.pathToAbsolute(spriteAttr.path);
388        }
389        return false;
390    },
391
392    setFill: function(sprite, params) {
393        var me = this,
394            el = sprite.el,
395            dom = el.dom,
396            fillEl = dom.getElementsByTagName('fill')[0],
397            opacity, gradient, fillUrl, rotation, angle;
398
399        if (fillEl) {
400            dom.removeChild(fillEl);
401        } else {
402            fillEl = me.createNode('fill');
403        }
404        if (Ext.isArray(params.fill)) {
405            params.fill = params.fill[0];
406        }
407        if (sprite.type == 'image') {
408            fillEl.on = true;
409            fillEl.src = params.src;
410            fillEl.type = &quot;tile&quot;;
411            fillEl.rotate = true;
412        } else if (params.fill == &quot;none&quot;) {
413            fillEl.on = false;
414        } else {
415            if (typeof params.opacity == &quot;number&quot;) {
416                fillEl.opacity = params.opacity;
417            }
418            if (typeof params[&quot;fill-opacity&quot;] == &quot;number&quot;) {
419                fillEl.opacity = params[&quot;fill-opacity&quot;];
420            }
421            fillEl.on = true;
422            if (typeof params.fill == &quot;string&quot;) {
423                fillUrl = params.fill.match(me.fillUrlRe);
424                if (fillUrl) {
425                    fillUrl = fillUrl[1];
426                    // If the URL matches one of the registered gradients, render that gradient
427                    if (fillUrl.charAt(0) == &quot;#&quot;) {
428                        gradient = me.gradientsColl.getByKey(fillUrl.substring(1));
429                    }
430                    if (gradient) {
431                        // VML angle is offset and inverted from standard, and must be adjusted to match rotation transform
432                        rotation = params.rotation;
433                        angle = -(gradient.angle + 270 + (rotation ? rotation.degrees : 0)) % 360;
434                        // IE will flip the angle at 0 degrees...
435                        if (angle === 0) {
436                            angle = 180;
437                        }
438                        fillEl.angle = angle;
439                        fillEl.type = &quot;gradient&quot;;
440                        fillEl.method = &quot;sigma&quot;;
441                        fillEl.colors = gradient.colors;
442                    }
443                    // Otherwise treat it as an image
444                    else {
445                        fillEl.src = fillUrl;
446                        fillEl.type = &quot;tile&quot;;
447                        fillEl.rotate = true;
448                    }
449                }
450                else {
451                    fillEl.color = Ext.draw.Color.toHex(params.fill) || params.fill;
452                    fillEl.src = &quot;&quot;;
453                    fillEl.type = &quot;solid&quot;;
454                }
455            }
456        }
457        dom.appendChild(fillEl);
458    },
459
460    setStroke: function(sprite, params) {
461        var me = this,
462            el = sprite.el.dom,
463            strokeEl = sprite.strokeEl,
464            newStroke = false,
465            width, opacity;
466
467        if (!strokeEl) {
468            strokeEl = sprite.strokeEl = me.createNode(&quot;stroke&quot;);
469            newStroke = true;
470        }
471        if (Ext.isArray(params.stroke)) {
472            params.stroke = params.stroke[0];
473        }
474        if (!params.stroke || params.stroke == &quot;none&quot; || params.stroke == 0 || params[&quot;stroke-width&quot;] == 0) {
475            strokeEl.on = false;
476        }
477        else {
478            strokeEl.on = true;
479            if (params.stroke &amp;&amp; !params.stroke.match(me.fillUrlRe)) {
480                // VML does NOT support a gradient stroke :(
481                strokeEl.color = Ext.draw.Color.toHex(params.stroke);
482            }
483            strokeEl.joinstyle = params[&quot;stroke-linejoin&quot;];
484            strokeEl.endcap = params[&quot;stroke-linecap&quot;] || &quot;round&quot;;
485            strokeEl.miterlimit = params[&quot;stroke-miterlimit&quot;] || 8;
486            width = parseFloat(params[&quot;stroke-width&quot;] || 1) * 0.75;
487            opacity = params[&quot;stroke-opacity&quot;] || 1;
488            // VML Does not support stroke widths under 1, so we're going to fiddle with stroke-opacity instead.
489            if (Ext.isNumber(width) &amp;&amp; width &lt; 1) {
490                strokeEl.weight = 1;
491                strokeEl.opacity = opacity * width;
492            }
493            else {
494                strokeEl.weight = width;
495                strokeEl.opacity = opacity;
496            }
497        }
498        if (newStroke) {
499            el.appendChild(strokeEl);
500        }
501    },
502
503    setClip: function(sprite, params) {
504        var me = this,
505            el = sprite.el,
506            clipEl = sprite.clipEl,
507            rect = String(params[&quot;clip-rect&quot;]).split(me.separatorRe);
508        if (!clipEl) {
509            clipEl = sprite.clipEl = me.el.insertFirst(Ext.getDoc().dom.createElement(&quot;div&quot;));
510            clipEl.addCls(Ext.baseCSSPrefix + 'vml-sprite');
511        }
512        if (rect.length == 4) {
513            rect[2] = +rect[2] + (+rect[0]);
514            rect[3] = +rect[3] + (+rect[1]);
515            clipEl.setStyle(&quot;clip&quot;, Ext.String.format(&quot;rect({1}px {2}px {3}px {0}px)&quot;, rect[0], rect[1], rect[2], rect[3]));
516            clipEl.setSize(me.el.width, me.el.height);
517        }
518        else {
519            clipEl.setStyle(&quot;clip&quot;, &quot;&quot;);
520        }
521    },
522
523    setTextAttributes: function(sprite, params) {
524        var me = this,
525            vml = sprite.vml,
526            textStyle = vml.textpath.style,
527            spanCacheStyle = me.span.style,
528            zoom = me.zoom,
529            round = Math.round,
530            fontObj = {
531                fontSize: &quot;font-size&quot;,
532                fontWeight: &quot;font-weight&quot;,
533                fontStyle: &quot;font-style&quot;
534            },
535            fontProp,
536            paramProp;
537        if (sprite.dirtyFont) {
538            if (params.font) {
539                textStyle.font = spanCacheStyle.font = params.font;
540            }
541            if (params[&quot;font-family&quot;]) {
542                textStyle.fontFamily = '&quot;' + params[&quot;font-family&quot;].split(&quot;,&quot;)[0].replace(me.fontFamilyRe, &quot;&quot;) + '&quot;';
543                spanCacheStyle.fontFamily = params[&quot;font-family&quot;];
544            }
545
546            for (fontProp in fontObj) {
547                paramProp = params[fontObj[fontProp]];
548                if (paramProp) {
549                    textStyle[fontProp] = spanCacheStyle[fontProp] = paramProp;
550                }
551            }
552
553            me.setText(sprite, params.text);
554            
555            if (vml.textpath.string) {
556                me.span.innerHTML = String(vml.textpath.string).replace(/&lt;/g, &quot;&amp;#60;&quot;).replace(/&amp;/g, &quot;&amp;#38;&quot;).replace(/\n/g, &quot;&lt;br&gt;&quot;);
557            }
558            vml.W = me.span.offsetWidth;
559            vml.H = me.span.offsetHeight + 2; // TODO handle baseline differences and offset in VML Textpath
560
561            // text-anchor emulation
562            if (params[&quot;text-anchor&quot;] == &quot;middle&quot;) {
563                textStyle[&quot;v-text-align&quot;] = &quot;center&quot;;
564            }
565            else if (params[&quot;text-anchor&quot;] == &quot;end&quot;) {
566                textStyle[&quot;v-text-align&quot;] = &quot;right&quot;;
567                vml.bbx = -Math.round(vml.W / 2);
568            }
569            else {
570                textStyle[&quot;v-text-align&quot;] = &quot;left&quot;;
571                vml.bbx = Math.round(vml.W / 2);
572            }
573        }
574        vml.X = params.x;
575        vml.Y = params.y;
576        vml.path.v = Ext.String.format(&quot;m{0},{1}l{2},{1}&quot;, Math.round(vml.X * zoom), Math.round(vml.Y * zoom), Math.round(vml.X * zoom) + 1);
577        // Clear bbox cache
578        sprite.bbox.plain = null;
579        sprite.bbox.transform = null;
580        sprite.dirtyFont = false;
581    },
582    
583    setText: function(sprite, text) {
584        sprite.vml.textpath.string = Ext.htmlDecode(text);
585    },
586
587    hide: function() {
588        this.el.hide();
589    },
590
591    show: function() {
592        this.el.show();
593    },
594
595    hidePrim: function(sprite) {
596        sprite.el.addCls(Ext.baseCSSPrefix + 'hide-visibility');
597    },
598
599    showPrim: function(sprite) {
600        sprite.el.removeCls(Ext.baseCSSPrefix + 'hide-visibility');
601    },
602
603    setSize: function(width, height) {
604        var me = this;
605        width = width || me.width;
606        height = height || me.height;
607        me.width = width;
608        me.height = height;
609
610        if (me.el) {
611            // Size outer div
612            if (width != undefined) {
613                me.el.setWidth(width);
614            }
615            if (height != undefined) {
616                me.el.setHeight(height);
617            }
618
619            // Handle viewBox sizing
620            me.applyViewBox();
621
622            me.callParent(arguments);
623        }
624    },
625
626    setViewBox: function(x, y, width, height) {
627        this.callParent(arguments);
628        this.viewBox = {
629            x: x,
630            y: y,
631            width: width,
632            height: height
633        };
634        this.applyViewBox();
635    },
636
637<span id='Ext-draw-engine-Vml-method-applyViewBox'>    /**
638</span>     * @private Using the current viewBox property and the surface's width and height, calculate the
639     * appropriate viewBoxShift that will be applied as a persistent transform to all sprites.
640     */
641    applyViewBox: function() {
642        var me = this,
643            viewBox = me.viewBox,
644            width = me.width,
645            height = me.height,
646            viewBoxX, viewBoxY, viewBoxWidth, viewBoxHeight,
647            relativeHeight, relativeWidth, size;
648
649        if (viewBox &amp;&amp; (width || height)) {
650            viewBoxX = viewBox.x;
651            viewBoxY = viewBox.y;
652            viewBoxWidth = viewBox.width;
653            viewBoxHeight = viewBox.height;
654            relativeHeight = height / viewBoxHeight;
655            relativeWidth = width / viewBoxWidth;
656
657            if (viewBoxWidth * relativeHeight &lt; width) {
658                viewBoxX -= (width - viewBoxWidth * relativeHeight) / 2 / relativeHeight;
659            }
660            if (viewBoxHeight * relativeWidth &lt; height) {
661                viewBoxY -= (height - viewBoxHeight * relativeWidth) / 2 / relativeWidth;
662            }
663
664            size = 1 / Math.max(viewBoxWidth / width, viewBoxHeight / height);
665
666            me.viewBoxShift = {
667                dx: -viewBoxX,
668                dy: -viewBoxY,
669                scale: size
670            };
671            me.items.each(function(item) {
672                me.transform(item);
673            });
674        }
675    },
676
677    onAdd: function(item) {
678        this.callParent(arguments);
679        if (this.el) {
680            this.renderItem(item);
681        }
682    },
683
684    onRemove: function(sprite) {
685        if (sprite.el) {
686            sprite.el.remove();
687            delete sprite.el;
688        }
689        this.callParent(arguments);
690    },
691
692    // VML Node factory method (createNode)
693    createNode : (function () {
694        try {
695            var doc = Ext.getDoc().dom;
696            if (!doc.namespaces.rvml) {
697                doc.namespaces.add(&quot;rvml&quot;, &quot;urn:schemas-microsoft-com:vml&quot;);
698            }
699            return function (tagName) {
700                return doc.createElement(&quot;&lt;rvml:&quot; + tagName + ' class=&quot;rvml&quot;&gt;');
701            };
702        } catch (e) {
703            return function (tagName) {
704                return doc.createElement(&quot;&lt;&quot; + tagName + ' xmlns=&quot;urn:schemas-microsoft.com:vml&quot; class=&quot;rvml&quot;&gt;');
705            };
706        }
707    })(),
708
709    render: function (container) {
710        var me = this,
711            doc = Ext.getDoc().dom;
712
713        if (!me.el) {
714            var el = doc.createElement(&quot;div&quot;);
715            me.el = Ext.get(el);
716            me.el.addCls(me.baseVmlCls);
717
718            // Measuring span (offscrren)
719            me.span = doc.createElement(&quot;span&quot;);
720            Ext.get(me.span).addCls(me.measureSpanCls);
721            el.appendChild(me.span);
722            me.el.setSize(me.width || 10, me.height || 10);
723            container.appendChild(el);
724            me.el.on({
725                scope: me,
726                mouseup: me.onMouseUp,
727                mousedown: me.onMouseDown,
728                mouseover: me.onMouseOver,
729                mouseout: me.onMouseOut,
730                mousemove: me.onMouseMove,
731                mouseenter: me.onMouseEnter,
732                mouseleave: me.onMouseLeave,
733                click: me.onClick
734            });
735        }
736        me.renderAll();
737    },
738
739    renderAll: function() {
740        this.items.each(this.renderItem, this);
741    },
742
743    redraw: function(sprite) {
744        sprite.dirty = true;
745        this.renderItem(sprite);
746    },
747
748    renderItem: function (sprite) {
749        // Does the surface element exist?
750        if (!this.el) {
751            return;
752        }
753
754        // Create sprite element if necessary
755        if (!sprite.el) {
756            this.createSpriteElement(sprite);
757        }
758
759        if (sprite.dirty) {
760            this.applyAttrs(sprite);
761            if (sprite.dirtyTransform) {
762                this.applyTransformations(sprite);
763            }
764        }
765    },
766
767    rotationCompensation: function (deg, dx, dy) {
768        var matrix = Ext.create('Ext.draw.Matrix');
769        matrix.rotate(-deg, 0.5, 0.5);
770        return {
771            x: matrix.x(dx, dy),
772            y: matrix.y(dx, dy)
773        };
774    },
775
776    extractTransform: function (sprite) {
777        var me = this,
778            matrix = Ext.create('Ext.draw.Matrix'), scale,
779            transformstions, tranformationsLength,
780            transform, i = 0,
781            shift = me.viewBoxShift;
782
783        for(transformstions = sprite.transformations, tranformationsLength = transformstions.length;
784            i &lt; tranformationsLength; i ++) {
785            transform = transformstions[i];
786            switch (transform.type) {
787                case 'translate' :
788                    matrix.translate(transform.x, transform.y);
789                    break;
790                case 'rotate':
791                    matrix.rotate(transform.degrees, transform.x, transform.y);
792                    break;
793                case 'scale':
794                    matrix.scale(transform.x || transform.scale, transform.y || transform.scale, transform.centerX, transform.centerY);
795                    break;
796            }
797        }
798
799        if (shift) {
800            matrix.add(1, 0, 0, 1, shift.dx, shift.dy);
801            matrix.prepend(shift.scale, 0, 0, shift.scale, 0, 0);
802        }
803        
804        return sprite.matrix = matrix;
805    },
806
807    setSimpleCoords: function(sprite, sx, sy, dx, dy, rotate) {
808        var me = this,
809            matrix = sprite.matrix,
810            dom = sprite.el.dom,
811            style = dom.style,
812            yFlipper = 1,
813            flip = &quot;&quot;,
814            fill = dom.getElementsByTagName('fill')[0],
815            kx = me.zoom / sx,
816            ky = me.zoom / sy,
817            rotationCompensation;
818        if (!sx || !sy) {
819            return;
820        }
821        dom.coordsize = Math.abs(kx) + ' ' + Math.abs(ky);
822        style.rotation = rotate * (sx * sy &lt; 0 ? -1 : 1);
823        if (rotate) {
824            rotationCompensation = me.rotationCompensation(rotate, dx, dy);
825            dx = rotationCompensation.x;
826            dy = rotationCompensation.y;
827        }
828        if (sx &lt; 0) {
829            flip += &quot;x&quot;
830        }
831        if (sy &lt; 0) {
832            flip += &quot; y&quot;;
833            yFlipper = -1;
834        }
835        style.flip = flip;
836        dom.coordorigin = (dx * -kx) + ' ' + (dy * -ky);
837        if (fill) {
838            dom.removeChild(fill);
839            rotationCompensation = me.rotationCompensation(rotate, matrix.x(sprite.x, sprite.y), matrix.y(sprite.x, sprite.y));
840            fill.position = rotationCompensation.x * yFlipper + ' ' + rotationCompensation.y * yFlipper;
841            fill.size = sprite.width * Math.abs(sx) + ' ' + sprite.height * Math.abs(sy);
842            dom.appendChild(fill);
843        }
844    },
845
846    transform : function (sprite) {
847        var me = this,
848            el = sprite.el,
849            skew = sprite.skew,
850            dom = el.dom,
851            domStyle = dom.style,
852            matrix = me.extractTransform(sprite).clone(),
853            split, zoom = me.zoom,
854            fill = dom.getElementsByTagName('fill')[0],
855            isPatt = !String(sprite.fill).indexOf(&quot;url(&quot;),
856            offset, c;
857
858
859        // Hide element while we transform
860
861        if (sprite.type != &quot;image&quot; &amp;&amp; skew &amp;&amp; !isPatt) {
862            // matrix transform via VML skew
863            skew.matrix = matrix.toString();
864            // skew.offset = '32767,1' OK
865            // skew.offset = '32768,1' Crash
866            // M$, R U kidding??
867            offset = matrix.offset();
868            if (offset[0] &gt; 32767) {
869                offset[0] = 32767;
870            } else if (offset[0] &lt; -32768) {
871                offset[0] = -32768
872            }
873            if (offset[1] &gt; 32767) {
874                offset[1] = 32767;
875            } else if (offset[1] &lt; -32768) {
876                offset[1] = -32768
877            }
878            skew.offset = offset;
879        } else {
880            if (skew) {
881                skew.matrix = &quot;1 0 0 1&quot;;
882                skew.offset = &quot;0 0&quot;;
883            }
884            split = matrix.split();
885            if (split.isSimple) {
886                domStyle.filter = '';
887                me.setSimpleCoords(sprite, split.scaleX, split.scaleY, split.translateX, split.translateY, split.rotate / Math.PI * 180);
888            } else {
889                domStyle.filter = matrix.toFilter();
890                var bb = me.getBBox(sprite),
891                    dx = bb.x - sprite.x,
892                    dy = bb.y - sprite.y;
893                dom.coordorigin = (dx * -zoom) + ' ' + (dy * -zoom);
894                if (fill) {
895                    dom.removeChild(fill);
896                    fill.position = dx + ' ' + dy;
897                    fill.size = sprite.width * sprite.scale.x + ' ' + sprite.height * 1.1;
898                    dom.appendChild(fill);
899                }
900            }
901        }
902    },
903
904    createItem: function (config) {
905        return Ext.create('Ext.draw.Sprite', config);
906    },
907
908    getRegion: function() {
909        return this.el.getRegion();
910    },
911
912    addCls: function(sprite, className) {
913        if (sprite &amp;&amp; sprite.el) {
914            sprite.el.addCls(className);
915        }
916    },
917
918    removeCls: function(sprite, className) {
919        if (sprite &amp;&amp; sprite.el) {
920            sprite.el.removeCls(className);
921        }
922    },
923
924<span id='Ext-draw-engine-Vml-method-addGradient'>    /**
925</span>     * Adds a definition to this Surface for a linear gradient. We convert the gradient definition
926     * to its corresponding VML attributes and store it for later use by individual sprites.
927     * @param {Object} gradient
928     */
929    addGradient: function(gradient) {
930        var gradients = this.gradientsColl || (this.gradientsColl = Ext.create('Ext.util.MixedCollection')),
931            colors = [],
932            stops = Ext.create('Ext.util.MixedCollection');
933
934        // Build colors string
935        stops.addAll(gradient.stops);
936        stops.sortByKey(&quot;ASC&quot;, function(a, b) {
937            a = parseInt(a, 10);
938            b = parseInt(b, 10);
939            return a &gt; b ? 1 : (a &lt; b ? -1 : 0);
940        });
941        stops.eachKey(function(k, v) {
942            colors.push(k + &quot;% &quot; + v.color);
943        });
944
945        gradients.add(gradient.id, {
946            colors: colors.join(&quot;,&quot;),
947            angle: gradient.angle
948        });
949    },
950
951    destroy: function() {
952        var me = this;
953        
954        me.callParent(arguments);
955        if (me.el) {
956            me.el.remove();
957        }
958        delete me.el;
959    }
960});
961</pre>
962</body>
963</html>