Fixed flipping in negative scaling animations
[raphael] / raphael.js
index de231d0..748185f 100644 (file)
@@ -1,5 +1,5 @@
 /*!
- * Raphael 1.2.9dev - JavaScript Vector Library
+ * Raphael 1.3.0dev - JavaScript Vector Library
  *
  * Copyright (c) 2008 - 2009 Dmitry Baranovskiy (http://raphaeljs.com)
  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
@@ -28,14 +28,15 @@ window.Raphael = (function () {
             }
             return create[apply](R, arguments);
         },
+        Paper = function () {},
         appendChild = "appendChild",
         apply = "apply",
         concat = "concat",
         E = "",
         S = " ",
-        events = "click dblclick mousedown mousemove mouseout mouseover mouseup".split(S),
+        split = "split",
+        events = "click dblclick mousedown mousemove mouseout mouseover mouseup"[split](S),
         has = "hasOwnProperty",
-        isit = /^\[object\s+|\]$/gi,
         join = "join",
         length = "length",
         proto = "prototype",
@@ -43,6 +44,7 @@ window.Raphael = (function () {
         math = Math,
         mmax = math.max,
         mmin = math.min,
+        abs = math.abs,
         nu = "number",
         toString = "toString",
         objectToString = Object[proto][toString],
@@ -54,22 +56,29 @@ window.Raphael = (function () {
         colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|rgb\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i,
         round = math.round,
         setAttribute = "setAttribute",
-        split = "split",
         toFloat = parseFloat,
         toInt = parseInt,
         upperCase = String[proto].toUpperCase,
         availableAttrs = {"clip-rect": "0 0 10e9 10e9", cursor: "default", cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '10px "Arial"', "font-family": '"Arial"', "font-size": "10", "font-style": "normal", "font-weight": 400, gradient: 0, height: 0, href: "http://raphaeljs.com/", opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", src: "", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, target: "_blank", "text-anchor": "middle", title: "Raphael", translation: "0 0", width: 0, x: 0, y: 0},
         availableAnimAttrs = {"clip-rect": "csv", cx: nu, cy: nu, fill: "colour", "fill-opacity": nu, "font-size": nu, height: nu, opacity: nu, path: "path", r: nu, rotation: "csv", rx: nu, ry: nu, scale: "csv", stroke: "colour", "stroke-opacity": nu, "stroke-width": nu, translation: "csv", width: nu, x: nu, y: nu},
         rp = "replace";
-    R.version = "1.2.9dev";
+    R.version = "1.3.0dev";
     R.type = (win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
+    if (R.type == "VML") {
+        var d = document.createElement("div");
+        d.innerHTML = '<!--[if vml]><br><br><![endif]-->';
+        if (d.childNodes[length] != 2) {
+            return null;
+        }
+    }
     R.svg = !(R.vml = R.type == "VML");
+    Paper[proto] = R[proto];
     R._id = 0;
     R._oid = 0;
     R.fn = {};
     R.is = function (o, type) {
         type = lowerCase.call(type);
-        return ((type == "object" || type == "undefined") && typeof o == type) || (o == null && type == "null") || lowerCase.call(objectToString.call(o)[rp](isit, E)) == type;
+        return ((type == "object" || type == "undefined") && typeof o == type) || (o == null && type == "null") || lowerCase.call(objectToString.call(o).slice(8, -1)) == type;
     };
     R.setWindow = function (newwin) {
         win = newwin;
@@ -317,6 +326,20 @@ window.Raphael = (function () {
         data[toString] = R._path2string;
         return data;
     });
+    R.findDotsAtSegment = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+        var t1 = 1 - t,
+            x = pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
+            y = pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y,
+            mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x),
+            my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y),
+            nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x),
+            ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y),
+            ax = (1 - t) * p1x + t * c1x,
+            ay = (1 - t) * p1y + t * c1y,
+            cx = (1 - t) * c2x + t * p2x,
+            cy = (1 - t) * c2y + t * p2y;
+        return {x: x, y: y, m: {x: mx, y: my}, n: {x: nx, y: ny}, start: {x: ax, y: ay}, end: {x: cx, y: cy}};
+    });
     var pathDimensions = cacher(function (path) {
         if (!path) {
             return {x: 0, y: 0, width: 0, height: 0};
@@ -549,12 +572,12 @@ window.Raphael = (function () {
                     sin = math.sin(PI / 180 * angle),
                     x = (x1 - x2) / 2,
                     y = (y1 - y2) / 2;
-                rx = mmax(rx, math.abs(x));
-                ry = mmax(ry, math.abs(y));
+                rx = mmax(rx, abs(x));
+                ry = mmax(ry, abs(y));
                 var rx2 = rx * rx,
                     ry2 = ry * ry,
                     k = (large_arc_flag == sweep_flag ? -1 : 1) *
-                        math.sqrt(math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
+                        math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
                     cx = k * rx * y / ry + (x1 + x2) / 2,
                     cy = k * -ry * x / rx + (y1 + y2) / 2,
                     f1 = math.asin(((y1 - cy) / ry).toFixed(7)),
@@ -577,7 +600,7 @@ window.Raphael = (function () {
                 cy = recursive[3];
             }
             var df = f2 - f1;
-            if (math.abs(df) > _120) {
+            if (abs(df) > _120) {
                 var f2old = f2,
                     x2old = x2,
                     y2old = y2;
@@ -611,27 +634,13 @@ window.Raphael = (function () {
                 return newres;
             }
         },
-        findDotAtSegment = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+        findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
             var t1 = 1 - t;
             return {
                 x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
                 y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
             };
-        }),
-        findDotsAtSegment = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
-            var t1 = 1 - t,
-                x = pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
-                y = pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y,
-                mx = p1x + 2 * t * (c1x - p1x) + t * t * (c2x - 2 * c1x + p1x),
-                my = p1y + 2 * t * (c1y - p1y) + t * t * (c2y - 2 * c1y + p1y),
-                nx = c1x + 2 * t * (c2x - c1x) + t * t * (p2x - 2 * c2x + c1x),
-                ny = c1y + 2 * t * (c2y - c1y) + t * t * (p2y - 2 * c2y + c1y),
-                ax = (1 - t) * p1x + t * c1x,
-                ay = (1 - t) * p1y + t * c1y,
-                cx = (1 - t) * c2x + t * p2x,
-                cy = (1 - t) * c2y + t * p2y;
-            return {x: x, y: y, m: {x: mx, y: my}, n: {x: nx, y: ny}, start: {x: ax, y: ay}, end: {x: cx, y: cy}};
-        }),
+        },
         curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
             var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
                 b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
@@ -641,8 +650,8 @@ window.Raphael = (function () {
                 y = [p1y, p2y],
                 x = [p1x, p2x],
                 dot;
-            math.abs(t1) > 1e12 && (t1 = .5);
-            math.abs(t2) > 1e12 && (t2 = .5);
+            abs(t1) > 1e12 && (t1 = .5);
+            abs(t2) > 1e12 && (t2 = .5);
             if (t1 > 0 && t1 < 1) {
                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
                 x[push](dot.x);
@@ -658,8 +667,8 @@ window.Raphael = (function () {
             c = p1y - c1y;
             t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
             t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
-            math.abs(t1) > 1e12 && (t1 = .5);
-            math.abs(t2) > 1e12 && (t2 = .5);
+            abs(t1) > 1e12 && (t1 = .5);
+            abs(t2) > 1e12 && (t2 = .5);
             if (t1 > 0 && t1 < 1) {
                 dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
                 x[push](dot.x);
@@ -892,12 +901,17 @@ window.Raphael = (function () {
             el2.prev = el;
             el.next = el2;
         },
+        removed = function (methodname) {
+            return function () {
+                throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
+            };
+        },
         radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/;
 
     // SVG
     if (R.svg) {
-        paper.svgns = "http://www.w3.org/2000/svg";
-        paper.xlink = "http://www.w3.org/1999/xlink";
+        Paper[proto].svgns = "http://www.w3.org/2000/svg";
+        Paper[proto].xlink = "http://www.w3.org/1999/xlink";
         var round = function (num) {
             return +num + (~~num === num) * .5;
         },
@@ -920,7 +934,7 @@ window.Raphael = (function () {
                         el[setAttribute](key, attr[key]);
                     }
                 } else {
-                    return doc.createElementNS(paper.svgns, el);
+                    return doc.createElementNS(Paper[proto].svgns, el);
                 }
             };
         R[toString] = function () {
@@ -959,7 +973,7 @@ window.Raphael = (function () {
                     return null;
                 }
                 var vector = [0, 0, math.cos(angle * math.PI / 180), math.sin(angle * math.PI / 180)],
-                    max = 1 / (mmax(math.abs(vector[2]), math.abs(vector[3])) || 1);
+                    max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
                 vector[2] *= max;
                 vector[3] *= max;
                 if (vector[2] < 0) {
@@ -1062,7 +1076,7 @@ window.Raphael = (function () {
                             hl[appendChild](node);
                             pn = hl;
                         }
-                        pn.setAttributeNS(o.paper.xlink, att, value);
+                        pn.setAttributeNS(o.Paper[proto].xlink, att, value);
                         break;
                     case "cursor":
                         node.style.cursor = value;
@@ -1194,7 +1208,7 @@ window.Raphael = (function () {
                                 $(el, {width: this.offsetWidth, height: this.offsetHeight});
                                 $(ig, {width: this.offsetWidth, height: this.offsetHeight});
                                 doc.body.removeChild(this);
-                                paper.safari();
+                                o.paper.safari();
                             };
                             doc.body[appendChild](img);
                             img.src = isURL[1];
@@ -1484,7 +1498,19 @@ window.Raphael = (function () {
             insertbefore(this, element, this.paper);
             return this;
         };
-        
+        Element[proto].getTotalLength = function () {
+            if (this.type != "path") {
+                return -1;
+            }
+            return this.node.getTotalLength();
+        };
+        Element[proto].getPointAtLength = function (length) {
+            if (this.type != "path") {
+                return -1;
+            }
+            var point = this.node.getPointAtLength(length);
+            return {x: point.x, y: point.y};
+        };
         var theCircle = function (svg, x, y, r) {
             x = round(x);
             y = round(y);
@@ -1555,15 +1581,14 @@ window.Raphael = (function () {
             if (!container) {
                 throw new Error("SVG container not found.");
             }
-            paper.canvas = $("svg");
-            var cnvs = paper.canvas;
-            paper.width = width || 512;
-            paper.height = height || 342;
+            var cnvs = $("svg");
+            width = width || 512;
+            height = height || 342;
             $(cnvs, {
                 xmlns: "http://www.w3.org/2000/svg",
                 version: 1.1,
-                width: paper.width,
-                height: paper.height
+                width: width,
+                height: height
             });
             if (container == 1) {
                 cnvs.style.cssText = "position:absolute;left:" + x + "px;top:" + y + "px";
@@ -1575,17 +1600,15 @@ window.Raphael = (function () {
                     container[appendChild](cnvs);
                 }
             }
-            container = { canvas: cnvs };
-            for (var prop in paper) if (paper[has](prop)) {
-                container[prop] = paper[prop];
-            }
-            container.bottom = container.top = null;
+            container = new Paper;
+            container.width = width;
+            container.height = height;
+            container.canvas = cnvs;
             plugins.call(container, container, R.fn);
             container.clear();
-            container.raphael = R;
             return container;
         };
-        paper.clear = function () {
+        Paper[proto].clear = function () {
             var c = this.canvas;
             while (c.firstChild) {
                 c.removeChild(c.firstChild);
@@ -1595,10 +1618,10 @@ window.Raphael = (function () {
             c[appendChild](this.desc);
             c[appendChild](this.defs = $("defs"));
         };
-        paper.remove = function () {
+        Paper[proto].remove = function () {
             this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
             for (var i in this) {
-                delete this[i];
+                this[i] = removed(i);
             }
         };
     }
@@ -1634,6 +1657,16 @@ window.Raphael = (function () {
                 res[push](r);
             }
             return res[join](S);
+        },
+        segmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
+            var old = {x: 0, y: 0},
+                len = 0;
+            for (var i = 0; i < 1.01; i+=.01) {
+                var dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i);
+                i && (len += math.sqrt(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2)));
+                old = dot;
+            }
+            return len;
         };
         
         R[toString] = function () {
@@ -2192,6 +2225,33 @@ window.Raphael = (function () {
             insertbefore(this, element, this.paper);
             return this;
         };
+        var getLengthFactory = function (istotal) {
+            return function (length) {
+                if (this.type != "path") {
+                    return -1;
+                }
+                var path = path2curve(this.attrs.path), x, y, p, l,
+                    len = 0;
+                for (var i = 0, ii = path.length; i < ii; i++) {
+                    p = path[i];
+                    if (p[0] == "M") {
+                        x = +p[1];
+                        y = +p[2];
+                    } else {
+                        l = segmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+                        if (!istotal && len + l > length) {
+                            return findDotAtSegment(x, y, p[1], p[2], p[3], p[4], p[5], p[6], (length - len) / l);
+                        }
+                        len += l;
+                        x = +p[5];
+                        y = +p[6];
+                    }
+                }
+                return istotal ? len : {x: x, y: y};
+            };
+        };
+        Element[proto].getTotalLength = getLengthFactory(1);
+        Element[proto].getPointAtLength = getLengthFactory();
 
         var theCircle = function (vml, x, y, r) {
             var g = createNode("group"),
@@ -2210,8 +2270,8 @@ window.Raphael = (function () {
             res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
             vml.canvas[appendChild](g);
             return res;
-        };
-        var theRect = function (vml, x, y, w, h, r) {
+        },
+        theRect = function (vml, x, y, w, h, r) {
             var g = createNode("group"),
                 o = createNode("roundrect"),
                 arcsize = (+r || 0) / (mmin(w, h));
@@ -2227,8 +2287,8 @@ window.Raphael = (function () {
             res.setBox({x: x, y: y, width: w, height: h, r: r});
             vml.canvas[appendChild](g);
             return res;
-        };
-        var theEllipse = function (vml, x, y, rx, ry) {
+        },
+        theEllipse = function (vml, x, y, rx, ry) {
             var g = createNode("group"),
                 o = createNode("oval"),
                 ol = o.style;
@@ -2246,8 +2306,8 @@ window.Raphael = (function () {
             res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
             vml.canvas[appendChild](g);
             return res;
-        };
-        var theImage = function (vml, src, x, y, w, h) {
+        },
+        theImage = function (vml, src, x, y, w, h) {
             var g = createNode("group"),
                 o = createNode("image"),
                 ol = o.style;
@@ -2266,8 +2326,8 @@ window.Raphael = (function () {
             res.setBox({x: x, y: y, width: w, height: h});
             vml.canvas[appendChild](g);
             return res;
-        };
-        var theText = function (vml, x, y, text) {
+        },
+        theText = function (vml, x, y, text) {
             var g = createNode("group"),
                 el = createNode("shape"),
                 ol = el.style,
@@ -2299,8 +2359,8 @@ window.Raphael = (function () {
             res.setBox();
             vml.canvas[appendChild](g);
             return res;
-        };
-        var setSize = function (width, height) {
+        },
+        setSize = function (width, height) {
             var cs = this.canvas.style;
             width == +width && (width += "px");
             height == +height && (height += "px");
@@ -2308,15 +2368,16 @@ window.Raphael = (function () {
             cs.height = height;
             cs.clip = "rect(0 " + width + " " + height + " 0)";
             return this;
-        };
+        },
+        createNode;
         doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
         try {
             !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
-            var createNode = function (tagName) {
+            createNode = function (tagName) {
                 return doc.createElement('<rvml:' + tagName + ' class="rvml">');
             };
         } catch (e) {
-            var createNode = function (tagName) {
+            createNode = function (tagName) {
                 return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
             };
         }
@@ -2331,7 +2392,7 @@ window.Raphael = (function () {
             if (!container) {
                 throw new Error("VML container not found.");
             }
-            var res = {},
+            var res = new Paper,
                 c = res.canvas = doc.createElement("div"),
                 cs = c.style;
             width = width || 512;
@@ -2350,12 +2411,6 @@ window.Raphael = (function () {
                 doc.body[appendChild](c);
                 cs.left = x + "px";
                 cs.top = y + "px";
-                container = {
-                    style: {
-                        width: width,
-                        height: height
-                    }
-                };
             } else {
                 container.style.width = width;
                 container.style.height = height;
@@ -2365,22 +2420,20 @@ window.Raphael = (function () {
                     container[appendChild](c);
                 }
             }
-            for (var prop in paper) if (paper[has](prop)) {
-                res[prop] = paper[prop];
-            }
             plugins.call(res, res, R.fn);
-            res.top = res.bottom = null;
-            res.raphael = R;
             return res;
         };
-        paper.clear = function () {
+        Paper[proto].clear = function () {
             this.canvas.innerHTML = E;
+            this.span = doc.createElement("span");
+            this.span.style.cssText = "position:absolute;left:-9999px;top:-9999px;padding:0;margin:0;line-height:1;display:inline;";
+            this.canvas[appendChild](this.span);
             this.bottom = this.top = null;
         };
-        paper.remove = function () {
+        Paper[proto].remove = function () {
             this.canvas.parentNode.removeChild(this.canvas);
             for (var i in this) {
-                delete this[i];
+                this[i] = removed(i);
             }
         };
     }
@@ -2388,12 +2441,12 @@ window.Raphael = (function () {
     // rest
     // Safari or Chrome (WebKit) rendering bug workaround method
     if ((/^Apple|^Google/).test(navigator.vendor) && !(navigator.userAgent.indexOf("Version/4.0") + 1)) {
-        paper.safari = function () {
+        Paper[proto].safari = function () {
             var rect = this.rect(-99, -99, this.width + 99, this.height + 99);
             setTimeout(function () {rect.remove();});
         };
     } else {
-        paper.safari = function () {};
+        Paper[proto].safari = function () {};
     }
 
     // Events
@@ -2451,30 +2504,32 @@ window.Raphael = (function () {
     Element[proto].unhover = function (f_in, f_out) {
         return this.unmouseover(f_in).unmouseout(f_out);
     };
-    paper.circle = function (x, y, r) {
+    Paper[proto].circle = function (x, y, r) {
         return theCircle(this, x || 0, y || 0, r || 0);
     };
-    paper.rect = function (x, y, w, h, r) {
+    Paper[proto].rect = function (x, y, w, h, r) {
         return theRect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
     };
-    paper.ellipse = function (x, y, rx, ry) {
+    Paper[proto].ellipse = function (x, y, rx, ry) {
         return theEllipse(this, x || 0, y || 0, rx || 0, ry || 0);
     };
-    paper.path = function (pathString) {
+    Paper[proto].path = function (pathString) {
         pathString && !R.is(pathString, "string") && !R.is(pathString[0], "array") && (pathString += E);
         return thePath(R.format[apply](R, arguments), this);
     };
-    paper.image = function (src, x, y, w, h) {
+    Paper[proto].image = function (src, x, y, w, h) {
         return theImage(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
     };
-    paper.text = function (x, y, text) {
+    Paper[proto].text = function (x, y, text) {
         return theText(this, x || 0, y || 0, text || E);
     };
-    paper.set = function (itemsArray) {
+    Paper[proto].set = function (itemsArray) {
         arguments[length] > 1 && (itemsArray = Array[proto].splice.call(arguments, 0, arguments[length]));
         return new Set(itemsArray);
     };
-    paper.setSize = setSize;
+    Paper[proto].setSize = setSize;
+    Paper[proto].top = Paper[proto].bottom = null;
+    Paper[proto].raphael = R;
     function x_y() {
         return this.x + S + this.y;
     };
@@ -2497,23 +2552,25 @@ window.Raphael = (function () {
             var bb = this.getBBox(),
                 rcx = bb.x + bb.width / 2,
                 rcy = bb.y + bb.height / 2,
-                kx = x / this._.sx,
-                ky = y / this._.sy;
+                kx = abs(x / this._.sx),
+                ky = abs(y / this._.sy);
             cx = (+cx || cx == 0) ? cx : rcx;
             cy = (+cy || cy == 0) ? cy : rcy;
-            var dirx = ~~(x / math.abs(x)),
-                diry = ~~(y / math.abs(y)),
+            var dirx = ~~(x / abs(x)),
+                diry = ~~(y / abs(y)),
+                dkx = kx * dirx,
+                dky = ky * diry,
                 s = this.node.style,
-                ncx = cx + (rcx - cx) * kx,
-                ncy = cy + (rcy - cy) * ky;
+                ncx = cx - abs(rcx - cx) * dkx,
+                ncy = cy - abs(rcy - cy) * dky;
             switch (this.type) {
                 case "rect":
                 case "image":
-                    var neww = a.width * dirx * kx,
-                        hewh = a.height * diry * ky;
+                    var neww = a.width * kx,
+                        newh = a.height * ky;
                     this.attr({
                         height: newh,
-                        r: a.r * mmin(dirx * kx, diry * ky),
+                        r: a.r * mmin(kx, ky),
                         width: neww,
                         x: ncx - neww / 2,
                         y: ncy - newh / 2
@@ -2522,9 +2579,9 @@ window.Raphael = (function () {
                 case "circle":
                 case "ellipse":
                     this.attr({
-                        rx: a.rx * dirx * kx,
-                        ry: a.ry * diry * ky,
-                        r: a.r * mmin(dirx * kx, diry * ky),
+                        rx: a.rx * kx,
+                        ry: a.ry * ky,
+                        r: a.r * mmin(kx, ky),
                         cx: ncx,
                         cy: ncy
                     });
@@ -2542,22 +2599,22 @@ window.Raphael = (function () {
                             skip = false;
                         }
                         if (P0 == "A") {
-                            p[path[i][length] - 2] *= kx;
-                            p[path[i][length] - 1] *= ky;
-                            p[1] *= dirx * kx;
-                            p[2] *= diry * ky;
-                            p[5] = +(dirx + diry ? !!+p[5] : !+p[5]);
+                            p[path[i][length] - 2] = abs(p[path[i][length] - 2]) * dkx;
+                            p[path[i][length] - 1] = abs(p[path[i][length] - 1]) * dky;
+                            p[1] *= kx;
+                            p[2] *= ky;
+                            p[5] = +(dirx + diry ? !+p[4] : !!+p[4]);
                         } else if (P0 == "H") {
                             for (j = 1, jj = p[length]; j < jj; j++) {
-                                p[j] *= kx;
+                                p[j] = abs(p[j]) * dkx;
                             }
                         } else if (P0 == "V") {
                             for (j = 1, jj = p[length]; j < jj; j++) {
-                                p[j] *= ky;
+                                p[j] = abs(p[j]) * dky;
                             }
                          } else {
                             for (j = 1, jj = p[length]; j < jj; j++) {
-                                p[j] *= (j % 2) ? kx : ky;
+                                p[j] = abs(p[j]) * ( (j % 2) ? dkx : dky );
                             }
                         }
                     }
@@ -2756,7 +2813,7 @@ window.Raphael = (function () {
                 }
                 e.prev = time;
             }
-            R.svg && paper.safari();
+            R.svg && that.paper.safari();
             animationElements[length] && setTimeout(animation);
         },
         upto255 = function (color) {
@@ -3026,7 +3083,7 @@ window.Raphael = (function () {
         }
         return font;
     };
-    paper.getFont = function (family, weight, style, stretch) {
+    Paper[proto].getFont = function (family, weight, style, stretch) {
         stretch = stretch || "normal";
         style = style || "normal";
         weight = +weight || {normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
@@ -3051,7 +3108,7 @@ window.Raphael = (function () {
         }
         return thefont;
     };
-    paper.print = function (x, y, string, font, size, origin) {
+    Paper[proto].print = function (x, y, string, font, size, origin) {
         origin = origin || "middle"; // baseline|middle
         var out = this.set(),
             letters = (string + E)[split](E),