Fix for bug #17. New method Savage.angle. Fixed handling of <defs>. Fix use.getBBox. New method Element.toDefs(). Fix for gradient rgba. Fix for double defs.

master
Dmitry Baranovskiy 2013-09-19 21:03:24 +10:00
parent 38ce178bb1
commit 38696874c1
6 changed files with 602 additions and 381 deletions

View File

@ -27,3 +27,9 @@ _*Savage uses Grunt 0.4.0. You might want to [read](http://gruntjs.com/getting-s
* Type `grunt` in the command line to build the files. * Type `grunt` in the command line to build the files.
* The results will be built into the release folder. * The results will be built into the release folder.
* Alternatively type `grunt watch` to have the build run automatically when you make changes to source files. * Alternatively type `grunt watch` to have the build run automatically when you make changes to source files.
* For documentation generation use this command
cd node_modules/dr.js/; node dr ../../dr.json; cd ../../
_Its ugly at the moment. Somebody should port it to grunt. May be even me :)_

258
dist/reference.html vendored

File diff suppressed because one or more lines are too long

8
dist/savage-min.js vendored

File diff suppressed because one or more lines are too long

374
dist/savage.js vendored
View File

@ -28,7 +28,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// //
// build: 2013-09-17 // build: 2013-09-19
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved. // Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -479,6 +479,23 @@ var mina = (function (eve) {
delete animations[a.id]; delete animations[a.id];
eve("mina.stop." + a.id, a); eve("mina.stop." + a.id, a);
}, },
pause = function () {
var a = this;
if (a.pdif) {
return;
}
delete animations[a.id];
a.pdif = a.get() - a.b;
},
resume = function () {
var a = this;
if (!a.pdif) {
return;
}
a.b = a.get() - a.pdif;
delete a.pdif;
animations[a.id] = a;
},
frame = function () { frame = function () {
var len = 0; var len = 0;
for (var i in animations) if (animations.hasOwnProperty(i)) { for (var i in animations) if (animations.hasOwnProperty(i)) {
@ -554,7 +571,9 @@ var mina = (function (eve) {
status: sta, status: sta,
speed: speed, speed: speed,
duration: duration, duration: duration,
stop: stopit stop: stopit,
pause: pause,
resume: resume
}; };
animations[anim.id] = anim; animations[anim.id] = anim;
var len = 0, i; var len = 0, i;
@ -586,7 +605,7 @@ var mina = (function (eve) {
= (object) See @mina = (object) See @mina
\*/ \*/
mina.getById = function (id) { mina.getById = function (id) {
return animations[anim.id] || null; return animations[id] || null;
}; };
/*\ /*\
@ -1323,6 +1342,18 @@ function cacher(f, scope, postprocessor) {
return newf; return newf;
} }
Savage._.cacher = cacher; Savage._.cacher = cacher;
function angle(x1, y1, x2, y2, x3, y3) {
if (x3 == null) {
var x = x1 - x2,
y = y1 - y2;
if (!x && !y) {
return 0;
}
return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
} else {
return angle(x1, y1, x3, y3) - angle(x2, y2, x3, y3);
}
}
function rad(deg) { function rad(deg) {
return deg % 360 * PI / 180; return deg % 360 * PI / 180;
} }
@ -1354,6 +1385,21 @@ Savage.rad = rad;
= (number) angle in degrees. = (number) angle in degrees.
\*/ \*/
Savage.deg = deg; Savage.deg = deg;
/*\
* Savage.angle
[ method ]
**
* Returns angle between two or three points.
> Parameters
- x1 (number) x coord of first point
- y1 (number) y coord of first point
- x2 (number) x coord of second point
- y2 (number) y coord of second point
- x3 (number) #optional x coord of third point
- y3 (number) #optional y coord of third point
= (number) angle in degrees.
\*/
Savage.angle = angle;
/*\ /*\
* Savage.is * Savage.is
[ method ] [ method ]
@ -1433,7 +1479,7 @@ function Matrix(a, b, c, d, e, f) {
- d (number) - d (number)
- e (number) - e (number)
- f (number) - f (number)
or * or
- matrix (object) @Matrix - matrix (object) @Matrix
\*/ \*/
matrixproto.add = function (a, b, c, d, e, f) { matrixproto.add = function (a, b, c, d, e, f) {
@ -2300,13 +2346,29 @@ function extractTransform(el, tstr) {
} }
} }
Savage._unit2px = unit2px; Savage._unit2px = unit2px;
function getSomeDefs(el) {
if (Savage._.someDefs) {
return Savage._.someDefs;
}
var p = el.paper ||
(el.node.parentNode && Savage(el.node.parentNode)) ||
Savage.select("svg") ||
Savage(0, 0),
defs = p.select("defs").node;
if (!defs) {
defs = make("defs", p.node).node;
}
Savage._.someDefs = defs;
return defs;
}
Savage._.getSomeDefs = getSomeDefs;
function unit2px(el, name, value) { function unit2px(el, name, value) {
var defs = el.paper.defs, var defs = getSomeDefs(el),
out = {}, out = {},
mgr = el.paper.measurer; mgr = defs.querySelector(".svg---mgr");
if (!mgr) { if (!mgr) {
el.paper.measurer = mgr = $("rect"); mgr = $("rect");
$(mgr, {width: 10, height: 10}); $(mgr, {width: 10, height: 10, "class": "svg---mgr"});
defs.appendChild(mgr); defs.appendChild(mgr);
} }
function getW(val) { function getW(val) {
@ -2545,25 +2607,29 @@ function arrayFirstValue(arr) {
o } o }
\*/ \*/
elproto.getBBox = function (isWithoutTransform) { elproto.getBBox = function (isWithoutTransform) {
if (this.removed) { var el = this;
if (el.type == "use") {
el = el.original;
}
if (el.removed) {
return {}; return {};
} }
var _ = this._; var _ = el._;
if (isWithoutTransform) { if (isWithoutTransform) {
if (_.dirty || !_.bboxwt) { if (_.dirty || !_.bboxwt) {
this.realPath = Savage.path.get[this.type](this); el.realPath = Savage.path.get[el.type](el);
_.bboxwt = Savage.path.getBBox(this.realPath); _.bboxwt = Savage.path.getBBox(el.realPath);
_.bboxwt.toString = x_y_w_h; _.bboxwt.toString = x_y_w_h;
_.dirty = 0; _.dirty = 0;
} }
return Savage._.box(_.bboxwt); return Savage._.box(_.bboxwt);
} }
if (_.dirty || _.dirtyT || !_.bbox) { if (_.dirty || _.dirtyT || !_.bbox) {
if (_.dirty || !this.realPath) { if (_.dirty || !el.realPath) {
_.bboxwt = 0; _.bboxwt = 0;
this.realPath = Savage.path.get[this.type](this); el.realPath = Savage.path.get[el.type](el);
} }
_.bbox = Savage.path.getBBox(Savage.path.map(this.realPath, this.matrix)); _.bbox = Savage.path.getBBox(Savage.path.map(el.realPath, el.matrix));
_.bbox.toString = x_y_w_h; _.bbox.toString = x_y_w_h;
_.dirty = _.dirtyT = 0; _.dirty = _.dirtyT = 0;
} }
@ -2749,7 +2815,7 @@ function arrayFirstValue(arr) {
= (Element) removed element = (Element) removed element
\*/ \*/
elproto.remove = function () { elproto.remove = function () {
this.node.parentNode.removeChild(this.node); this.node.parentNode && this.node.parentNode.removeChild(this.node);
delete this.paper; delete this.paper;
this.removed = true; this.removed = true;
return this; return this;
@ -2825,6 +2891,7 @@ function arrayFirstValue(arr) {
$(use.node, { $(use.node, {
"xlink:href": "#" + id "xlink:href": "#" + id
}); });
use.original = this;
return use; return use;
}; };
/*\ /*\
@ -2906,6 +2973,19 @@ function arrayFirstValue(arr) {
clone.insertAfter(this); clone.insertAfter(this);
return clone; return clone;
}; };
/*\
* Element.toDefs
[ method ]
**
* Moves element to the relative `<defs>` section.
**
= (Element) the clone
\*/
elproto.toDefs = function () {
var defs = getSomeDefs(this);
defs.appendChild(this.node);
return this;
};
/*\ /*\
* Element.pattern * Element.pattern
[ method ] [ method ]
@ -2930,7 +3010,7 @@ function arrayFirstValue(arr) {
| }); | });
\*/ \*/
elproto.pattern = function (x, y, width, height) { elproto.pattern = function (x, y, width, height) {
var p = make("pattern", this.paper.defs); var p = make("pattern", getSomeDefs(this));
if (x == null) { if (x == null) {
x = this.getBBox(); x = this.getBBox();
} }
@ -2970,7 +3050,7 @@ function arrayFirstValue(arr) {
\*/ \*/
// TODO add usage for markers // TODO add usage for markers
elproto.marker = function (x, y, width, height, refX, refY) { elproto.marker = function (x, y, width, height, refX, refY) {
var p = make("marker", this.paper.defs); var p = make("marker", getSomeDefs(this));
if (x == null) { if (x == null) {
x = this.getBBox(); x = this.getBBox();
} }
@ -3363,6 +3443,20 @@ function Paper(w, h) {
res = new Element(w); res = new Element(w);
desc = w.getElementsByTagName("desc")[0]; desc = w.getElementsByTagName("desc")[0];
defs = w.getElementsByTagName("defs")[0]; defs = w.getElementsByTagName("defs")[0];
if (!desc) {
desc = $("desc");
desc.appendChild(glob.doc.createTextNode("Created with Savage"));
res.node.appendChild(desc);
}
if (!defs) {
defs = $("defs");
res.node.appendChild(defs);
}
res.defs = defs;
for (var key in proto) if (proto[has](key)) {
res[key] = proto[key];
}
res.paper = res.root = res;
} else { } else {
res = make("svg", glob.doc.body); res = make("svg", glob.doc.body);
$(res.node, { $(res.node, {
@ -3372,20 +3466,6 @@ function Paper(w, h) {
xmlns: "http://www.w3.org/2000/svg" xmlns: "http://www.w3.org/2000/svg"
}); });
} }
if (!desc) {
desc = $("desc");
desc.appendChild(glob.doc.createTextNode("Created with Savage"));
res.node.appendChild(desc);
}
if (!defs) {
defs = $("defs");
res.node.appendChild(defs);
}
for (var key in proto) if (proto[has](key)) {
res[key] = proto[key];
}
res.paper = res.root = res;
res.defs = defs;
return res; return res;
} }
function wrap(dom) { function wrap(dom) {
@ -3400,6 +3480,115 @@ function wrap(dom) {
} }
return new Element(dom); return new Element(dom);
} }
// gradients helpers
function Gstops() {
return this.selectAll("stop");
}
function GaddStop(color, offset) {
var stop = $("stop"),
attr = {
offset: +offset + "%"
};
color = Savage.color(color);
attr["stop-color"] = color.hex;
if (color.opacity < 1) {
attr["stop-opacity"] = color.opacity;
}
$(stop, attr);
this.node.appendChild(stop);
return this;
}
function GgetBBox() {
if (this.type == "linearGradient") {
var x1 = $(this.node, "x1") || 0,
x2 = $(this.node, "x2") || 1,
y1 = $(this.node, "y1") || 0,
y2 = $(this.node, "y2") || 0;
return Savage._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1));
} else {
var cx = this.node.cx || .5,
cy = this.node.cy || .5,
r = this.node.r || 0;
return Savage._.box(cx - r, cy - r, r * 2, r * 2);
}
}
function gradient(defs, str) {
var grad = arrayFirstValue(eve("savage.util.grad.parse", null, str)),
el;
if (!grad) {
return null;
}
grad.params.unshift(defs);
if (grad.type.toLowerCase() == "l") {
el = gradientLinear.apply(0, grad.params);
} else {
el = gradientRadial.apply(0, grad.params);
}
if (grad.type != grad.type.toLowerCase()) {
$(el.node, {
gradientUnits: "userSpaceOnUse"
});
}
var stops = grad.stops,
len = stops.length,
start = 0,
j = 0;
function seed(i, end) {
var step = (end - start) / (i - j);
for (var k = j; k < i; k++) {
stops[k].offset = +(+start + step * (k - j)).toFixed(2);
}
j = i;
start = end;
}
len--;
for (var i = 0; i < len; i++) if ("offset" in stops[i]) {
seed(i, stops[i].offset);
}
stops[len].offset = stops[len].offset || 100;
seed(len, stops[len].offset);
for (i = 0; i <= len; i++) {
var stop = stops[i];
el.addStop(stop.color, stop.offset);
}
return el;
}
function gradientLinear(defs, x1, y1, x2, y2) {
var el = make("linearGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (x1 != null) {
$(el.node, {
x1: x1,
y1: y1,
x2: x2,
y2: y2
});
}
return el;
}
function gradientRadial(defs, cx, cy, r, fx, fy) {
var el = make("radialGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (cx != null) {
$(el.node, {
cx: cx,
cy: cy,
r: r
});
}
if (fx != null && fy != null) {
$(el.node, {
fx: fx,
fy: fy
});
}
return el;
}
// Paper prototype methods
(function (proto) { (function (proto) {
/*\ /*\
* Paper.el * Paper.el
@ -3801,105 +3990,13 @@ function wrap(dom) {
= (object) Element object with type gradient = (object) Element object with type gradient
\*/ \*/
proto.gradient = function (str) { proto.gradient = function (str) {
var grad = arrayFirstValue(eve("savage.util.grad.parse", null, str)), return gradient(this.defs, str);
el;
if (!grad) {
return null;
}
if (grad.type.toLowerCase() == "l") {
el = this.gradientLinear.apply(this, grad.params);
} else {
el = this.gradientRadial.apply(this, grad.params);
}
if (grad.type != grad.type.toLowerCase()) {
$(el.node, {
gradientUnits: "userSpaceOnUse"
});
}
var stops = grad.stops,
len = stops.length,
start = 0,
j = 0;
function seed(i, end) {
var step = (end - start) / (i - j);
for (var k = j; k < i; k++) {
stops[k].offset = +(+start + step * (k - j)).toFixed(2);
}
j = i;
start = end;
}
len--;
for (var i = 0; i < len; i++) if ("offset" in stops[i]) {
seed(i, stops[i].offset);
}
stops[len].offset = stops[len].offset || 100;
seed(len, stops[len].offset);
for (i = 0; i <= len; i++) {
var stop = stops[i];
el.addStop(stop.color, stop.offset);
}
return el;
}; };
function stops() {
return this.selectAll("stop");
}
function addStop(color, offset) {
var stop = $("stop");
$(stop, {
"stop-color": color,
offset: +offset + "%"
});
this.node.appendChild(stop);
return this;
}
function getBBox() {
if (this.type == "linearGradient") {
var x1 = $(this.node, "x1") || 0,
x2 = $(this.node, "x2") || 1,
y1 = $(this.node, "y1") || 0,
y2 = $(this.node, "y2") || 0;
return Savage._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1));
} else {
var cx = this.node.cx || .5,
cy = this.node.cy || .5,
r = this.node.r || 0;
return Savage._.box(cx - r, cy - r, r * 2, r * 2);
}
}
proto.gradientLinear = function (x1, y1, x2, y2) { proto.gradientLinear = function (x1, y1, x2, y2) {
var el = make("linearGradient", this.node); return gradientLinear(this.defs, x1, y1, x2, y2);
el.stops = stops;
el.addStop = addStop;
el.getBBox = getBBox;
if (x1 != null) {
$(el.node, {
x1: x1,
y1: y1,
x2: x2,
y2: y2
});
}
return el;
}; };
proto.gradientRadial = function (cx, cy, r, fx, fy) { proto.gradientRadial = function (cx, cy, r, fx, fy) {
var el = make("radialGradient", this.node); return gradientRadial(this.defs, cx, cy, r, fx, fy);
el.stops = stops;
el.addStop = addStop;
el.getBBox = getBBox;
if (cx != null) {
$(el.node, {
cx: cx,
cy: cy,
r: r
});
}
if (fx != null && fy != null) {
$(el.node, {
fx: fx,
fy: fy
});
}
return el;
}; };
/*\ /*\
* Paper.toString * Paper.toString
@ -3999,13 +4096,13 @@ eve.on("savage.util.attr.mask", function (value) {
eve.stop(); eve.stop();
if (value instanceof Fragment && value.node.childNodes.length == 1) { if (value instanceof Fragment && value.node.childNodes.length == 1) {
value = value.node.firstChild; value = value.node.firstChild;
this.paper.defs.appendChild(value); getSomeDefs(this).appendChild(value);
value = wrap(value); value = wrap(value);
} }
if (value.type == "mask") { if (value.type == "mask") {
var mask = value; var mask = value;
} else { } else {
mask = make("mask", this.paper.defs); mask = make("mask", getSomeDefs(this));
mask.node.appendChild(value.node); mask.node.appendChild(value.node);
!mask.node.id && $(mask.node, { !mask.node.id && $(mask.node, {
id: mask.id id: mask.id
@ -4026,7 +4123,7 @@ eve.on("savage.util.attr.mask", function (value) {
if (value.type == "clipPath") { if (value.type == "clipPath") {
var clip = value; var clip = value;
} else { } else {
clip = make("clipPath", this.paper.defs); clip = make("clipPath", getSomeDefs(this));
clip.node.appendChild(value.node); clip.node.appendChild(value.node);
!clip.node.id && $(clip.node, { !clip.node.id && $(clip.node, {
id: clip.id id: clip.id
@ -4045,7 +4142,7 @@ function fillStroke(name) {
value.node.firstChild.tagName == "linearGradient" || value.node.firstChild.tagName == "linearGradient" ||
value.node.firstChild.tagName == "pattern")) { value.node.firstChild.tagName == "pattern")) {
value = value.node.firstChild; value = value.node.firstChild;
this.paper.defs.appendChild(value); getSomeDefs(this).appendChild(value);
value = wrap(value); value = wrap(value);
} }
if (value instanceof Element) { if (value instanceof Element) {
@ -4063,7 +4160,7 @@ function fillStroke(name) {
} else { } else {
fill = Savage.color(value); fill = Savage.color(value);
if (fill.error) { if (fill.error) {
var grad = this.paper.gradient(value); var grad = gradient(getSomeDefs(this), value);
if (grad) { if (grad) {
if (!grad.node.id) { if (!grad.node.id) {
$(grad.node, { $(grad.node, {
@ -4806,7 +4903,7 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
function O(val) { function O(val) {
return +(+val).toFixed(3); return +(+val).toFixed(3);
} }
return function (path, length, onlystart) { return Savage._.cacher(function (path, length, onlystart) {
if (path instanceof Element) { if (path instanceof Element) {
path = path.attr("d"); path = path.attr("d");
} }
@ -4861,7 +4958,7 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
subpaths.end = sp; subpaths.end = sp;
point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1); point = istotal ? len : subpath ? subpaths : findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
return point; return point;
}; }, null, Savage._.clone);
} }
var getTotalLength = getLengthFactory(1), var getTotalLength = getLengthFactory(1),
getPointAtLength = getLengthFactory(), getPointAtLength = getLengthFactory(),
@ -5242,7 +5339,8 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
} }
}; };
function pathToRelative(pathArray) { function pathToRelative(pathArray) {
var pth = paths(pathArray); var pth = paths(pathArray),
lowerCase = String.prototype.toLowerCase;
if (pth.rel) { if (pth.rel) {
return pathClone(pth.rel); return pathClone(pth.rel);
} }
@ -6200,6 +6298,7 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
this.splice(i, 1); this.splice(i, 1);
return true; return true;
} }
return false;
}; };
setproto.insertAfter = function (el) { setproto.insertAfter = function (el) {
var i = this.items.length; var i = this.items.length;
@ -7005,6 +7104,10 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
if (blur == null) { if (blur == null) {
blur = 4; blur = 4;
} }
if (typeof blur == "string") {
color = blur;
blur = 4;
}
if (dx == null) { if (dx == null) {
dx = 0; dx = 0;
dy = 2; dy = 2;
@ -7012,6 +7115,7 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
if (dy == null) { if (dy == null) {
dy = dx; dy = dx;
} }
color = Savage.color(color);
return Savage.format('<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>', { return Savage.format('<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>', {
color: color, color: color,
dx: dx, dx: dx,

View File

@ -149,6 +149,7 @@ Savage.plugin(function (Savage, Element, Paper, glob) {
this.splice(i, 1); this.splice(i, 1);
return true; return true;
} }
return false;
}; };
setproto.insertAfter = function (el) { setproto.insertAfter = function (el) {
var i = this.items.length; var i = this.items.length;

View File

@ -245,6 +245,18 @@ function cacher(f, scope, postprocessor) {
return newf; return newf;
} }
Savage._.cacher = cacher; Savage._.cacher = cacher;
function angle(x1, y1, x2, y2, x3, y3) {
if (x3 == null) {
var x = x1 - x2,
y = y1 - y2;
if (!x && !y) {
return 0;
}
return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
} else {
return angle(x1, y1, x3, y3) - angle(x2, y2, x3, y3);
}
}
function rad(deg) { function rad(deg) {
return deg % 360 * PI / 180; return deg % 360 * PI / 180;
} }
@ -276,6 +288,21 @@ Savage.rad = rad;
= (number) angle in degrees. = (number) angle in degrees.
\*/ \*/
Savage.deg = deg; Savage.deg = deg;
/*\
* Savage.angle
[ method ]
**
* Returns angle between two or three points.
> Parameters
- x1 (number) x coord of first point
- y1 (number) y coord of first point
- x2 (number) x coord of second point
- y2 (number) y coord of second point
- x3 (number) #optional x coord of third point
- y3 (number) #optional y coord of third point
= (number) angle in degrees.
\*/
Savage.angle = angle;
/*\ /*\
* Savage.is * Savage.is
[ method ] [ method ]
@ -355,7 +382,7 @@ function Matrix(a, b, c, d, e, f) {
- d (number) - d (number)
- e (number) - e (number)
- f (number) - f (number)
or * or
- matrix (object) @Matrix - matrix (object) @Matrix
\*/ \*/
matrixproto.add = function (a, b, c, d, e, f) { matrixproto.add = function (a, b, c, d, e, f) {
@ -1222,13 +1249,29 @@ function extractTransform(el, tstr) {
} }
} }
Savage._unit2px = unit2px; Savage._unit2px = unit2px;
function getSomeDefs(el) {
if (Savage._.someDefs) {
return Savage._.someDefs;
}
var p = el.paper ||
(el.node.parentNode && Savage(el.node.parentNode)) ||
Savage.select("svg") ||
Savage(0, 0),
defs = p.select("defs").node;
if (!defs) {
defs = make("defs", p.node).node;
}
Savage._.someDefs = defs;
return defs;
}
Savage._.getSomeDefs = getSomeDefs;
function unit2px(el, name, value) { function unit2px(el, name, value) {
var defs = el.paper.defs, var defs = getSomeDefs(el),
out = {}, out = {},
mgr = el.paper.measurer; mgr = defs.querySelector(".svg---mgr");
if (!mgr) { if (!mgr) {
el.paper.measurer = mgr = $("rect"); mgr = $("rect");
$(mgr, {width: 10, height: 10}); $(mgr, {width: 10, height: 10, "class": "svg---mgr"});
defs.appendChild(mgr); defs.appendChild(mgr);
} }
function getW(val) { function getW(val) {
@ -1467,25 +1510,29 @@ function arrayFirstValue(arr) {
o } o }
\*/ \*/
elproto.getBBox = function (isWithoutTransform) { elproto.getBBox = function (isWithoutTransform) {
if (this.removed) { var el = this;
if (el.type == "use") {
el = el.original;
}
if (el.removed) {
return {}; return {};
} }
var _ = this._; var _ = el._;
if (isWithoutTransform) { if (isWithoutTransform) {
if (_.dirty || !_.bboxwt) { if (_.dirty || !_.bboxwt) {
this.realPath = Savage.path.get[this.type](this); el.realPath = Savage.path.get[el.type](el);
_.bboxwt = Savage.path.getBBox(this.realPath); _.bboxwt = Savage.path.getBBox(el.realPath);
_.bboxwt.toString = x_y_w_h; _.bboxwt.toString = x_y_w_h;
_.dirty = 0; _.dirty = 0;
} }
return Savage._.box(_.bboxwt); return Savage._.box(_.bboxwt);
} }
if (_.dirty || _.dirtyT || !_.bbox) { if (_.dirty || _.dirtyT || !_.bbox) {
if (_.dirty || !this.realPath) { if (_.dirty || !el.realPath) {
_.bboxwt = 0; _.bboxwt = 0;
this.realPath = Savage.path.get[this.type](this); el.realPath = Savage.path.get[el.type](el);
} }
_.bbox = Savage.path.getBBox(Savage.path.map(this.realPath, this.matrix)); _.bbox = Savage.path.getBBox(Savage.path.map(el.realPath, el.matrix));
_.bbox.toString = x_y_w_h; _.bbox.toString = x_y_w_h;
_.dirty = _.dirtyT = 0; _.dirty = _.dirtyT = 0;
} }
@ -1671,7 +1718,7 @@ function arrayFirstValue(arr) {
= (Element) removed element = (Element) removed element
\*/ \*/
elproto.remove = function () { elproto.remove = function () {
this.node.parentNode.removeChild(this.node); this.node.parentNode && this.node.parentNode.removeChild(this.node);
delete this.paper; delete this.paper;
this.removed = true; this.removed = true;
return this; return this;
@ -1747,6 +1794,7 @@ function arrayFirstValue(arr) {
$(use.node, { $(use.node, {
"xlink:href": "#" + id "xlink:href": "#" + id
}); });
use.original = this;
return use; return use;
}; };
/*\ /*\
@ -1828,6 +1876,19 @@ function arrayFirstValue(arr) {
clone.insertAfter(this); clone.insertAfter(this);
return clone; return clone;
}; };
/*\
* Element.toDefs
[ method ]
**
* Moves element to the relative `<defs>` section.
**
= (Element) the clone
\*/
elproto.toDefs = function () {
var defs = getSomeDefs(this);
defs.appendChild(this.node);
return this;
};
/*\ /*\
* Element.pattern * Element.pattern
[ method ] [ method ]
@ -1852,7 +1913,7 @@ function arrayFirstValue(arr) {
| }); | });
\*/ \*/
elproto.pattern = function (x, y, width, height) { elproto.pattern = function (x, y, width, height) {
var p = make("pattern", this.paper.defs); var p = make("pattern", getSomeDefs(this));
if (x == null) { if (x == null) {
x = this.getBBox(); x = this.getBBox();
} }
@ -1892,7 +1953,7 @@ function arrayFirstValue(arr) {
\*/ \*/
// TODO add usage for markers // TODO add usage for markers
elproto.marker = function (x, y, width, height, refX, refY) { elproto.marker = function (x, y, width, height, refX, refY) {
var p = make("marker", this.paper.defs); var p = make("marker", getSomeDefs(this));
if (x == null) { if (x == null) {
x = this.getBBox(); x = this.getBBox();
} }
@ -2285,6 +2346,20 @@ function Paper(w, h) {
res = new Element(w); res = new Element(w);
desc = w.getElementsByTagName("desc")[0]; desc = w.getElementsByTagName("desc")[0];
defs = w.getElementsByTagName("defs")[0]; defs = w.getElementsByTagName("defs")[0];
if (!desc) {
desc = $("desc");
desc.appendChild(glob.doc.createTextNode("Created with Savage"));
res.node.appendChild(desc);
}
if (!defs) {
defs = $("defs");
res.node.appendChild(defs);
}
res.defs = defs;
for (var key in proto) if (proto[has](key)) {
res[key] = proto[key];
}
res.paper = res.root = res;
} else { } else {
res = make("svg", glob.doc.body); res = make("svg", glob.doc.body);
$(res.node, { $(res.node, {
@ -2294,20 +2369,6 @@ function Paper(w, h) {
xmlns: "http://www.w3.org/2000/svg" xmlns: "http://www.w3.org/2000/svg"
}); });
} }
if (!desc) {
desc = $("desc");
desc.appendChild(glob.doc.createTextNode("Created with Savage"));
res.node.appendChild(desc);
}
if (!defs) {
defs = $("defs");
res.node.appendChild(defs);
}
for (var key in proto) if (proto[has](key)) {
res[key] = proto[key];
}
res.paper = res.root = res;
res.defs = defs;
return res; return res;
} }
function wrap(dom) { function wrap(dom) {
@ -2322,6 +2383,115 @@ function wrap(dom) {
} }
return new Element(dom); return new Element(dom);
} }
// gradients helpers
function Gstops() {
return this.selectAll("stop");
}
function GaddStop(color, offset) {
var stop = $("stop"),
attr = {
offset: +offset + "%"
};
color = Savage.color(color);
attr["stop-color"] = color.hex;
if (color.opacity < 1) {
attr["stop-opacity"] = color.opacity;
}
$(stop, attr);
this.node.appendChild(stop);
return this;
}
function GgetBBox() {
if (this.type == "linearGradient") {
var x1 = $(this.node, "x1") || 0,
x2 = $(this.node, "x2") || 1,
y1 = $(this.node, "y1") || 0,
y2 = $(this.node, "y2") || 0;
return Savage._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1));
} else {
var cx = this.node.cx || .5,
cy = this.node.cy || .5,
r = this.node.r || 0;
return Savage._.box(cx - r, cy - r, r * 2, r * 2);
}
}
function gradient(defs, str) {
var grad = arrayFirstValue(eve("savage.util.grad.parse", null, str)),
el;
if (!grad) {
return null;
}
grad.params.unshift(defs);
if (grad.type.toLowerCase() == "l") {
el = gradientLinear.apply(0, grad.params);
} else {
el = gradientRadial.apply(0, grad.params);
}
if (grad.type != grad.type.toLowerCase()) {
$(el.node, {
gradientUnits: "userSpaceOnUse"
});
}
var stops = grad.stops,
len = stops.length,
start = 0,
j = 0;
function seed(i, end) {
var step = (end - start) / (i - j);
for (var k = j; k < i; k++) {
stops[k].offset = +(+start + step * (k - j)).toFixed(2);
}
j = i;
start = end;
}
len--;
for (var i = 0; i < len; i++) if ("offset" in stops[i]) {
seed(i, stops[i].offset);
}
stops[len].offset = stops[len].offset || 100;
seed(len, stops[len].offset);
for (i = 0; i <= len; i++) {
var stop = stops[i];
el.addStop(stop.color, stop.offset);
}
return el;
}
function gradientLinear(defs, x1, y1, x2, y2) {
var el = make("linearGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (x1 != null) {
$(el.node, {
x1: x1,
y1: y1,
x2: x2,
y2: y2
});
}
return el;
}
function gradientRadial(defs, cx, cy, r, fx, fy) {
var el = make("radialGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (cx != null) {
$(el.node, {
cx: cx,
cy: cy,
r: r
});
}
if (fx != null && fy != null) {
$(el.node, {
fx: fx,
fy: fy
});
}
return el;
}
// Paper prototype methods
(function (proto) { (function (proto) {
/*\ /*\
* Paper.el * Paper.el
@ -2723,105 +2893,13 @@ function wrap(dom) {
= (object) Element object with type gradient = (object) Element object with type gradient
\*/ \*/
proto.gradient = function (str) { proto.gradient = function (str) {
var grad = arrayFirstValue(eve("savage.util.grad.parse", null, str)), return gradient(this.defs, str);
el;
if (!grad) {
return null;
}
if (grad.type.toLowerCase() == "l") {
el = this.gradientLinear.apply(this, grad.params);
} else {
el = this.gradientRadial.apply(this, grad.params);
}
if (grad.type != grad.type.toLowerCase()) {
$(el.node, {
gradientUnits: "userSpaceOnUse"
});
}
var stops = grad.stops,
len = stops.length,
start = 0,
j = 0;
function seed(i, end) {
var step = (end - start) / (i - j);
for (var k = j; k < i; k++) {
stops[k].offset = +(+start + step * (k - j)).toFixed(2);
}
j = i;
start = end;
}
len--;
for (var i = 0; i < len; i++) if ("offset" in stops[i]) {
seed(i, stops[i].offset);
}
stops[len].offset = stops[len].offset || 100;
seed(len, stops[len].offset);
for (i = 0; i <= len; i++) {
var stop = stops[i];
el.addStop(stop.color, stop.offset);
}
return el;
}; };
function stops() {
return this.selectAll("stop");
}
function addStop(color, offset) {
var stop = $("stop");
$(stop, {
"stop-color": color,
offset: +offset + "%"
});
this.node.appendChild(stop);
return this;
}
function getBBox() {
if (this.type == "linearGradient") {
var x1 = $(this.node, "x1") || 0,
x2 = $(this.node, "x2") || 1,
y1 = $(this.node, "y1") || 0,
y2 = $(this.node, "y2") || 0;
return Savage._.box(x1, y1, math.abs(x2 - x1), math.abs(y2 - y1));
} else {
var cx = this.node.cx || .5,
cy = this.node.cy || .5,
r = this.node.r || 0;
return Savage._.box(cx - r, cy - r, r * 2, r * 2);
}
}
proto.gradientLinear = function (x1, y1, x2, y2) { proto.gradientLinear = function (x1, y1, x2, y2) {
var el = make("linearGradient", this.node); return gradientLinear(this.defs, x1, y1, x2, y2);
el.stops = stops;
el.addStop = addStop;
el.getBBox = getBBox;
if (x1 != null) {
$(el.node, {
x1: x1,
y1: y1,
x2: x2,
y2: y2
});
}
return el;
}; };
proto.gradientRadial = function (cx, cy, r, fx, fy) { proto.gradientRadial = function (cx, cy, r, fx, fy) {
var el = make("radialGradient", this.node); return gradientRadial(this.defs, cx, cy, r, fx, fy);
el.stops = stops;
el.addStop = addStop;
el.getBBox = getBBox;
if (cx != null) {
$(el.node, {
cx: cx,
cy: cy,
r: r
});
}
if (fx != null && fy != null) {
$(el.node, {
fx: fx,
fy: fy
});
}
return el;
}; };
/*\ /*\
* Paper.toString * Paper.toString
@ -2921,13 +2999,13 @@ eve.on("savage.util.attr.mask", function (value) {
eve.stop(); eve.stop();
if (value instanceof Fragment && value.node.childNodes.length == 1) { if (value instanceof Fragment && value.node.childNodes.length == 1) {
value = value.node.firstChild; value = value.node.firstChild;
this.paper.defs.appendChild(value); getSomeDefs(this).appendChild(value);
value = wrap(value); value = wrap(value);
} }
if (value.type == "mask") { if (value.type == "mask") {
var mask = value; var mask = value;
} else { } else {
mask = make("mask", this.paper.defs); mask = make("mask", getSomeDefs(this));
mask.node.appendChild(value.node); mask.node.appendChild(value.node);
!mask.node.id && $(mask.node, { !mask.node.id && $(mask.node, {
id: mask.id id: mask.id
@ -2948,7 +3026,7 @@ eve.on("savage.util.attr.mask", function (value) {
if (value.type == "clipPath") { if (value.type == "clipPath") {
var clip = value; var clip = value;
} else { } else {
clip = make("clipPath", this.paper.defs); clip = make("clipPath", getSomeDefs(this));
clip.node.appendChild(value.node); clip.node.appendChild(value.node);
!clip.node.id && $(clip.node, { !clip.node.id && $(clip.node, {
id: clip.id id: clip.id
@ -2967,7 +3045,7 @@ function fillStroke(name) {
value.node.firstChild.tagName == "linearGradient" || value.node.firstChild.tagName == "linearGradient" ||
value.node.firstChild.tagName == "pattern")) { value.node.firstChild.tagName == "pattern")) {
value = value.node.firstChild; value = value.node.firstChild;
this.paper.defs.appendChild(value); getSomeDefs(this).appendChild(value);
value = wrap(value); value = wrap(value);
} }
if (value instanceof Element) { if (value instanceof Element) {
@ -2985,7 +3063,7 @@ function fillStroke(name) {
} else { } else {
fill = Savage.color(value); fill = Savage.color(value);
if (fill.error) { if (fill.error) {
var grad = this.paper.gradient(value); var grad = gradient(getSomeDefs(this), value);
if (grad) { if (grad) {
if (!grad.node.id) { if (!grad.node.id) {
$(grad.node, { $(grad.node, {