2 * Raphael 0.7 - JavaScript Vector Library
4 * Copyright (c) 2008 – 2009 Dmitry Baranovskiy (http://raphaeljs.com)
5 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
8 return Raphael._create.apply(Raphael, arguments);
19 var availableAttrs = {cx: 0, cy: 0, fill: "#fff", "fill-opacity": 1, font: '16px "Arial"', "font-family": '"Arial"', "font-size": "16", gradient: 0, height: 0, opacity: 1, path: "M0,0", r: 0, rotation: 0, rx: 0, ry: 0, scale: "1 1", stroke: "#000", "stroke-dasharray": "", "stroke-linecap": "butt", "stroke-linejoin": "butt", "stroke-miterlimit": 0, "stroke-opacity": 1, "stroke-width": 1, translation: "0 0", width: 0, x: 0, y: 0},
20 availableAnimAttrs = {cx: "number", cy: "number", fill: "colour", "fill-opacity": "number", "font-size": "number", height: "number", opacity: "number", path: "path", r: "number", rotation: "number", rx: "number", ry: "number", scale: "csv", stroke: "colour", "stroke-opacity": "number", "stroke-width": "number", translation: "csv", width: "number", x: "number", y: "number"};
21 R._.paper.circle = function (x, y, r) {
22 return R._.theCircle(this, x, y, r);
24 R._.paper.rect = function (x, y, w, h, r) {
25 return R._.theRect(this, x, y, w, h, r);
27 R._.paper.ellipse = function (x, y, rx, ry) {
28 return R._.theEllipse(this, x, y, rx, ry);
30 R._.paper.path = function (params, pathString) {
31 return R._.thePath(params, pathString, this);
33 R._.paper.image = function (src, x, y, w, h) {
34 return R._.theImage(this, src, x, y, w, h);
36 R._.paper.text = function (x, y, text) {
37 return R._.theText(this, x, y, text);
39 R._.paper.group = function () {
42 R._.paper.drawGrid = function (x, y, w, h, wv, hv, color) {
43 color = color || "#000";
44 var p = this.path({stroke: color, "stroke-width": 1})
45 .moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).lineTo(x, y),
48 for (var i = 1; i < hv; i++) {
49 p.moveTo(x, y + i * rowHeight).lineTo(x + w, y + i * rowHeight);
51 for (var i = 1; i < wv; i++) {
52 p.moveTo(x + i * columnWidth, y).lineTo(x + i * columnWidth, y + h);
56 R._.element.stop = function () {
57 clearTimeout(this.animation_in_progress);
59 R._.element.scale = function (x, y) {
60 if (x == undefined && y == undefined) {
61 return {x: this._.sx, y: this._.sy};
65 if (x != 0 && !(x == 1 && y == 1)) {
66 var dirx = Math.round(x / Math.abs(x)),
67 diry = Math.round(y / Math.abs(y)),
73 if (dirx != 1 || diry != 1) {
74 if (this.transformations) {
75 this.transformations[2] = "scale(" + [dirx, diry] + ")";
76 this.node.setAttribute("transform", this.transformations.join(" "));
77 dx = (dirx < 0) ? -this.attr("x") - this.attrs.width * x * dirx / this._.sx : this.attr("x");
78 dy = (diry < 0) ? -this.attr("y") - this.attrs.height * y * diry / this._.sy : this.attr("y");
79 cx = this.attr("cx") * dirx;
80 cy = this.attr("cy") * diry;
82 this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11=" + dirx +
83 ", M12=0, M21=0, M22=" + diry +
84 ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')";
85 s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
88 if (this.transformations) {
89 this.transformations[2] = "";
90 this.node.setAttribute("transform", this.transformations.join(" "));
92 this.node.filterMatrix = "";
93 s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
100 width: this.attrs.width * x * dirx / this._.sx,
101 height: this.attrs.height * y * diry / this._.sy,
109 rx: this.attrs.rx * x * dirx / this._.sx,
110 ry: this.attrs.ry * y * diry / this._.sy,
111 r: this.attrs.r * x * diry / this._.sx,
117 var path = Raphael.pathToRelative(Raphael.parsePathString(this.attr("path"))),
119 dim = Raphael.pathDimensions(this.attrs.path),
120 dx = -dim.width * (x - 1) / 2,
121 dy = -dim.height * (y - 1) / 2;
122 for (var i = 0, ii = path.length; i < ii; i++) {
123 if (path[i][0].toUpperCase() == "M" && skip) {
128 if (path[i][0].toUpperCase() == "A") {
129 path[i][path[i].length - 2] *= x * dirx;
130 path[i][path[i].length - 1] *= y * diry;
132 for (var j = 1, jj = path[i].length; j < jj; j++) {
133 path[i][j] *= (j % 2) ? x * dirx / this._.sx : y * diry / this._.sy;
137 var dim2 = Raphael.pathDimensions(path),
138 dx = dim.x + dim.width / 2 - dim2.x - dim2.width / 2,
139 dy = dim.y + dim.height / 2 - dim2.y - dim2.height / 2;
140 path = Raphael.pathToRelative(path);
144 this.attr({path: path.join(" ")});
151 R._.element.animate = function (params, ms, callback) {
152 clearTimeout(this.animation_in_progress);
153 var from = {}, to = {}, diff = {}, t = {x: 0, y: 0};
154 for (var attr in params) {
155 if (attr in availableAnimAttrs) {
156 from[attr] = this.attr(attr);
157 if (typeof from[attr] == "undefined") {
158 from[attr] = availableAttrs[attr];
160 to[attr] = params[attr];
161 switch (availableAnimAttrs[attr]) {
163 diff[attr] = (to[attr] - from[attr]) / ms;
166 from[attr] = Raphael.getRGB(from[attr]);
167 var toColour = Raphael.getRGB(to[attr]);
169 r: (toColour.r - from[attr].r) / ms,
170 g: (toColour.g - from[attr].g) / ms,
171 b: (toColour.b - from[attr].b) / ms
175 var pathes = Raphael.pathEqualiser(from[attr], to[attr]);
176 from[attr] = pathes[0];
177 to[attr] = pathes[1];
179 for (var i = 0, ii = from[attr].length; i < ii; i++) {
181 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
182 diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
187 var values = params[attr].split(/[, ]+/);
188 if (attr == "translation") {
190 diff[attr] = [values[0] / ms, values[1] / ms];
192 from[attr] = from[attr].split(/[, ]+/);
193 diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][0]) / ms];
199 var start = new Date(),
203 var time = (new Date()).getTime() - start.getTime(),
207 for (var attr in from) {
208 switch (availableAnimAttrs[attr]) {
210 now = +from[attr] + time * diff[attr];
214 Math.round(from[attr].r + time * diff[attr].r),
215 Math.round(from[attr].g + time * diff[attr].g),
216 Math.round(from[attr].b + time * diff[attr].b)
221 for (var i = 0, ii = from[attr].length; i < ii; i++) {
222 now[i] = [from[attr][i][0]];
223 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
224 now[i][j] = from[attr][i][j] + time * diff[attr][i][j];
226 now[i] = now[i].join(" ");
231 if (attr == "translation") {
232 var x = diff[attr][0] * (time - prev),
233 y = diff[attr][1] * (time - prev);
236 now = [x, y].join(" ");
238 now = [+from[attr][0] + time * diff[attr][0], +from[attr][1] + time * diff[attr][1]].join(" ");
242 if (attr == "font-size") {
243 set[attr] = now + "px";
249 that.animation_in_progress = setTimeout(arguments.callee, 0);
253 that.translate(-t.x, -t.y);
256 clearTimeout(that.animation_in_progress);
258 (typeof callback == "function") && callback.call(that);
264 R._.paper.pathfinder = function (p, path) {
269 C: function (x1, y1, x2, y2, x3, y3) {
270 this.curveTo(x1, y1, x2, y2, x3, y3);
272 Q: function (x1, y1, x2, y2) {
273 this.qcurveTo(x1, y1, x2, y2);
278 S: function (x1, y1, x2, y2) {
279 p.curveTo(x1, y1, x2, y2);
285 this.lineTo(x, this.last.y);
288 this.lineTo(this.last.x, y);
290 A: function (rx, ry, xaxisrotation, largearcflag, sweepflag, x, y) {
291 this.arcTo(rx, ry, largearcflag, sweepflag, x, y);
298 path = Raphael.pathToAbsolute(path);
299 for (var i = 0, ii = path.length; i < ii; i++) {
300 var b = path[i].shift();
301 commands[b].apply(p, path[i]);
305 R.toString = function () {
306 return "Your browser " + (this.vml ? "doesn't ": "") + "support" + (this.svg ? "s": "") +
307 " SVG.\nYou are running " + unescape("Rapha%EBl%20") + this.version;
310 R.hsb2rgb = function (hue, saturation, brightness) {
311 if (typeof hue == "object" && "h" in hue && "s" in hue && "b" in hue) {
319 if (brightness == 0) {
320 return {r: 0, g: 0, b: 0, hex: "#000"};
322 var i = Math.floor(hue * 6),
324 p = brightness * (1 - saturation),
325 q = brightness * (1 - (saturation * f)),
326 t = brightness * (1 - (saturation * (1 - f)));
328 function () {red = brightness; green = t; blue = p;},
329 function () {red = q; green = brightness; blue = p;},
330 function () {red = p; green = brightness; blue = t;},
331 function () {red = p; green = q; blue = brightness;},
332 function () {red = t; green = p; blue = brightness;},
333 function () {red = brightness; green = p; blue = q;},
334 function () {red = brightness; green = t; blue = p;}
337 var rgb = {r: red, g: green, b: blue};
341 var r = Math.round(red).toString(16);
345 var g = Math.round(green).toString(16);
349 var b = Math.round(blue).toString(16);
353 rgb.hex = "#" + r + g + b;
356 R.rgb2hsb = function (red, green, blue) {
357 if (typeof red == "object" && "r" in red && "g" in red && "b" in red) {
362 if (typeof red == "string" && red.charAt(0) == "#") {
363 if (red.length == 4) {
364 blue = parseInt(red.substring(3), 16);
365 green = parseInt(red.substring(2, 3), 16);
366 red = parseInt(red.substring(1, 2), 16);
368 blue = parseInt(red.substring(5), 16);
369 green = parseInt(red.substring(3, 5), 16);
370 red = parseInt(red.substring(1, 3), 16);
373 if (red > 1 || green > 1 || blue > 1) {
378 var max = Math.max(red, green, blue),
379 min = Math.min(red, green, blue),
384 return {h: 0, s: 0, b: max};
386 var delta = (max - min);
387 saturation = delta / max;
389 hue = (green - blue) / delta;
390 } else if (green == max) {
391 hue = 2 + ((blue - red) / delta);
393 hue = 4 + ((red - green) / delta);
403 return {h: hue, s: saturation, b: brightness};
405 R.getRGB = function (colour) {
406 var red, green, blue,
407 rgb = colour.match(/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*(\d+,\s*\d+,\s*\d+)\s*\)|rgb\(\s*(\d+%,\s*\d+%,\s*\d+%)\s*\)|hsb\(\s*(\d+,\s*\d+,\s*\d+)\s*\)|hsb\(\s*(\d+%,\s*\d+%,\s*\d+%)\s*\))\s*$/i);
410 blue = parseInt(rgb[2].substring(5), 16);
411 green = parseInt(rgb[2].substring(3, 5), 16);
412 red = parseInt(rgb[2].substring(1, 3), 16);
415 blue = parseInt(rgb[3].substring(3) + rgb[3].substring(3), 16);
416 green = parseInt(rgb[3].substring(2, 3) + rgb[3].substring(2, 3), 16);
417 red = parseInt(rgb[3].substring(1, 2) + rgb[3].substring(1, 2), 16);
420 rgb = rgb[4].split(/\s*,\s*/);
421 red = parseInt(rgb[0], 10);
422 green = parseInt(rgb[1], 10);
423 blue = parseInt(rgb[2], 10);
426 rgb = rgb[5].split(/\s*,\s*/);
427 red = parseInt(rgb[0], 10) * 2.55;
428 green = parseInt(rgb[1], 10) * 2.55;
429 blue = parseInt(rgb[2], 10) * 2.55;
432 rgb = rgb[6].split(/\s*,\s*/);
433 red = parseInt(rgb[0], 10);
434 green = parseInt(rgb[1], 10);
435 blue = parseInt(rgb[2], 10);
436 return this.hsb2rgb(red, green, blue);
439 rgb = rgb[7].split(/\s*,\s*/);
440 red = parseInt(rgb[0], 10) * 2.55;
441 green = parseInt(rgb[1], 10) * 2.55;
442 blue = parseInt(rgb[2], 10) * 2.55;
443 return this.hsb2rgb(red, green, blue);
445 var rgb = {r: red, g: green, b: blue};
446 var r = Math.round(red).toString(16);
447 (r.length == 1) && (r = "0" + r);
448 var g = Math.round(green).toString(16);
449 (g.length == 1) && (g = "0" + g);
450 var b = Math.round(blue).toString(16);
451 (b.length == 1) && (b = "0" + b);
452 rgb.hex = "#" + r + g + b;
456 R.getColor = function (value) {
457 var start = arguments.callee.start = arguments.callee.start || {h: 0, s: 1, b: value || .75};
458 var rgb = this.hsb2rgb(start.h, start.s, start.b);
464 arguments.callee.start = {h: 0, s: 1, b: start.b};
469 R.getColor.reset = function () {
470 this.start = undefined;
472 R.parsePathString = function (pathString) {
473 var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
475 toString = function () {
477 for (var i = 0, ii = this.length; i < ii; i++) {
478 res += this[i][0] + this[i].join(",").substring(2);
482 if (pathString.toString.toString() == toString.toString()) {
485 pathString.replace(/([achlmqstvz])[\s,]*((-?\d*\.?\d*\s*,?\s*)+)/ig, function (a, b, c) {
486 var params = [], name = b.toLowerCase();
487 c.replace(/(-?\d*\.?\d*)\s*,?\s*/ig, function (a, b) {
488 b && params.push(+b);
490 while (params.length >= paramCounts[name]) {
491 data.push([b].concat(params.splice(0, paramCounts[name])));
492 if (!paramCounts[name]) {
497 data.toString = toString;
500 R.pathDimensions = function (path) {
501 var pathArray = path;
502 if (typeof path == "string") {
503 pathArray = this.parsePathString(path);
505 pathArray = this.pathToAbsolute(pathArray);
506 var x = [], y = [], length = 0;
507 for (var i = 0, ii = pathArray.length; i < ii; i++) {
508 switch (pathArray[i][0]) {
512 x.push(pathArray[i][pathArray[i].length - 2]);
513 y.push(pathArray[i][pathArray[i].length - 1]);
516 for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
518 x.push(pathArray[i][j]);
520 y.push(pathArray[i][j]);
525 var minx = Math.min.apply(Math, x),
526 miny = Math.min.apply(Math, y);
530 width: Math.max.apply(Math, x) - minx,
531 height: Math.max.apply(Math, y) - miny,
536 R.pathToRelative = function (pathArray) {
538 if (typeof pathArray == "string") {
539 pathArray = this.parsePathString(pathArray);
541 var x = 0, y = 0, start = 0;
542 if (pathArray[0][0] == "M") {
546 res.push(pathArray[0]);
548 for (var i = start, ii = pathArray.length; i < ii; i++) {
550 if (pathArray[i][0] != pathArray[i][0].toLowerCase()) {
551 res[i][0] = pathArray[i][0].toLowerCase();
554 res[i][1] = pathArray[i][1];
555 res[i][2] = pathArray[i][2];
557 res[i][4] = pathArray[i][4];
558 res[i][5] = pathArray[i][5];
559 res[i][6] = +(pathArray[i][6] - x).toFixed(3);
560 res[i][7] = +(pathArray[i][7] - y).toFixed(3);
563 res[i][1] = +(pathArray[i][1] - y).toFixed(3);
566 for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
567 res[i][j] = +(pathArray[i][j] - ((j % 2) ? x : y)).toFixed(3);
571 res[i] = pathArray[i];
577 x += res[i][res[i].length - 1];
580 y += res[i][res[i].length - 1];
583 x += res[i][res[i].length - 2];
584 y += res[i][res[i].length - 1];
587 res.toString = pathArray.toString;
590 R.pathToAbsolute = function (pathArray) {
592 if (typeof pathArray == "string") {
593 pathArray = this.parsePathString(pathArray);
595 var x = 0, y = 0, start = 0;
596 if (pathArray[0][0] == "M") {
597 x = +pathArray[0][1];
598 y = +pathArray[0][2];
600 res[0] = pathArray[0];
602 for (var i = start, ii = pathArray.length; i < ii; i++) {
604 if (pathArray[i][0] != pathArray[i][0].toUpperCase()) {
605 res[i][0] = pathArray[i][0].toUpperCase();
608 res[i][1] = pathArray[i][1];
609 res[i][2] = pathArray[i][2];
611 res[i][4] = pathArray[i][4];
612 res[i][5] = pathArray[i][5];
613 res[i][6] = +(pathArray[i][6] + x).toFixed(3);
614 res[i][7] = +(pathArray[i][7] + y).toFixed(3);
617 res[i][1] = +pathArray[i][1] + y;
620 for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
621 res[i][j] = +pathArray[i][j] + ((j % 2) ? x : y);
625 res[i] = pathArray[i];
637 x = res[i][res[i].length - 2];
638 y = res[i][res[i].length - 1];
641 res.toString = pathArray.toString;
644 R.pathEqualiser = function (path1, path2) {
645 var data = [this.pathToAbsolute(this.parsePathString(path1)), this.pathToAbsolute(this.parsePathString(path2))],
646 attrs = [{x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}, {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0}],
647 processPath = function (path, d) {
657 var nx = d.x + (d.x - (d.bx || d.x));
658 var ny = d.y + (d.y - (d.by || d.y));
659 path = ["C", nx, ny, path[1], path[2], path[3], path[4]];
662 var nx = d.x + (d.x - (d.bx || d.x));
663 var ny = d.y + (d.y - (d.by || d.y));
664 path = ["Q", nx, ny, path[1], path[2]];
667 path = ["L", path[1], d.y];
670 path = ["L", d.x, path[1]];
673 path = ["L", d.X, d.Y];
678 edgeCases = function (a, b, i) {
679 if (data[a][i][0] == "M" && data[b][i][0] != "M") {
680 data[b].splice(i, 0, ["M", attrs[b].x, attrs[b].y]);
681 attrs[a].bx = data[a][i][data[a][i].length - 4] || 0;
682 attrs[a].by = data[a][i][data[a][i].length - 3] || 0;
683 attrs[a].x = data[a][i][data[a][i].length - 2];
684 attrs[a].y = data[a][i][data[a][i].length - 1];
686 } else if (data[a][i][0] == "L" && data[b][i][0] == "C") {
687 data[a][i] = ["C", attrs[a].x, attrs[a].y, data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]];
688 } else if (data[a][i][0] == "L" && data[b][i][0] == "Q") {
689 data[a][i] = ["Q", data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]];
690 } else if (data[a][i][0] == "Q" && data[b][i][0] == "C") {
691 var x = data[b][i][data[b][i].length - 2];
692 var y = data[b][i][data[b][i].length - 1];
693 data[b].splice(i + 1, 0, ["Q", x, y, x, y]);
694 data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]);
696 attrs[b].bx = data[b][i][data[b][i].length - 4] || 0;
697 attrs[b].by = data[b][i][data[b][i].length - 3] || 0;
698 attrs[b].x = data[b][i][data[b][i].length - 2];
699 attrs[b].y = data[b][i][data[b][i].length - 1];
701 } else if (data[a][i][0] == "A" && data[b][i][0] == "C") {
702 var x = data[b][i][data[b][i].length - 2];
703 var y = data[b][i][data[b][i].length - 1];
704 data[b].splice(i + 1, 0, ["A", 0, 0, data[a][i][3], data[a][i][4], data[a][i][5], x, y]);
705 data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]);
707 attrs[b].bx = data[b][i][data[b][i].length - 4] || 0;
708 attrs[b].by = data[b][i][data[b][i].length - 3] || 0;
709 attrs[b].x = data[b][i][data[b][i].length - 2];
710 attrs[b].y = data[b][i][data[b][i].length - 1];
712 } else if (data[a][i][0] == "U") {
713 data[a][i][0] = data[b][i][0];
714 for (var j = 1, jj = data[b][i].length; j < jj; j++) {
715 data[a][i][j] = (j % 2) ? attrs[a].x : attrs[a].y;
720 for (var i = 0; i < Math.max(data[0].length, data[1].length); i++) {
721 data[0][i] = processPath(data[0][i], attrs[0]);
722 data[1][i] = processPath(data[1][i], attrs[1]);
723 if (data[0][i][0] != data[1][i][0] && (edgeCases(0, 1, i) || edgeCases(1, 0, i))) {
726 attrs[0].bx = data[0][i][data[0][i].length - 4] || 0;
727 attrs[0].by = data[0][i][data[0][i].length - 3] || 0;
728 attrs[0].x = data[0][i][data[0][i].length - 2];
729 attrs[0].y = data[0][i][data[0][i].length - 1];
730 attrs[1].bx = data[1][i][data[1][i].length - 4] || 0;
731 attrs[1].by = data[1][i][data[1][i].length - 3] || 0;
732 attrs[1].x = data[1][i][data[1][i].length - 2];
733 attrs[1].y = data[1][i][data[1][i].length - 1];
738 var script = document.getElementsByTagName("script"),
739 newscript = document.createElement("script");
740 script = script[script.length - 1];
741 newscript.type = "text/javascript";
742 newscript.src = window.SVGAngle ? "raphael-svg.js" : "raphael-vml.js";
743 script.parentNode.appendChild(newscript);