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);
20 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},
21 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"};
22 R._.paper.circle = function (x, y, r) {
23 return R._.theCircle(this, x, y, r);
25 R._.paper.rect = function (x, y, w, h, r) {
26 return R._.theRect(this, x, y, w, h, r);
28 R._.paper.ellipse = function (x, y, rx, ry) {
29 return R._.theEllipse(this, x, y, rx, ry);
31 R._.paper.path = function (params, pathString) {
32 return R._.thePath(params, pathString, this);
34 R._.paper.image = function (src, x, y, w, h) {
35 return R._.theImage(this, src, x, y, w, h);
37 R._.paper.text = function (x, y, text) {
38 return R._.theText(this, x, y, text);
40 R._.paper.group = function () {
43 R._.paper.drawGrid = function (x, y, w, h, wv, hv, color) {
44 color = color || "#000";
45 var p = this.path({stroke: color, "stroke-width": 1})
46 .moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).lineTo(x, y),
49 for (var i = 1; i < hv; i++) {
50 p.moveTo(x, y + i * rowHeight).lineTo(x + w, y + i * rowHeight);
52 for (var i = 1; i < wv; i++) {
53 p.moveTo(x + i * columnWidth, y).lineTo(x + i * columnWidth, y + h);
57 R._.element.stop = function () {
58 clearTimeout(this.animation_in_progress);
60 R._.element.scale = function (x, y) {
61 if (x == undefined && y == undefined) {
62 return {x: this._.sx, y: this._.sy};
66 if (x != 0 && !(x == 1 && y == 1)) {
67 var dirx = Math.round(x / Math.abs(x)),
68 diry = Math.round(y / Math.abs(y)),
74 if (dirx != 1 || diry != 1) {
75 if (this.transformations) {
76 this.transformations[2] = "scale(" + [dirx, diry] + ")";
77 this.node.setAttribute("transform", this.transformations.join(" "));
78 dx = (dirx < 0) ? -this.attr("x") - this.attrs.width * x * dirx / this._.sx : this.attr("x");
79 dy = (diry < 0) ? -this.attr("y") - this.attrs.height * y * diry / this._.sy : this.attr("y");
80 cx = this.attr("cx") * dirx;
81 cy = this.attr("cy") * diry;
83 this.node.filterMatrix = " progid:DXImageTransform.Microsoft.Matrix(M11=" + dirx +
84 ", M12=0, M21=0, M22=" + diry +
85 ", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')";
86 s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
89 if (this.transformations) {
90 this.transformations[2] = "";
91 this.node.setAttribute("transform", this.transformations.join(" "));
93 this.node.filterMatrix = "";
94 s.filter = (this.node.filterMatrix || "") + (this.node.filterOpacity || "");
101 width: this.attrs.width * x * dirx / this._.sx,
102 height: this.attrs.height * y * diry / this._.sy,
110 rx: this.attrs.rx * x * dirx / this._.sx,
111 ry: this.attrs.ry * y * diry / this._.sy,
112 r: this.attrs.r * x * diry / this._.sx,
118 var path = Raphael.pathToRelative(Raphael.parsePathString(this.attr("path"))),
120 dim = Raphael.pathDimensions(this.attrs.path),
121 dx = -dim.width * (x - 1) / 2,
122 dy = -dim.height * (y - 1) / 2;
123 for (var i = 0, ii = path.length; i < ii; i++) {
124 if (path[i][0].toUpperCase() == "M" && skip) {
129 if (path[i][0].toUpperCase() == "A") {
130 path[i][path[i].length - 2] *= x * dirx;
131 path[i][path[i].length - 1] *= y * diry;
133 for (var j = 1, jj = path[i].length; j < jj; j++) {
134 path[i][j] *= (j % 2) ? x * dirx / this._.sx : y * diry / this._.sy;
138 var dim2 = Raphael.pathDimensions(path),
139 dx = dim.x + dim.width / 2 - dim2.x - dim2.width / 2,
140 dy = dim.y + dim.height / 2 - dim2.y - dim2.height / 2;
141 path = Raphael.pathToRelative(path);
145 this.attr({path: path.join(" ")});
152 R._.element.animate = function (params, ms, callback) {
153 clearTimeout(this.animation_in_progress);
154 var from = {}, to = {}, diff = {}, t = {x: 0, y: 0};
155 for (var attr in params) {
156 if (attr in availableAnimAttrs) {
157 from[attr] = this.attr(attr);
158 if (typeof from[attr] == "undefined") {
159 from[attr] = availableAttrs[attr];
161 to[attr] = params[attr];
162 switch (availableAnimAttrs[attr]) {
164 diff[attr] = (to[attr] - from[attr]) / ms;
167 from[attr] = Raphael.getRGB(from[attr]);
168 var toColour = Raphael.getRGB(to[attr]);
170 r: (toColour.r - from[attr].r) / ms,
171 g: (toColour.g - from[attr].g) / ms,
172 b: (toColour.b - from[attr].b) / ms
176 var pathes = Raphael.pathEqualiser(from[attr], to[attr]);
177 from[attr] = pathes[0];
178 to[attr] = pathes[1];
180 for (var i = 0, ii = from[attr].length; i < ii; i++) {
182 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
183 diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
188 var values = params[attr].split(/[, ]+/);
189 if (attr == "translation") {
191 diff[attr] = [values[0] / ms, values[1] / ms];
193 from[attr] = from[attr].split(/[, ]+/);
194 diff[attr] = [(values[0] - from[attr][0]) / ms, (values[1] - from[attr][0]) / ms];
200 var start = new Date(),
204 var time = (new Date()).getTime() - start.getTime(),
208 for (var attr in from) {
209 switch (availableAnimAttrs[attr]) {
211 now = +from[attr] + time * diff[attr];
215 Math.round(from[attr].r + time * diff[attr].r),
216 Math.round(from[attr].g + time * diff[attr].g),
217 Math.round(from[attr].b + time * diff[attr].b)
222 for (var i = 0, ii = from[attr].length; i < ii; i++) {
223 now[i] = [from[attr][i][0]];
224 for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
225 now[i][j] = from[attr][i][j] + time * diff[attr][i][j];
227 now[i] = now[i].join(" ");
232 if (attr == "translation") {
233 var x = diff[attr][0] * (time - prev),
234 y = diff[attr][1] * (time - prev);
237 now = [x, y].join(" ");
239 now = [+from[attr][0] + time * diff[attr][0], +from[attr][1] + time * diff[attr][1]].join(" ");
243 if (attr == "font-size") {
244 set[attr] = now + "px";
250 that.animation_in_progress = setTimeout(arguments.callee, 0);
254 that.translate(-t.x, -t.y);
257 clearTimeout(that.animation_in_progress);
259 (typeof callback == "function") && callback.call(that);
265 R._.paper.pathfinder = function (p, path) {
270 C: function (x1, y1, x2, y2, x3, y3) {
271 this.curveTo(x1, y1, x2, y2, x3, y3);
273 Q: function (x1, y1, x2, y2) {
274 this.qcurveTo(x1, y1, x2, y2);
279 S: function (x1, y1, x2, y2) {
280 p.curveTo(x1, y1, x2, y2);
286 this.lineTo(x, this.last.y);
289 this.lineTo(this.last.x, y);
291 A: function (rx, ry, xaxisrotation, largearcflag, sweepflag, x, y) {
292 this.arcTo(rx, ry, largearcflag, sweepflag, x, y);
299 path = Raphael.pathToAbsolute(path);
300 for (var i = 0, ii = path.length; i < ii; i++) {
301 var b = path[i].shift();
302 commands[b].apply(p, path[i]);
306 R.toString = function () {
307 return "Your browser " + (this.vml ? "doesn't ": "") + "support" + (this.svg ? "s": "") +
308 " SVG.\nYou are running " + unescape("Rapha%EBl%20") + this.version;
311 R.hsb2rgb = function (hue, saturation, brightness) {
312 if (typeof hue == "object" && "h" in hue && "s" in hue && "b" in hue) {
320 if (brightness == 0) {
321 return {r: 0, g: 0, b: 0, hex: "#000"};
323 var i = Math.floor(hue * 6),
325 p = brightness * (1 - saturation),
326 q = brightness * (1 - (saturation * f)),
327 t = brightness * (1 - (saturation * (1 - f)));
329 function () {red = brightness; green = t; blue = p;},
330 function () {red = q; green = brightness; blue = p;},
331 function () {red = p; green = brightness; blue = t;},
332 function () {red = p; green = q; blue = brightness;},
333 function () {red = t; green = p; blue = brightness;},
334 function () {red = brightness; green = p; blue = q;},
335 function () {red = brightness; green = t; blue = p;}
338 var rgb = {r: red, g: green, b: blue};
342 var r = Math.round(red).toString(16);
346 var g = Math.round(green).toString(16);
350 var b = Math.round(blue).toString(16);
354 rgb.hex = "#" + r + g + b;
357 R.rgb2hsb = function (red, green, blue) {
358 if (typeof red == "object" && "r" in red && "g" in red && "b" in red) {
363 if (typeof red == "string" && red.charAt(0) == "#") {
364 if (red.length == 4) {
365 blue = parseInt(red.substring(3), 16);
366 green = parseInt(red.substring(2, 3), 16);
367 red = parseInt(red.substring(1, 2), 16);
369 blue = parseInt(red.substring(5), 16);
370 green = parseInt(red.substring(3, 5), 16);
371 red = parseInt(red.substring(1, 3), 16);
374 if (red > 1 || green > 1 || blue > 1) {
379 var max = Math.max(red, green, blue),
380 min = Math.min(red, green, blue),
385 return {h: 0, s: 0, b: max};
387 var delta = (max - min);
388 saturation = delta / max;
390 hue = (green - blue) / delta;
391 } else if (green == max) {
392 hue = 2 + ((blue - red) / delta);
394 hue = 4 + ((red - green) / delta);
404 return {h: hue, s: saturation, b: brightness};
406 R.getRGB = function (colour) {
407 var red, green, blue,
408 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);
411 blue = parseInt(rgb[2].substring(5), 16);
412 green = parseInt(rgb[2].substring(3, 5), 16);
413 red = parseInt(rgb[2].substring(1, 3), 16);
416 blue = parseInt(rgb[3].substring(3) + rgb[3].substring(3), 16);
417 green = parseInt(rgb[3].substring(2, 3) + rgb[3].substring(2, 3), 16);
418 red = parseInt(rgb[3].substring(1, 2) + rgb[3].substring(1, 2), 16);
421 rgb = rgb[4].split(/\s*,\s*/);
422 red = parseInt(rgb[0], 10);
423 green = parseInt(rgb[1], 10);
424 blue = parseInt(rgb[2], 10);
427 rgb = rgb[5].split(/\s*,\s*/);
428 red = parseInt(rgb[0], 10) * 2.55;
429 green = parseInt(rgb[1], 10) * 2.55;
430 blue = parseInt(rgb[2], 10) * 2.55;
433 rgb = rgb[6].split(/\s*,\s*/);
434 red = parseInt(rgb[0], 10);
435 green = parseInt(rgb[1], 10);
436 blue = parseInt(rgb[2], 10);
437 return this.hsb2rgb(red, green, blue);
440 rgb = rgb[7].split(/\s*,\s*/);
441 red = parseInt(rgb[0], 10) * 2.55;
442 green = parseInt(rgb[1], 10) * 2.55;
443 blue = parseInt(rgb[2], 10) * 2.55;
444 return this.hsb2rgb(red, green, blue);
446 var rgb = {r: red, g: green, b: blue};
447 var r = Math.round(red).toString(16);
448 (r.length == 1) && (r = "0" + r);
449 var g = Math.round(green).toString(16);
450 (g.length == 1) && (g = "0" + g);
451 var b = Math.round(blue).toString(16);
452 (b.length == 1) && (b = "0" + b);
453 rgb.hex = "#" + r + g + b;
457 R.getColor = function (value) {
458 var start = arguments.callee.start = arguments.callee.start || {h: 0, s: 1, b: value || .75};
459 var rgb = this.hsb2rgb(start.h, start.s, start.b);
465 arguments.callee.start = {h: 0, s: 1, b: start.b};
470 R.getColor.reset = function () {
471 this.start = undefined;
473 R.parsePathString = function (pathString) {
474 var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
476 toString = function () {
478 for (var i = 0, ii = this.length; i < ii; i++) {
479 res += this[i][0] + this[i].join(",").substring(2);
483 if (pathString.toString.toString() == toString.toString()) {
486 pathString.replace(/([achlmqstvz])[\s,]*((-?\d*\.?\d*\s*,?\s*)+)/ig, function (a, b, c) {
487 var params = [], name = b.toLowerCase();
488 c.replace(/(-?\d*\.?\d*)\s*,?\s*/ig, function (a, b) {
489 b && params.push(+b);
491 while (params.length >= paramCounts[name]) {
492 data.push([b].concat(params.splice(0, paramCounts[name])));
493 if (!paramCounts[name]) {
498 data.toString = toString;
501 R.pathDimensions = function (path) {
502 var pathArray = path;
503 if (typeof path == "string") {
504 pathArray = this.parsePathString(path);
506 pathArray = this.pathToAbsolute(pathArray);
507 var x = [], y = [], length = 0;
508 for (var i = 0, ii = pathArray.length; i < ii; i++) {
509 switch (pathArray[i][0]) {
513 x.push(pathArray[i][pathArray[i].length - 2]);
514 y.push(pathArray[i][pathArray[i].length - 1]);
517 for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
519 x.push(pathArray[i][j]);
521 y.push(pathArray[i][j]);
526 var minx = Math.min.apply(Math, x),
527 miny = Math.min.apply(Math, y);
531 width: Math.max.apply(Math, x) - minx,
532 height: Math.max.apply(Math, y) - miny,
537 R.pathToRelative = function (pathArray) {
539 if (typeof pathArray == "string") {
540 pathArray = this.parsePathString(pathArray);
542 var x = 0, y = 0, start = 0;
543 if (pathArray[0][0] == "M") {
547 res.push(pathArray[0]);
549 for (var i = start, ii = pathArray.length; i < ii; i++) {
551 if (pathArray[i][0] != pathArray[i][0].toLowerCase()) {
552 res[i][0] = pathArray[i][0].toLowerCase();
555 res[i][1] = pathArray[i][1];
556 res[i][2] = pathArray[i][2];
558 res[i][4] = pathArray[i][4];
559 res[i][5] = pathArray[i][5];
560 res[i][6] = +(pathArray[i][6] - x).toFixed(3);
561 res[i][7] = +(pathArray[i][7] - y).toFixed(3);
564 res[i][1] = +(pathArray[i][1] - y).toFixed(3);
567 for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
568 res[i][j] = +(pathArray[i][j] - ((j % 2) ? x : y)).toFixed(3);
572 res[i] = pathArray[i];
578 x += res[i][res[i].length - 1];
581 y += res[i][res[i].length - 1];
584 x += res[i][res[i].length - 2];
585 y += res[i][res[i].length - 1];
588 res.toString = pathArray.toString;
591 R.pathToAbsolute = function (pathArray) {
593 if (typeof pathArray == "string") {
594 pathArray = this.parsePathString(pathArray);
596 var x = 0, y = 0, start = 0;
597 if (pathArray[0][0] == "M") {
598 x = +pathArray[0][1];
599 y = +pathArray[0][2];
601 res[0] = pathArray[0];
603 for (var i = start, ii = pathArray.length; i < ii; i++) {
605 if (pathArray[i][0] != pathArray[i][0].toUpperCase()) {
606 res[i][0] = pathArray[i][0].toUpperCase();
609 res[i][1] = pathArray[i][1];
610 res[i][2] = pathArray[i][2];
612 res[i][4] = pathArray[i][4];
613 res[i][5] = pathArray[i][5];
614 res[i][6] = +(pathArray[i][6] + x).toFixed(3);
615 res[i][7] = +(pathArray[i][7] + y).toFixed(3);
618 res[i][1] = +pathArray[i][1] + y;
621 for (var j = 1, jj = pathArray[i].length; j < jj; j++) {
622 res[i][j] = +pathArray[i][j] + ((j % 2) ? x : y);
626 res[i] = pathArray[i];
638 x = res[i][res[i].length - 2];
639 y = res[i][res[i].length - 1];
642 res.toString = pathArray.toString;
645 R.pathEqualiser = function (path1, path2) {
646 var data = [this.pathToAbsolute(this.parsePathString(path1)), this.pathToAbsolute(this.parsePathString(path2))],
647 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}],
648 processPath = function (path, d) {
658 var nx = d.x + (d.x - (d.bx || d.x));
659 var ny = d.y + (d.y - (d.by || d.y));
660 path = ["C", nx, ny, path[1], path[2], path[3], path[4]];
663 var nx = d.x + (d.x - (d.bx || d.x));
664 var ny = d.y + (d.y - (d.by || d.y));
665 path = ["Q", nx, ny, path[1], path[2]];
668 path = ["L", path[1], d.y];
671 path = ["L", d.x, path[1]];
674 path = ["L", d.X, d.Y];
679 edgeCases = function (a, b, i) {
680 if (data[a][i][0] == "M" && data[b][i][0] != "M") {
681 data[b].splice(i, 0, ["M", attrs[b].x, attrs[b].y]);
682 attrs[a].bx = data[a][i][data[a][i].length - 4] || 0;
683 attrs[a].by = data[a][i][data[a][i].length - 3] || 0;
684 attrs[a].x = data[a][i][data[a][i].length - 2];
685 attrs[a].y = data[a][i][data[a][i].length - 1];
687 } else if (data[a][i][0] == "L" && data[b][i][0] == "C") {
688 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]];
689 } else if (data[a][i][0] == "L" && data[b][i][0] == "Q") {
690 data[a][i] = ["Q", data[a][i][1], data[a][i][2], data[a][i][1], data[a][i][2]];
691 } else if (data[a][i][0] == "Q" && data[b][i][0] == "C") {
692 var x = data[b][i][data[b][i].length - 2];
693 var y = data[b][i][data[b][i].length - 1];
694 data[b].splice(i + 1, 0, ["Q", x, y, x, y]);
695 data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]);
697 attrs[b].bx = data[b][i][data[b][i].length - 4] || 0;
698 attrs[b].by = data[b][i][data[b][i].length - 3] || 0;
699 attrs[b].x = data[b][i][data[b][i].length - 2];
700 attrs[b].y = data[b][i][data[b][i].length - 1];
702 } else if (data[a][i][0] == "A" && data[b][i][0] == "C") {
703 var x = data[b][i][data[b][i].length - 2];
704 var y = data[b][i][data[b][i].length - 1];
705 data[b].splice(i + 1, 0, ["A", 0, 0, data[a][i][3], data[a][i][4], data[a][i][5], x, y]);
706 data[a].splice(i, 0, ["C", attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y, attrs[a].x, attrs[a].y]);
708 attrs[b].bx = data[b][i][data[b][i].length - 4] || 0;
709 attrs[b].by = data[b][i][data[b][i].length - 3] || 0;
710 attrs[b].x = data[b][i][data[b][i].length - 2];
711 attrs[b].y = data[b][i][data[b][i].length - 1];
713 } else if (data[a][i][0] == "U") {
714 data[a][i][0] = data[b][i][0];
715 for (var j = 1, jj = data[b][i].length; j < jj; j++) {
716 data[a][i][j] = (j % 2) ? attrs[a].x : attrs[a].y;
721 for (var i = 0; i < Math.max(data[0].length, data[1].length); i++) {
722 data[0][i] = processPath(data[0][i], attrs[0]);
723 data[1][i] = processPath(data[1][i], attrs[1]);
724 if (data[0][i][0] != data[1][i][0] && (edgeCases(0, 1, i) || edgeCases(1, 0, i))) {
727 attrs[0].bx = data[0][i][data[0][i].length - 4] || 0;
728 attrs[0].by = data[0][i][data[0][i].length - 3] || 0;
729 attrs[0].x = data[0][i][data[0][i].length - 2];
730 attrs[0].y = data[0][i][data[0][i].length - 1];
731 attrs[1].bx = data[1][i][data[1][i].length - 4] || 0;
732 attrs[1].by = data[1][i][data[1][i].length - 3] || 0;
733 attrs[1].x = data[1][i][data[1][i].length - 2];
734 attrs[1].y = data[1][i][data[1][i].length - 1];
739 var script = document.getElementsByTagName("script"),
740 newscript = document.createElement("script");
741 script = script[script.length - 1];
742 var path = script.src.match(/.*\//);
743 path = path ? path[0] : "";
744 newscript.type = "text/javascript";
745 newscript.src = path + (window.SVGAngle ? "raphael-svg.js" : "raphael-vml.js");
746 script.parentNode.appendChild(newscript);