Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / Svg.html
diff --git a/docs/source/Svg.html b/docs/source/Svg.html
new file mode 100644 (file)
index 0000000..d9bd369
--- /dev/null
@@ -0,0 +1,672 @@
+<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-draw.engine.Svg'>/**
+</span> * @class Ext.draw.engine.Svg
+ * @extends Ext.draw.Surface
+ * Provides specific methods to draw with SVG.
+ */
+Ext.define('Ext.draw.engine.Svg', {
+
+    /* Begin Definitions */
+
+    extend: 'Ext.draw.Surface',
+
+    requires: ['Ext.draw.Draw', 'Ext.draw.Sprite', 'Ext.draw.Matrix', 'Ext.core.Element'],
+
+    /* End Definitions */
+
+    engine: 'Svg',
+
+    trimRe: /^\s+|\s+$/g,
+    spacesRe: /\s+/,
+    xlink: &quot;http:/&quot; + &quot;/www.w3.org/1999/xlink&quot;,
+
+    translateAttrs: {
+        radius: &quot;r&quot;,
+        radiusX: &quot;rx&quot;,
+        radiusY: &quot;ry&quot;,
+        path: &quot;d&quot;,
+        lineWidth: &quot;stroke-width&quot;,
+        fillOpacity: &quot;fill-opacity&quot;,
+        strokeOpacity: &quot;stroke-opacity&quot;,
+        strokeLinejoin: &quot;stroke-linejoin&quot;
+    },
+
+    minDefaults: {
+        circle: {
+            cx: 0,
+            cy: 0,
+            r: 0,
+            fill: &quot;none&quot;,
+            stroke: null,
+            &quot;stroke-width&quot;: null,
+            opacity: null,
+            &quot;fill-opacity&quot;: null,
+            &quot;stroke-opacity&quot;: null
+        },
+        ellipse: {
+            cx: 0,
+            cy: 0,
+            rx: 0,
+            ry: 0,
+            fill: &quot;none&quot;,
+            stroke: null,
+            &quot;stroke-width&quot;: null,
+            opacity: null,
+            &quot;fill-opacity&quot;: null,
+            &quot;stroke-opacity&quot;: null
+        },
+        rect: {
+            x: 0,
+            y: 0,
+            width: 0,
+            height: 0,
+            rx: 0,
+            ry: 0,
+            fill: &quot;none&quot;,
+            stroke: null,
+            &quot;stroke-width&quot;: null,
+            opacity: null,
+            &quot;fill-opacity&quot;: null,
+            &quot;stroke-opacity&quot;: null
+        },
+        text: {
+            x: 0,
+            y: 0,
+            &quot;text-anchor&quot;: &quot;start&quot;,
+            &quot;font-family&quot;: null,
+            &quot;font-size&quot;: null,
+            &quot;font-weight&quot;: null,
+            &quot;font-style&quot;: null,
+            fill: &quot;#000&quot;,
+            stroke: null,
+            &quot;stroke-width&quot;: null,
+            opacity: null,
+            &quot;fill-opacity&quot;: null,
+            &quot;stroke-opacity&quot;: null
+        },
+        path: {
+            d: &quot;M0,0&quot;,
+            fill: &quot;none&quot;,
+            stroke: null,
+            &quot;stroke-width&quot;: null,
+            opacity: null,
+            &quot;fill-opacity&quot;: null,
+            &quot;stroke-opacity&quot;: null
+        },
+        image: {
+            x: 0,
+            y: 0,
+            width: 0,
+            height: 0,
+            preserveAspectRatio: &quot;none&quot;,
+            opacity: null
+        }
+    },
+
+    createSvgElement: function(type, attrs) {
+        var el = this.domRef.createElementNS(&quot;http:/&quot; + &quot;/www.w3.org/2000/svg&quot;, type),
+            key;
+        if (attrs) {
+            for (key in attrs) {
+                el.setAttribute(key, String(attrs[key]));
+            }
+        }
+        return el;
+    },
+
+    createSpriteElement: function(sprite) {
+        // Create svg element and append to the DOM.
+        var el = this.createSvgElement(sprite.type);
+        el.id = sprite.id;
+        if (el.style) {
+            el.style.webkitTapHighlightColor = &quot;rgba(0,0,0,0)&quot;;
+        }
+        sprite.el = Ext.get(el);
+        this.applyZIndex(sprite); //performs the insertion
+        sprite.matrix = Ext.create('Ext.draw.Matrix');
+        sprite.bbox = {
+            plain: 0,
+            transform: 0
+        };
+        sprite.fireEvent(&quot;render&quot;, sprite);
+        return el;
+    },
+
+    getBBox: function (sprite, isWithoutTransform) {
+        var realPath = this[&quot;getPath&quot; + sprite.type](sprite);
+        if (isWithoutTransform) {
+            sprite.bbox.plain = sprite.bbox.plain || Ext.draw.Draw.pathDimensions(realPath);
+            return sprite.bbox.plain;
+        }
+        sprite.bbox.transform = sprite.bbox.transform || Ext.draw.Draw.pathDimensions(Ext.draw.Draw.mapPath(realPath, sprite.matrix));
+        return sprite.bbox.transform;
+    },
+    
+    getBBoxText: function (sprite) {
+        var bbox = {},
+            bb, height, width, i, ln, el;
+
+        if (sprite &amp;&amp; sprite.el) {
+            el = sprite.el.dom;
+            try {
+                bbox = el.getBBox();
+                return bbox;
+            } catch(e) {
+                // Firefox 3.0.x plays badly here
+            }
+            bbox = {x: bbox.x, y: Infinity, width: 0, height: 0};
+            ln = el.getNumberOfChars();
+            for (i = 0; i &lt; ln; i++) {
+                bb = el.getExtentOfChar(i);
+                bbox.y = Math.min(bb.y, bbox.y);
+                height = bb.y + bb.height - bbox.y;
+                bbox.height = Math.max(bbox.height, height);
+                width = bb.x + bb.width - bbox.x;
+                bbox.width = Math.max(bbox.width, width);
+            }
+            return bbox;
+        }
+    },
+
+    hide: function() {
+        Ext.get(this.el).hide();
+    },
+
+    show: function() {
+        Ext.get(this.el).show();
+    },
+
+    hidePrim: function(sprite) {
+        this.addCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
+    },
+
+    showPrim: function(sprite) {
+        this.removeCls(sprite, Ext.baseCSSPrefix + 'hide-visibility');
+    },
+
+    getDefs: function() {
+        return this._defs || (this._defs = this.createSvgElement(&quot;defs&quot;));
+    },
+
+    transform: function(sprite) {
+        var me = this,
+            matrix = Ext.create('Ext.draw.Matrix'),
+            transforms = sprite.transformations,
+            transformsLength = transforms.length,
+            i = 0,
+            transform, type;
+            
+        for (; i &lt; transformsLength; i++) {
+            transform = transforms[i];
+            type = transform.type;
+            if (type == &quot;translate&quot;) {
+                matrix.translate(transform.x, transform.y);
+            }
+            else if (type == &quot;rotate&quot;) {
+                matrix.rotate(transform.degrees, transform.x, transform.y);
+            }
+            else if (type == &quot;scale&quot;) {
+                matrix.scale(transform.x, transform.y, transform.centerX, transform.centerY);
+            }
+        }
+        sprite.matrix = matrix;
+        sprite.el.set({transform: matrix.toSvg()});
+    },
+
+    setSize: function(w, h) {
+        var me = this,
+            el = me.el;
+        
+        w = +w || me.width;
+        h = +h || me.height;
+        me.width = w;
+        me.height = h;
+
+        el.setSize(w, h);
+        el.set({
+            width: w,
+            height: h
+        });
+        me.callParent([w, h]);
+    },
+
+<span id='Ext-draw.engine.Svg-method-getRegion'>    /**
+</span>     * Get the region for the surface's canvas area
+     * @returns {Ext.util.Region}
+     */
+    getRegion: function() {
+        // Mozilla requires using the background rect because the svg element returns an
+        // incorrect region. Webkit gives no region for the rect and must use the svg element.
+        var svgXY = this.el.getXY(),
+            rectXY = this.bgRect.getXY(),
+            max = Math.max,
+            x = max(svgXY[0], rectXY[0]),
+            y = max(svgXY[1], rectXY[1]);
+        return {
+            left: x,
+            top: y,
+            right: x + this.width,
+            bottom: y + this.height
+        };
+    },
+
+    onRemove: function(sprite) {
+        if (sprite.el) {
+            sprite.el.remove();
+            delete sprite.el;
+        }
+        this.callParent(arguments);
+    },
+    
+    setViewBox: function(x, y, width, height) {
+        if (isFinite(x) &amp;&amp; isFinite(y) &amp;&amp; isFinite(width) &amp;&amp; isFinite(height)) {
+            this.callParent(arguments);
+            this.el.dom.setAttribute(&quot;viewBox&quot;, [x, y, width, height].join(&quot; &quot;));
+        }
+    },
+
+    render: function (container) {
+        var me = this;
+        if (!me.el) {
+            var width = me.width || 10,
+                height = me.height || 10,
+                el = me.createSvgElement('svg', {
+                    xmlns: &quot;http:/&quot; + &quot;/www.w3.org/2000/svg&quot;,
+                    version: 1.1,
+                    width: width,
+                    height: height
+                }),
+                defs = me.getDefs(),
+
+                // Create a rect that is always the same size as the svg root; this serves 2 purposes:
+                // (1) It allows mouse events to be fired over empty areas in Webkit, and (2) we can
+                // use it rather than the svg element for retrieving the correct client rect of the
+                // surface in Mozilla (see https://bugzilla.mozilla.org/show_bug.cgi?id=530985)
+                bgRect = me.createSvgElement(&quot;rect&quot;, {
+                    width: &quot;100%&quot;,
+                    height: &quot;100%&quot;,
+                    fill: &quot;#000&quot;,
+                    stroke: &quot;none&quot;,
+                    opacity: 0
+                }),
+                webkitRect;
+            
+                if (Ext.isSafari3) {
+                    // Rect that we will show/hide to fix old WebKit bug with rendering issues.
+                    webkitRect = me.createSvgElement(&quot;rect&quot;, {
+                        x: -10,
+                        y: -10,
+                        width: &quot;110%&quot;,
+                        height: &quot;110%&quot;,
+                        fill: &quot;none&quot;,
+                        stroke: &quot;#000&quot;
+                    });
+                }
+            el.appendChild(defs);
+            if (Ext.isSafari3) {
+                el.appendChild(webkitRect);
+            }
+            el.appendChild(bgRect);
+            container.appendChild(el);
+            me.el = Ext.get(el);
+            me.bgRect = Ext.get(bgRect);
+            if (Ext.isSafari3) {
+                me.webkitRect = Ext.get(webkitRect);
+                me.webkitRect.hide();
+            }
+            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
+            });
+        }
+        me.renderAll();
+    },
+
+    // private
+    onMouseEnter: function(e) {
+        if (this.el.parent().getRegion().contains(e.getPoint())) {
+            this.fireEvent('mouseenter', e);
+        }
+    },
+
+    // private
+    onMouseLeave: function(e) {
+        if (!this.el.parent().getRegion().contains(e.getPoint())) {
+            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);
+        // We wrap text types in a tspan, sprite is the parent.
+        if (target.nodeName == &quot;tspan&quot; &amp;&amp; target.parentNode) {
+            target = target.parentNode;
+        }
+        sprite = this.items.get(target.id);
+        if (sprite) {
+            sprite.fireEvent(name, sprite, e);
+        }
+    },
+
+    /* @private - Wrap SVG text inside a tspan to allow for line wrapping.  In addition this normallizes
+     * the baseline for text the vertical middle of the text to be the same as VML.
+     */
+    tuneText: function (sprite, attrs) {
+        var el = sprite.el.dom,
+            tspans = [],
+            height, tspan, text, i, ln, texts, factor;
+
+        if (attrs.hasOwnProperty(&quot;text&quot;)) {
+           tspans = this.setText(sprite, attrs.text);
+        }
+        // Normalize baseline via a DY shift of first tspan. Shift other rows by height * line height (1.2)
+        if (tspans.length) {
+            height = this.getBBoxText(sprite).height;
+            for (i = 0, ln = tspans.length; i &lt; ln; i++) {
+                // The text baseline for FireFox 3.0 and 3.5 is different than other SVG implementations
+                // so we are going to normalize that here
+                factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4;
+                tspans[i].setAttribute(&quot;dy&quot;, i ? height * 1.2 : height / factor);
+            }
+            sprite.dirty = true;
+        }
+    },
+
+    setText: function(sprite, textString) {
+         var me = this,
+             el = sprite.el.dom,
+             x = el.getAttribute(&quot;x&quot;),
+             tspans = [],
+             height, tspan, text, i, ln, texts;
+        
+        while (el.firstChild) {
+            el.removeChild(el.firstChild);
+        }
+        // Wrap each row into tspan to emulate rows
+        texts = String(textString).split(&quot;\n&quot;);
+        for (i = 0, ln = texts.length; i &lt; ln; i++) {
+            text = texts[i];
+            if (text) {
+                tspan = me.createSvgElement(&quot;tspan&quot;);
+                tspan.appendChild(document.createTextNode(Ext.htmlDecode(text)));
+                tspan.setAttribute(&quot;x&quot;, x);
+                el.appendChild(tspan);
+                tspans[i] = tspan;
+            }
+        }
+        return tspans;
+    },
+
+    renderAll: function() {
+        this.items.each(this.renderItem, this);
+    },
+
+    renderItem: function (sprite) {
+        if (!this.el) {
+            return;
+        }
+        if (!sprite.el) {
+            this.createSpriteElement(sprite);
+        }
+        if (sprite.zIndexDirty) {
+            this.applyZIndex(sprite);
+        }
+        if (sprite.dirty) {
+            this.applyAttrs(sprite);
+            this.applyTransformations(sprite);
+        }
+    },
+
+    redraw: function(sprite) {
+        sprite.dirty = sprite.zIndexDirty = true;
+        this.renderItem(sprite);
+    },
+
+    applyAttrs: function (sprite) {
+        var me = this,
+            el = sprite.el,
+            group = sprite.group,
+            sattr = sprite.attr,
+            groups, i, ln, attrs, font, key, style, name, rect;
+
+        if (group) {
+            groups = [].concat(group);
+            ln = groups.length;
+            for (i = 0; i &lt; ln; i++) {
+                group = groups[i];
+                me.getGroup(group).add(sprite);
+            }
+            delete sprite.group;
+        }
+        attrs = me.scrubAttrs(sprite) || {};
+
+        // if (sprite.dirtyPath) {
+        sprite.bbox.plain = 0;
+        sprite.bbox.transform = 0;
+            if (sprite.type == &quot;circle&quot; || sprite.type == &quot;ellipse&quot;) {
+                attrs.cx = attrs.cx || attrs.x;
+                attrs.cy = attrs.cy || attrs.y;
+            }
+            else if (sprite.type == &quot;rect&quot;) {
+                attrs.rx = attrs.ry = attrs.r;
+            }
+            else if (sprite.type == &quot;path&quot; &amp;&amp; attrs.d) {
+                attrs.d = Ext.draw.Draw.pathToAbsolute(attrs.d);
+            }
+            sprite.dirtyPath = false;
+        // }
+
+        if (attrs['clip-rect']) {
+            me.setClip(sprite, attrs);
+            delete attrs['clip-rect'];
+        }
+        if (sprite.type == 'text' &amp;&amp; attrs.font &amp;&amp; sprite.dirtyFont) {
+            el.set({ style: &quot;font: &quot; + attrs.font});
+            sprite.dirtyFont = false;
+        }
+        if (sprite.type == &quot;image&quot;) {
+            el.dom.setAttributeNS(me.xlink, &quot;href&quot;, attrs.src);
+        }
+        Ext.applyIf(attrs, me.minDefaults[sprite.type]);
+
+        if (sprite.dirtyHidden) {
+            (sattr.hidden) ? me.hidePrim(sprite) : me.showPrim(sprite);
+            sprite.dirtyHidden = false;
+        }
+        for (key in attrs) {
+            if (attrs.hasOwnProperty(key) &amp;&amp; attrs[key] != null) {
+                el.dom.setAttribute(key, String(attrs[key]));
+            }
+        }
+        if (sprite.type == 'text') {
+            me.tuneText(sprite, attrs);
+        }
+
+        //set styles
+        style = sattr.style;
+        if (style) {
+            el.setStyle(style);
+        }
+
+        sprite.dirty = false;
+
+        if (Ext.isSafari3) {
+            // Refreshing the view to fix bug EXTJSIV-1: rendering issue in old Safari 3
+            me.webkitRect.show();
+            setTimeout(function () {
+                me.webkitRect.hide();
+            });
+        }
+    },
+
+    setClip: function(sprite, params) {
+        var me = this,
+            rect = params[&quot;clip-rect&quot;],
+            clipEl, clipPath;
+        if (rect) {
+            if (sprite.clip) {
+                sprite.clip.parentNode.parentNode.removeChild(sprite.clip.parentNode);
+            }
+            clipEl = me.createSvgElement('clipPath');
+            clipPath = me.createSvgElement('rect');
+            clipEl.id = Ext.id(null, 'ext-clip-');
+            clipPath.setAttribute(&quot;x&quot;, rect.x);
+            clipPath.setAttribute(&quot;y&quot;, rect.y);
+            clipPath.setAttribute(&quot;width&quot;, rect.width);
+            clipPath.setAttribute(&quot;height&quot;, rect.height);
+            clipEl.appendChild(clipPath);
+            me.getDefs().appendChild(clipEl);
+            sprite.el.dom.setAttribute(&quot;clip-path&quot;, &quot;url(#&quot; + clipEl.id + &quot;)&quot;);
+            sprite.clip = clipPath;
+        }
+        // if (!attrs[key]) {
+        //     var clip = Ext.getDoc().dom.getElementById(sprite.el.getAttribute(&quot;clip-path&quot;).replace(/(^url\(#|\)$)/g, &quot;&quot;));
+        //     clip &amp;&amp; clip.parentNode.removeChild(clip);
+        //     sprite.el.setAttribute(&quot;clip-path&quot;, &quot;&quot;);
+        //     delete attrss.clip;
+        // }
+    },
+
+<span id='Ext-draw.engine.Svg-method-applyZIndex'>    /**
+</span>     * Insert or move a given sprite's element to the correct place in the DOM list for its zIndex
+     * @param {Ext.draw.Sprite} sprite
+     */
+    applyZIndex: function(sprite) {
+        var idx = this.normalizeSpriteCollection(sprite),
+            el = sprite.el,
+            prevEl;
+        if (this.el.dom.childNodes[idx + 2] !== el.dom) { //shift by 2 to account for defs and bg rect 
+            if (idx &gt; 0) {
+                // Find the first previous sprite which has its DOM element created already
+                do {
+                    prevEl = this.items.getAt(--idx).el;
+                } while (!prevEl &amp;&amp; idx &gt; 0);
+            }
+            el.insertAfter(prevEl || this.bgRect);
+        }
+        sprite.zIndexDirty = false;
+    },
+
+    createItem: function (config) {
+        var sprite = Ext.create('Ext.draw.Sprite', config);
+        sprite.surface = this;
+        return sprite;
+    },
+
+    addGradient: function(gradient) {
+        gradient = Ext.draw.Draw.parseGradient(gradient);
+        var ln = gradient.stops.length,
+            vector = gradient.vector,
+            gradientEl,
+            stop,
+            stopEl,
+            i;
+        if (gradient.type == &quot;linear&quot;) {
+            gradientEl = this.createSvgElement(&quot;linearGradient&quot;);
+            gradientEl.setAttribute(&quot;x1&quot;, vector[0]);
+            gradientEl.setAttribute(&quot;y1&quot;, vector[1]);
+            gradientEl.setAttribute(&quot;x2&quot;, vector[2]);
+            gradientEl.setAttribute(&quot;y2&quot;, vector[3]);
+        }
+        else {
+            gradientEl = this.createSvgElement(&quot;radialGradient&quot;);
+            gradientEl.setAttribute(&quot;cx&quot;, gradient.centerX);
+            gradientEl.setAttribute(&quot;cy&quot;, gradient.centerY);
+            gradientEl.setAttribute(&quot;r&quot;, gradient.radius);
+            if (Ext.isNumber(gradient.focalX) &amp;&amp; Ext.isNumber(gradient.focalY)) {
+                gradientEl.setAttribute(&quot;fx&quot;, gradient.focalX);
+                gradientEl.setAttribute(&quot;fy&quot;, gradient.focalY);
+            }
+        }    
+        gradientEl.id = gradient.id;
+        this.getDefs().appendChild(gradientEl);
+
+        for (i = 0; i &lt; ln; i++) {
+            stop = gradient.stops[i];
+            stopEl = this.createSvgElement(&quot;stop&quot;);
+            stopEl.setAttribute(&quot;offset&quot;, stop.offset + &quot;%&quot;);
+            stopEl.setAttribute(&quot;stop-color&quot;, stop.color);
+            stopEl.setAttribute(&quot;stop-opacity&quot;,stop.opacity);
+            gradientEl.appendChild(stopEl);
+        }
+    },
+
+<span id='Ext-draw.engine.Svg-method-hasCls'>    /**
+</span>     * Checks if the specified CSS class exists on this element's DOM node.
+     * @param {String} className The CSS class to check for
+     * @return {Boolean} True if the class exists, else false
+     */
+    hasCls: function(sprite, className) {
+        return className &amp;&amp; (' ' + (sprite.el.dom.getAttribute('class') || '') + ' ').indexOf(' ' + className + ' ') != -1;
+    },
+
+    addCls: function(sprite, className) {
+        var el = sprite.el,
+            i,
+            len,
+            v,
+            cls = [],
+            curCls =  el.getAttribute('class') || '';
+        // Separate case is for speed
+        if (!Ext.isArray(className)) {
+            if (typeof className == 'string' &amp;&amp; !this.hasCls(sprite, className)) {
+                el.set({ 'class': curCls + ' ' + className });
+            }
+        }
+        else {
+            for (i = 0, len = className.length; i &lt; len; i++) {
+                v = className[i];
+                if (typeof v == 'string' &amp;&amp; (' ' + curCls + ' ').indexOf(' ' + v + ' ') == -1) {
+                    cls.push(v);
+                }
+            }
+            if (cls.length) {
+                el.set({ 'class': ' ' + cls.join(' ') });
+            }
+        }
+    },
+
+    removeCls: function(sprite, className) {
+        var me = this,
+            el = sprite.el,
+            curCls =  el.getAttribute('class') || '',
+            i, idx, len, cls, elClasses;
+        if (!Ext.isArray(className)){
+            className = [className];
+        }
+        if (curCls) {
+            elClasses = curCls.replace(me.trimRe, ' ').split(me.spacesRe);
+            for (i = 0, len = className.length; i &lt; len; i++) {
+                cls = className[i];
+                if (typeof cls == 'string') {
+                    cls = cls.replace(me.trimRe, '');
+                    idx = Ext.Array.indexOf(elClasses, cls);
+                    if (idx != -1) {
+                        elClasses.splice(idx, 1);
+                    }
+                }
+            }
+            el.set({ 'class': elClasses.join(' ') });
+        }
+    },
+
+    destroy: function() {
+        var me = this;
+        
+        me.callParent();
+        if (me.el) {
+            me.el.remove();
+        }
+        delete me.el;
+    }
+});</pre></pre></body></html>
\ No newline at end of file