Savage.plugin(function (Savage, Element, Paper, glob) { var names = {}, reUnit = /[a-z]+$/i, Str = String; names.stroke = names.fill = "colour"; function getEmpty(item) { var l = item[0]; switch (l.toLowerCase()) { case "t": return [l, 0, 0]; case "m": return [l, 1, 0, 0, 1, 0, 0]; case "r": if (item.length == 4) { return [l, 0, item[2], item[3]]; } else { return [l, 0]; } case "s": if (item.length == 5) { return [l, 1, 1, item[3], item[4]]; } else if (item.length == 3) { return [l, 1, 1]; } else { return [l, 1]; } } } function equaliseTransform(t1, t2) { t2 = Str(t2).replace(/\.{3}|\u2026/g, t1); t1 = Savage.parseTransformString(t1) || []; t2 = Savage.parseTransformString(t2) || []; var maxlength = Math.max(t1.length, t2.length), from = [], to = [], i = 0, j, jj, tt1, tt2; for (; i < maxlength; i++) { tt1 = t1[i] || getEmpty(t2[i]); tt2 = t2[i] || getEmpty(tt1); if ((tt1[0] != tt2[0]) || (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) || (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4])) ) { return; } from[i] = []; to[i] = []; for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) { j in tt1 && (from[i][j] = tt1[j]); j in tt2 && (to[i][j] = tt2[j]); } } return { from: path2array(from), to: path2array(to), f: getPath(from) }; } function getNumber(val) { return val; } function getUnit(unit) { return function (val) { return +val.toFixed(3) + unit; }; } function getColour(clr) { return Savage.rgb(clr[0], clr[1], clr[2]); } function getPath(path) { var k = 0, i, ii, j, jj, out, a, b = []; for (i = 0, ii = path.length; i < ii; i++) { out = "["; a = ['"' + path[i][0] + '"']; for (j = 1, jj = path[i].length; j < jj; j++) { a[j] = "val[" + (k++) + "]"; } out += a + "]"; b[i] = out; } return Function("val", "return Savage.path.toString.call([" + b + "])"); } function path2array(path) { var out = []; for (var i = 0, ii = path.length; i < ii; i++) { for (var j = 1, jj = path[i].length; j < jj; j++) { out.push(path[i][j]); } } return out; } Element.prototype.equal = function (name, b) { var A, B, a = Str(this.attr(name) || ""); if (a == +a && b == +b) { return { from: +a, to: +b, f: getNumber }; } if (names[name] == "colour") { A = Savage.color(a); B = Savage.color(b); return { from: [A.r, A.g, A.b, A.opacity], to: [B.r, B.g, B.b, B.opacity], f: getColour }; } if (name == "transform" || name == "gradientTransform" || name == "patternTransform") { // TODO: b could be an SVG transform string or matrix return equaliseTransform(a.local, b); } if (name == "d" || name == "path") { A = Savage.path.toCubic(a, b); return { from: path2array(A[0]), to: path2array(A[1]), f: getPath(A[0]) }; } var aUnit = a.match(reUnit), bUnit = b.match(reUnit); if (aUnit && aUnit == bUnit) { return { from: parseFloat(a), to: parseFloat(b), f: getUnit(aUnit) }; } else { return { from: this.asPX(name), to: this.asPX(name, b), f: getNumber }; } }; });