vendor/assets/javascripts/snapsvg.js in snapsvg-rails-0.2.0 vs vendor/assets/javascripts/snapsvg.js in snapsvg-rails-0.4.1
- old
+ new
@@ -1,22 +1,23 @@
-// Snap.svg 0.2.0
-//
-// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
-//
+// Snap.svg 0.4.1
+//
+// Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved.
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-//
-// build: 2013-12-23
+//
+// build: 2015-04-13
+
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -36,18 +37,34 @@
(function (glob) {
var version = "0.4.2",
has = "hasOwnProperty",
separator = /[\.\/]/,
+ comaseparator = /\s*,\s*/,
wildcard = "*",
fun = function () {},
numsort = function (a, b) {
return a - b;
},
current_event,
stop,
events = {n: {}},
+ firstDefined = function () {
+ for (var i = 0, ii = this.length; i < ii; i++) {
+ if (typeof this[i] != "undefined") {
+ return this[i];
+ }
+ }
+ },
+ lastDefined = function () {
+ var i = this.length;
+ while (--i) {
+ if (typeof this[i] != "undefined") {
+ return this[i];
+ }
+ }
+ },
/*\
* eve
[ method ]
* Fires event with given `name`, given scope and other parameters.
@@ -56,14 +73,14 @@
- name (string) name of the *event*, dot (`.`) or slash (`/`) separated
- scope (object) context for the event handlers
- varargs (...) the rest of arguments will be sent to event handlers
- = (object) array of returned values from the listeners
+ = (object) array of returned values from the listeners. Array has two methods `.firstDefined()` and `.lastDefined()` to get first or last not `undefined` value.
\*/
eve = function (name, scope) {
- name = String(name);
+ name = String(name);
var e = events,
oldstop = stop,
args = Array.prototype.slice.call(arguments, 2),
listeners = eve.listeners(name),
z = 0,
@@ -72,10 +89,12 @@
indexed = [],
queue = {},
out = [],
ce = current_event,
errors = [];
+ out.firstDefined = firstDefined;
+ out.lastDefined = lastDefined;
current_event = name;
stop = 0;
for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
indexed.push(listeners[i].zIndex);
if (listeners[i].zIndex < 0) {
@@ -117,14 +136,14 @@
}
}
}
stop = oldstop;
current_event = ce;
- return out.length ? out : null;
+ return out;
};
- // Undocumented. Debug only.
- eve._events = events;
+ // Undocumented. Debug only.
+ eve._events = events;
/*\
* eve.listeners
[ method ]
* Internal method which gives you array of all event handlers that will be triggered by the given `name`.
@@ -184,31 +203,38 @@
= (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment.
> Example:
| eve.on("mouse", eatIt)(2);
| eve.on("mouse", scream);
| eve.on("mouse", catchIt)(1);
- * This will ensure that `catchIt()` function will be called before `eatIt()`.
- *
+ * This will ensure that `catchIt` function will be called before `eatIt`.
+ *
* If you want to put your handler before non-indexed handlers, specify a negative value.
* Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
\*/
eve.on = function (name, f) {
- name = String(name);
- if (typeof f != "function") {
- return function () {};
- }
- var names = name.split(separator),
- e = events;
+ name = String(name);
+ if (typeof f != "function") {
+ return function () {};
+ }
+ var names = name.split(comaseparator);
for (var i = 0, ii = names.length; i < ii; i++) {
- e = e.n;
- e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
+ (function (name) {
+ var names = name.split(separator),
+ e = events,
+ exist;
+ for (var i = 0, ii = names.length; i < ii; i++) {
+ e = e.n;
+ e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
+ }
+ e.f = e.f || [];
+ for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
+ exist = true;
+ break;
+ }
+ !exist && e.f.push(f);
+ }(names[i]));
}
- e.f = e.f || [];
- for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
- return fun;
- }
- e.f.push(f);
return function (zIndex) {
if (+zIndex == +zIndex) {
f.zIndex = +zIndex;
}
};
@@ -216,27 +242,27 @@
/*\
* eve.f
[ method ]
**
* Returns function that will fire given event with optional arguments.
- * Arguments that will be passed to the result function will be also
- * concated to the list of final arguments.
- | el.onclick = eve.f("click", 1, 2);
- | eve.on("click", function (a, b, c) {
- | console.log(a, b, c); // 1, 2, [event object]
- | });
+ * Arguments that will be passed to the result function will be also
+ * concated to the list of final arguments.
+ | el.onclick = eve.f("click", 1, 2);
+ | eve.on("click", function (a, b, c) {
+ | console.log(a, b, c); // 1, 2, [event object]
+ | });
> Arguments
- - event (string) event name
- - varargs (…) and any other arguments
- = (function) possible event handler function
+ - event (string) event name
+ - varargs (…) and any other arguments
+ = (function) possible event handler function
\*/
- eve.f = function (event) {
- var attrs = [].slice.call(arguments, 1);
- return function () {
- eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
- };
- };
+ eve.f = function (event) {
+ var attrs = [].slice.call(arguments, 1);
+ return function () {
+ eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
+ };
+ };
/*\
* eve.stop
[ method ]
**
* Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
@@ -279,11 +305,11 @@
/*\
* eve.off
[ method ]
**
* Removes given function from the list of event listeners assigned to given name.
- * If no arguments specified all the events will be cleared.
+ * If no arguments specified all the events will be cleared.
**
> Arguments
**
- name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
- f (function) event handler function
@@ -293,16 +319,23 @@
[ method ]
**
* See @eve.off
\*/
eve.off = eve.unbind = function (name, f) {
- if (!name) {
- eve._events = events = {n: {}};
- return;
- }
- var names = name.split(separator),
- e,
+ if (!name) {
+ eve._events = events = {n: {}};
+ return;
+ }
+ var names = name.split(comaseparator);
+ if (names.length > 1) {
+ for (var i = 0, ii = names.length; i < ii; i++) {
+ eve.off(names[i], f);
+ }
+ return;
+ }
+ names = name.split(separator);
+ var e,
key,
splice,
i, ii, j, jj,
cur = [events];
for (i = 0, ii = names.length; i < ii; i++) {
@@ -382,26 +415,31 @@
\*/
eve.version = version;
eve.toString = function () {
return "You are running Eve " + version;
};
- (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define != "undefined" ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
+ (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define === "function" && define.amd ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
})(this);
(function (glob, factory) {
// AMD support
- if (typeof define === "function" && define.amd) {
+ if (typeof define == "function" && define.amd) {
// Define as an anonymous module
- define(["eve"], function( eve ) {
+ define(["eve"], function (eve) {
return factory(glob, eve);
});
+ } else if (typeof exports != 'undefined') {
+ // Next for Node.js or CommonJS
+ var eve = require('eve');
+ module.exports = factory(glob, eve);
} else {
// Browser globals (glob is window)
// Snap adds itself to window
factory(glob, glob.eve);
}
-}(this, function (window, eve) {
+}(window || this, function (window, eve) {
+
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -474,18 +512,20 @@
a.dur = val;
},
stopit = function () {
var a = this;
delete animations[a.id];
+ a.update();
eve("mina.stop." + a.id, a);
},
pause = function () {
var a = this;
if (a.pdif) {
return;
}
delete animations[a.id];
+ a.update();
a.pdif = a.get() - a.b;
},
resume = function () {
var a = this;
if (!a.pdif) {
@@ -493,10 +533,24 @@
}
a.b = a.get() - a.pdif;
delete a.pdif;
animations[a.id] = a;
},
+ update = function () {
+ var a = this,
+ res;
+ if (isArray(a.start)) {
+ res = [];
+ for (var j = 0, jj = a.start.length; j < jj; j++) {
+ res[j] = +a.start[j] +
+ (a.end[j] - a.start[j]) * a.easing(a.s);
+ }
+ } else {
+ res = +a.start + (a.end - a.start) * a.easing(a.s);
+ }
+ a.set(res);
+ },
frame = function () {
var len = 0;
for (var i in animations) if (animations.hasOwnProperty(i)) {
var a = animations[i],
b = a.get(),
@@ -511,24 +565,14 @@
setTimeout(function () {
eve("mina.finish." + a.id, a);
});
}(a));
}
- if (isArray(a.start)) {
- res = [];
- for (var j = 0, jj = a.start.length; j < jj; j++) {
- res[j] = +a.start[j] +
- (a.end[j] - a.start[j]) * a.easing(a.s);
- }
- } else {
- res = +a.start + (a.end - a.start) * a.easing(a.s);
- }
- a.set(res);
+ a.update();
}
len && requestAnimFrame(frame);
},
- // SIERRA Unfamiliar with the word _slave_ in this context. Also, I don't know what _gereal_ means. Do you mean _general_?
/*\
* mina
[ method ]
**
* Generic animation of numbers
@@ -554,10 +598,13 @@
o easing (function) easing function, default is @mina.linear,
o status (function) status getter/setter,
o speed (function) speed getter/setter,
o duration (function) duration getter/setter,
o stop (function) animation stopper
+ o pause (function) pauses the animation
+ o resume (function) resumes the animation
+ o update (function) calles setter with the right value of the animation
o }
\*/
mina = function (a, A, b, B, get, set, easing) {
var anim = {
id: ID(),
@@ -573,11 +620,12 @@
status: sta,
speed: speed,
duration: duration,
stop: stopit,
pause: pause,
- resume: resume
+ resume: resume,
+ update: update
};
animations[anim.id] = anim;
var len = 0, i;
for (i in animations) if (animations.hasOwnProperty(i)) {
len++;
@@ -744,11 +792,11 @@
return l;
};
window.mina = mina;
return mina;
})(typeof eve == "undefined" ? function () {} : eve);
-// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+// Copyright (c) 2013 - 2015 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
@@ -758,12 +806,12 @@
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-var Snap = (function() {
-Snap.version = "0.2.0";
+var Snap = (function(root) {
+Snap.version = "0.4.0";
/*\
* Snap
[ method ]
**
* Creates a drawing surface or wraps existing SVG element.
@@ -771,23 +819,28 @@
- width (number|string) width of surface
- height (number|string) height of surface
* or
- DOM (SVGElement) element to be wrapped into Snap structure
* or
+ - array (array) array of elements (will return set of elements)
+ * or
- query (string) CSS query selector
= (object) @Element
\*/
function Snap(w, h) {
if (w) {
- if (w.tagName) {
+ if (w.nodeType) {
return wrap(w);
}
+ if (is(w, "array") && Snap.set) {
+ return Snap.set.apply(Snap, w);
+ }
if (w instanceof Element) {
return w;
}
if (h == null) {
- w = glob.doc.querySelector(w);
+ w = glob.doc.querySelector(String(w));
return wrap(w);
}
}
w = w == null ? "100%" : w;
h = h == null ? "100%" : h;
@@ -796,12 +849,12 @@
Snap.toString = function () {
return "Snap v" + this.version;
};
Snap._ = {};
var glob = {
- win: window,
- doc: window.document
+ win: root.window,
+ doc: root.window.document
};
Snap._.glob = glob;
var has = "hasOwnProperty",
Str = String,
toFloat = parseFloat,
@@ -818,61 +871,75 @@
objectToString = Object.prototype.toString,
ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,
bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
reURLValue = /^url\(#?([^)]+)\)$/,
- spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
- separator = new RegExp("[," + spaces + "]+"),
- whitespace = new RegExp("[" + spaces + "]", "g"),
- commaSpaces = new RegExp("[" + spaces + "]*,[" + spaces + "]*"),
+ separator = Snap._.separator = /[,\s]+/,
+ whitespace = /[\s]/g,
+ commaSpaces = /[\s]*,[\s]*/,
hsrg = {hs: 1, rg: 1},
- pathCommand = new RegExp("([a-z])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)", "ig"),
- tCommand = new RegExp("([rstm])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)", "ig"),
- pathValues = new RegExp("(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[" + spaces + "]*,?[" + spaces + "]*", "ig"),
+ pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig,
+ tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/ig,
+ pathValues = /(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/ig,
idgen = 0,
idprefix = "S" + (+new Date).toString(36),
- ID = function () {
- return idprefix + (idgen++).toString(36);
+ ID = function (el) {
+ return (el && el.type ? el.type : E) + idprefix + (idgen++).toString(36);
},
xlink = "http://www.w3.org/1999/xlink",
xmlns = "http://www.w3.org/2000/svg",
hub = {},
URL = Snap.url = function (url) {
return "url('#" + url + "')";
};
function $(el, attr) {
if (attr) {
+ if (el == "#text") {
+ el = glob.doc.createTextNode(attr.text || attr["#text"] || "");
+ }
+ if (el == "#comment") {
+ el = glob.doc.createComment(attr.text || attr["#text"] || "");
+ }
if (typeof el == "string") {
el = $(el);
}
if (typeof attr == "string") {
- if (attr.substring(0, 6) == "xlink:") {
- return el.getAttributeNS(xlink, attr.substring(6));
+ if (el.nodeType == 1) {
+ if (attr.substring(0, 6) == "xlink:") {
+ return el.getAttributeNS(xlink, attr.substring(6));
+ }
+ if (attr.substring(0, 4) == "xml:") {
+ return el.getAttributeNS(xmlns, attr.substring(4));
+ }
+ return el.getAttribute(attr);
+ } else if (attr == "text") {
+ return el.nodeValue;
+ } else {
+ return null;
}
- if (attr.substring(0, 4) == "xml:") {
- return el.getAttributeNS(xmlns, attr.substring(4));
- }
- return el.getAttribute(attr);
}
- for (var key in attr) if (attr[has](key)) {
- var val = Str(attr[key]);
- if (val) {
- if (key.substring(0, 6) == "xlink:") {
- el.setAttributeNS(xlink, key.substring(6), val);
- } else if (key.substring(0, 4) == "xml:") {
- el.setAttributeNS(xmlns, key.substring(4), val);
+ if (el.nodeType == 1) {
+ for (var key in attr) if (attr[has](key)) {
+ var val = Str(attr[key]);
+ if (val) {
+ if (key.substring(0, 6) == "xlink:") {
+ el.setAttributeNS(xlink, key.substring(6), val);
+ } else if (key.substring(0, 4) == "xml:") {
+ el.setAttributeNS(xmlns, key.substring(4), val);
+ } else {
+ el.setAttribute(key, val);
+ }
} else {
- el.setAttribute(key, val);
+ el.removeAttribute(key);
}
- } else {
- el.removeAttribute(key);
}
+ } else if ("text" in attr) {
+ el.nodeValue = attr.text;
}
} else {
el = glob.doc.createElementNS(xmlns, el);
- // el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
}
return el;
}
Snap._.$ = $;
Snap._.id = ID;
@@ -947,28 +1014,10 @@
return Str(str).replace(tokenRegex, function (all, key) {
return replacer(all, key, obj);
});
};
})();
-var preload = (function () {
- function onerror() {
- this.parentNode.removeChild(this);
- }
- return function (src, f) {
- var img = glob.doc.createElement("img"),
- body = glob.doc.body;
- img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
- img.onload = function () {
- f.call(img);
- img.onload = img.onerror = null;
- body.removeChild(img);
- };
- img.onerror = onerror;
- body.appendChild(img);
- img.src = src;
- };
-}());
function clone(obj) {
if (typeof obj == "function" || Object(obj) !== obj) {
return obj;
}
var res = new obj.constructor;
@@ -1042,12 +1091,88 @@
* Transform angle to degrees
- rad (number) angle in radians
= (number) angle in degrees
\*/
Snap.deg = deg;
-// SIERRA for which point is the angle calculated?
/*\
+ * Snap.sin
+ [ method ]
+ **
+ * Equivalent to `Math.sin()` only works with degrees, not radians.
+ - angle (number) angle in degrees
+ = (number) sin
+\*/
+Snap.sin = function (angle) {
+ return math.sin(Snap.rad(angle));
+};
+/*\
+ * Snap.tan
+ [ method ]
+ **
+ * Equivalent to `Math.tan()` only works with degrees, not radians.
+ - angle (number) angle in degrees
+ = (number) tan
+\*/
+Snap.tan = function (angle) {
+ return math.tan(Snap.rad(angle));
+};
+/*\
+ * Snap.cos
+ [ method ]
+ **
+ * Equivalent to `Math.cos()` only works with degrees, not radians.
+ - angle (number) angle in degrees
+ = (number) cos
+\*/
+Snap.cos = function (angle) {
+ return math.cos(Snap.rad(angle));
+};
+/*\
+ * Snap.asin
+ [ method ]
+ **
+ * Equivalent to `Math.asin()` only works with degrees, not radians.
+ - num (number) value
+ = (number) asin in degrees
+\*/
+Snap.asin = function (num) {
+ return Snap.deg(math.asin(num));
+};
+/*\
+ * Snap.acos
+ [ method ]
+ **
+ * Equivalent to `Math.acos()` only works with degrees, not radians.
+ - num (number) value
+ = (number) acos in degrees
+\*/
+Snap.acos = function (num) {
+ return Snap.deg(math.acos(num));
+};
+/*\
+ * Snap.atan
+ [ method ]
+ **
+ * Equivalent to `Math.atan()` only works with degrees, not radians.
+ - num (number) value
+ = (number) atan in degrees
+\*/
+Snap.atan = function (num) {
+ return Snap.deg(math.atan(num));
+};
+/*\
+ * Snap.atan2
+ [ method ]
+ **
+ * Equivalent to `Math.atan2()` only works with degrees, not radians.
+ - num (number) value
+ = (number) atan2 in degrees
+\*/
+Snap.atan2 = function (num) {
+ return Snap.deg(math.atan2(num));
+};
+/*\
* Snap.angle
[ method ]
**
* Returns an angle between two or three points
> Parameters
@@ -1059,10 +1184,104 @@
- y3 (number) #optional y coord of third point
= (number) angle in degrees
\*/
Snap.angle = angle;
/*\
+ * Snap.len
+ [ method ]
+ **
+ * Returns distance between two 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
+ = (number) distance
+\*/
+Snap.len = function (x1, y1, x2, y2) {
+ return Math.sqrt(Snap.len2(x1, y1, x2, y2));
+};
+/*\
+ * Snap.len2
+ [ method ]
+ **
+ * Returns squared distance between two 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
+ = (number) distance
+\*/
+Snap.len2 = function (x1, y1, x2, y2) {
+ return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
+};
+/*\
+ * Snap.closestPoint
+ [ method ]
+ **
+ * Returns closest point to a given one on a given path.
+ > Parameters
+ - path (Element) path element
+ - x (number) x coord of a point
+ - y (number) y coord of a point
+ = (object) in format
+ {
+ x (number) x coord of the point on the path
+ y (number) y coord of the point on the path
+ length (number) length of the path to the point
+ distance (number) distance from the given point to the path
+ }
+\*/
+// Copied from http://bl.ocks.org/mbostock/8027637
+Snap.closestPoint = function (path, x, y) {
+ function distance2(p) {
+ var dx = p.x - x,
+ dy = p.y - y;
+ return dx * dx + dy * dy;
+ }
+ var pathNode = path.node,
+ pathLength = pathNode.getTotalLength(),
+ precision = pathLength / pathNode.pathSegList.numberOfItems * .125,
+ best,
+ bestLength,
+ bestDistance = Infinity;
+
+ // linear scan for coarse approximation
+ for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
+ if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
+ best = scan, bestLength = scanLength, bestDistance = scanDistance;
+ }
+ }
+
+ // binary search for precise estimate
+ precision *= .5;
+ while (precision > .5) {
+ var before,
+ after,
+ beforeLength,
+ afterLength,
+ beforeDistance,
+ afterDistance;
+ if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) {
+ best = before, bestLength = beforeLength, bestDistance = beforeDistance;
+ } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) {
+ best = after, bestLength = afterLength, bestDistance = afterDistance;
+ } else {
+ precision *= .5;
+ }
+ }
+
+ best = {
+ x: best.x,
+ y: best.y,
+ length: bestLength,
+ distance: Math.sqrt(bestDistance)
+ };
+ return best;
+}
+/*\
* Snap.is
[ method ]
**
* Handy replacement for the `typeof` operator
- o (…) any object or primitive
@@ -1097,278 +1316,10 @@
return value - rem + values;
}
}
return value;
};
-
-// MATRIX
-function Matrix(a, b, c, d, e, f) {
- if (b == null && objectToString.call(a) == "[object SVGMatrix]") {
- this.a = a.a;
- this.b = a.b;
- this.c = a.c;
- this.d = a.d;
- this.e = a.e;
- this.f = a.f;
- return;
- }
- if (a != null) {
- this.a = +a;
- this.b = +b;
- this.c = +c;
- this.d = +d;
- this.e = +e;
- this.f = +f;
- } else {
- this.a = 1;
- this.b = 0;
- this.c = 0;
- this.d = 1;
- this.e = 0;
- this.f = 0;
- }
-}
-(function (matrixproto) {
- /*\
- * Matrix.add
- [ method ]
- **
- * Adds the given matrix to existing one
- - a (number)
- - b (number)
- - c (number)
- - d (number)
- - e (number)
- - f (number)
- * or
- - matrix (object) @Matrix
- \*/
- matrixproto.add = function (a, b, c, d, e, f) {
- var out = [[], [], []],
- m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
- matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
- x, y, z, res;
-
- if (a && a instanceof Matrix) {
- matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
- }
-
- for (x = 0; x < 3; x++) {
- for (y = 0; y < 3; y++) {
- res = 0;
- for (z = 0; z < 3; z++) {
- res += m[x][z] * matrix[z][y];
- }
- out[x][y] = res;
- }
- }
- this.a = out[0][0];
- this.b = out[1][0];
- this.c = out[0][1];
- this.d = out[1][1];
- this.e = out[0][2];
- this.f = out[1][2];
- return this;
- };
- /*\
- * Matrix.invert
- [ method ]
- **
- * Returns an inverted version of the matrix
- = (object) @Matrix
- \*/
- matrixproto.invert = function () {
- var me = this,
- x = me.a * me.d - me.b * me.c;
- return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
- };
- /*\
- * Matrix.clone
- [ method ]
- **
- * Returns a copy of the matrix
- = (object) @Matrix
- \*/
- matrixproto.clone = function () {
- return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
- };
- /*\
- * Matrix.translate
- [ method ]
- **
- * Translate the matrix
- - x (number) horizontal offset distance
- - y (number) vertical offset distance
- \*/
- matrixproto.translate = function (x, y) {
- return this.add(1, 0, 0, 1, x, y);
- };
- /*\
- * Matrix.scale
- [ method ]
- **
- * Scales the matrix
- - x (number) amount to be scaled, with `1` resulting in no change
- - y (number) #optional amount to scale along the vertical axis. (Otherwise `x` applies to both axes.)
- - cx (number) #optional horizontal origin point from which to scale
- - cy (number) #optional vertical origin point from which to scale
- * Default cx, cy is the middle point of the element.
- \*/
- matrixproto.scale = function (x, y, cx, cy) {
- y == null && (y = x);
- (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
- this.add(x, 0, 0, y, 0, 0);
- (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
- return this;
- };
- /*\
- * Matrix.rotate
- [ method ]
- **
- * Rotates the matrix
- - a (number) angle of rotation, in degrees
- - x (number) horizontal origin point from which to rotate
- - y (number) vertical origin point from which to rotate
- \*/
- matrixproto.rotate = function (a, x, y) {
- a = rad(a);
- x = x || 0;
- y = y || 0;
- var cos = +math.cos(a).toFixed(9),
- sin = +math.sin(a).toFixed(9);
- this.add(cos, sin, -sin, cos, x, y);
- return this.add(1, 0, 0, 1, -x, -y);
- };
- /*\
- * Matrix.x
- [ method ]
- **
- * Returns x coordinate for given point after transformation described by the matrix. See also @Matrix.y
- - x (number)
- - y (number)
- = (number) x
- \*/
- matrixproto.x = function (x, y) {
- return x * this.a + y * this.c + this.e;
- };
- /*\
- * Matrix.y
- [ method ]
- **
- * Returns y coordinate for given point after transformation described by the matrix. See also @Matrix.x
- - x (number)
- - y (number)
- = (number) y
- \*/
- matrixproto.y = function (x, y) {
- return x * this.b + y * this.d + this.f;
- };
- matrixproto.get = function (i) {
- return +this[Str.fromCharCode(97 + i)].toFixed(4);
- };
- matrixproto.toString = function () {
- return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")";
- };
- matrixproto.offset = function () {
- return [this.e.toFixed(4), this.f.toFixed(4)];
- };
- function norm(a) {
- return a[0] * a[0] + a[1] * a[1];
- }
- function normalize(a) {
- var mag = math.sqrt(norm(a));
- a[0] && (a[0] /= mag);
- a[1] && (a[1] /= mag);
- }
- /*\
- * Matrix.split
- [ method ]
- **
- * Splits matrix into primitive transformations
- = (object) in format:
- o dx (number) translation by x
- o dy (number) translation by y
- o scalex (number) scale by x
- o scaley (number) scale by y
- o shear (number) shear
- o rotate (number) rotation in deg
- o isSimple (boolean) could it be represented via simple transformations
- \*/
- matrixproto.split = function () {
- var out = {};
- // translation
- out.dx = this.e;
- out.dy = this.f;
-
- // scale and shear
- var row = [[this.a, this.c], [this.b, this.d]];
- out.scalex = math.sqrt(norm(row[0]));
- normalize(row[0]);
-
- out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
- row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
-
- out.scaley = math.sqrt(norm(row[1]));
- normalize(row[1]);
- out.shear /= out.scaley;
-
- // rotation
- var sin = -row[0][1],
- cos = row[1][1];
- if (cos < 0) {
- out.rotate = deg(math.acos(cos));
- if (sin < 0) {
- out.rotate = 360 - out.rotate;
- }
- } else {
- out.rotate = deg(math.asin(sin));
- }
-
- out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
- out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
- out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
- return out;
- };
- /*\
- * Matrix.toTransformString
- [ method ]
- **
- * Returns transform string that represents given matrix
- = (string) transform string
- \*/
- matrixproto.toTransformString = function (shorter) {
- var s = shorter || this.split();
- if (s.isSimple) {
- s.scalex = +s.scalex.toFixed(4);
- s.scaley = +s.scaley.toFixed(4);
- s.rotate = +s.rotate.toFixed(4);
- return (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) +
- (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
- (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E);
- } else {
- return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
- }
- };
-})(Matrix.prototype);
-/*\
- * Snap.Matrix
- [ method ]
- **
- * Utility method
- **
- * Returns a matrix based on the given parameters
- - a (number)
- - b (number)
- - c (number)
- - d (number)
- - e (number)
- - f (number)
- * or
- - svgMatrix (SVGMatrix)
- = (object) @Matrix
-\*/
-Snap.Matrix = Matrix;
// Colour
/*\
* Snap.getRGB
[ method ]
**
@@ -1477,11 +1428,10 @@
rgb.opacity = is(opacity, "finite") ? opacity : 1;
return rgb;
}
return {r: -1, g: -1, b: -1, hex: "none", error: 1, toString: rgbtoString};
}, Snap);
-// SIERRA It seems odd that the following 3 conversion methods are not expressed as .this2that(), like the others.
/*\
* Snap.hsb
[ method ]
**
* Converts HSB values to a hex representation of the color
@@ -1522,11 +1472,11 @@
return "rgba(" + [round(r), round(g), round(b), +o.toFixed(2)] + ")";
}
return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
});
var toHex = function (color) {
- var i = glob.doc.getElementsByTagName("head")[0],
+ var i = glob.doc.getElementsByTagName("head")[0] || glob.doc.getElementsByTagName("svg")[0],
red = "rgb(255, 0, 0)";
toHex = cacher(function (color) {
if (color.toLowerCase() == "red") {
return red;
}
@@ -1581,11 +1531,10 @@
toString: rgbtoString
};
is(o, "finite") && (rgb.opacity = o);
return rgb;
};
-// SIERRA Clarify if Snap does not support consolidated HSLA/RGBA colors. E.g., can you specify a semi-transparent value for Snap.filter.shadow()?
/*\
* Snap.color
[ method ]
**
* Parses the color string and returns an object featuring the color's component values
@@ -1657,12 +1606,12 @@
\*/
Snap.hsb2rgb = function (h, s, v, o) {
if (is(h, "object") && "h" in h && "s" in h && "b" in h) {
v = h.b;
s = h.s;
- h = h.h;
o = h.o;
+ h = h.h;
}
h *= 360;
var R, G, B, X, C;
h = (h % 360) / 60;
C = v * s;
@@ -1784,11 +1733,10 @@
C / (2 - 2 * L));
return {h: H, s: S, l: L, toString: hsltoString};
};
// Transformations
-// SIERRA Snap.parsePathString(): By _array of arrays,_ I assume you mean a format like this for two separate segments? [ ["M10,10","L90,90"], ["M90,10","L10,90"] ] Otherwise how is each command structured?
/*\
* Snap.parsePathString
[ method ]
**
* Utility method
@@ -1878,11 +1826,13 @@
params = params.split(/\s*,\s*|\s+/);
if (name == "rotate" && params.length == 1) {
params.push(0, 0);
}
if (name == "scale") {
- if (params.length == 2) {
+ if (params.length > 2) {
+ params = params.slice(0, 2);
+ } else if (params.length == 2) {
params.push(0, 0);
}
if (params.length == 1) {
params.push(params[0], 0, 0);
}
@@ -1897,14 +1847,14 @@
return all;
});
return res;
}
Snap._.svgTransform2string = svgTransform2string;
-Snap._.rgTransform = new RegExp("^[a-z][" + spaces + "]*-?\\.?\\d", "i");
+Snap._.rgTransform = /^[a-z][\s]*-?\.?\d/i;
function transform2matrix(tstr, bbox) {
var tdata = parseTransformString(tstr),
- m = new Matrix;
+ m = new Snap.Matrix;
if (tdata) {
for (var i = 0, ii = tdata.length; i < ii; i++) {
var t = tdata[i],
tlen = t.length,
command = Str(t[0]).toLowerCase(),
@@ -1967,42 +1917,10 @@
}
}
return m;
}
Snap._.transform2matrix = transform2matrix;
-function extractTransform(el, tstr) {
- if (tstr == null) {
- var doReturn = true;
- if (el.type == "linearGradient" || el.type == "radialGradient") {
- tstr = el.node.getAttribute("gradientTransform");
- } else if (el.type == "pattern") {
- tstr = el.node.getAttribute("patternTransform");
- } else {
- tstr = el.node.getAttribute("transform");
- }
- if (!tstr) {
- return new Matrix;
- }
- tstr = svgTransform2string(tstr);
- } else {
- if (!Snap._.rgTransform.test(tstr)) {
- tstr = svgTransform2string(tstr);
- } else {
- tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
- }
- if (is(tstr, "array")) {
- tstr = Snap.path ? Snap.path.toString.call(tstr) : Str(tstr);
- }
- el._.transform = tstr;
- }
- var m = transform2matrix(tstr, el.getBBox(1));
- if (doReturn) {
- return m;
- } else {
- el.matrix = m;
- }
-}
Snap._unit2px = unit2px;
var contains = glob.doc.contains || glob.doc.compareDocumentPosition ?
function (a, b) {
var adown = a.nodeType == 9 ? a.documentElement : a,
bup = b && b.parentNode;
@@ -2022,61 +1940,68 @@
}
}
return false;
};
function getSomeDefs(el) {
- var cache = Snap._.someDefs;
- if (cache && contains(cache.ownerDocument.documentElement, cache)) {
- return cache;
- }
var p = (el.node.ownerSVGElement && wrap(el.node.ownerSVGElement)) ||
(el.node.parentNode && wrap(el.node.parentNode)) ||
Snap.select("svg") ||
Snap(0, 0),
pdefs = p.select("defs"),
defs = pdefs == null ? false : pdefs.node;
if (!defs) {
defs = make("defs", p.node).node;
}
- Snap._.someDefs = defs;
return defs;
}
+function getSomeSVG(el) {
+ return el.node.ownerSVGElement && wrap(el.node.ownerSVGElement) || Snap.select("svg");
+}
Snap._.getSomeDefs = getSomeDefs;
+Snap._.getSomeSVG = getSomeSVG;
function unit2px(el, name, value) {
- var defs = getSomeDefs(el),
+ var svg = getSomeSVG(el).node,
out = {},
- mgr = defs.querySelector(".svg---mgr");
+ mgr = svg.querySelector(".svg---mgr");
if (!mgr) {
mgr = $("rect");
- $(mgr, {width: 10, height: 10, "class": "svg---mgr"});
- defs.appendChild(mgr);
+ $(mgr, {x: -9e9, y: -9e9, width: 10, height: 10, "class": "svg---mgr", fill: "none"});
+ svg.appendChild(mgr);
}
function getW(val) {
if (val == null) {
return E;
}
if (val == +val) {
return val;
}
$(mgr, {width: val});
- return mgr.getBBox().width;
+ try {
+ return mgr.getBBox().width;
+ } catch (e) {
+ return 0;
+ }
}
function getH(val) {
if (val == null) {
return E;
}
if (val == +val) {
return val;
}
$(mgr, {height: val});
- return mgr.getBBox().height;
+ try {
+ return mgr.getBBox().height;
+ } catch (e) {
+ return 0;
+ }
}
function set(nam, f) {
if (name == null) {
- out[nam] = f(el.attr(nam));
+ out[nam] = f(el.attr(nam) || 0);
} else if (nam == name) {
- out = f(value == null ? el.attr(nam) : value);
+ out = f(value == null ? el.attr(nam) || 0 : value);
}
}
switch (el.type) {
case "rect":
set("rx", getW);
@@ -2120,10 +2045,11 @@
set("dy", getH);
break;
default:
set(name, getW);
}
+ svg.removeChild(mgr);
return out;
}
/*\
* Snap.select
[ method ]
@@ -2131,10 +2057,11 @@
* Wraps a DOM element specified by CSS selector as @Element
- query (string) CSS selector of the element
= (Element) the current element
\*/
Snap.select = function (query) {
+ query = Str(query).replace(/([^\\]):/g, "$1\\:");
return wrap(glob.doc.querySelector(query));
};
/*\
* Snap.selectAll
[ method ]
@@ -2173,52 +2100,72 @@
for (i = 0; i < children.length; i++) {
this[j++] = wrap(children[i]);
}
return this;
}
+// Hub garbage collector every 10s
+setInterval(function () {
+ for (var key in hub) if (hub[has](key)) {
+ var el = hub[key],
+ node = el.node;
+ if (el.type != "svg" && !node.ownerSVGElement || el.type == "svg" && (!node.parentNode || "ownerSVGElement" in node.parentNode && !node.ownerSVGElement)) {
+ delete hub[key];
+ }
+ }
+}, 1e4);
function Element(el) {
if (el.snap in hub) {
return hub[el.snap];
}
- var id = this.id = ID(),
- svg;
+ var svg;
try {
svg = el.ownerSVGElement;
} catch(e) {}
+ /*\
+ * Element.node
+ [ property (object) ]
+ **
+ * Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
+ > Usage
+ | // draw a circle at coordinate 10,10 with radius of 10
+ | var c = paper.circle(10, 10, 10);
+ | c.node.onclick = function () {
+ | c.attr("fill", "red");
+ | };
+ \*/
this.node = el;
if (svg) {
this.paper = new Paper(svg);
}
- this.type = el.tagName;
+ /*\
+ * Element.type
+ [ property (string) ]
+ **
+ * SVG tag name of the given element.
+ \*/
+ this.type = el.tagName || el.nodeName;
+ var id = this.id = ID(this);
this.anims = {};
this._ = {
transform: []
};
el.snap = id;
hub[id] = this;
if (this.type == "g") {
this.add = add2group;
+ }
+ if (this.type in {g: 1, mask: 1, pattern: 1, symbol: 1}) {
for (var method in Paper.prototype) if (Paper.prototype[has](method)) {
this[method] = Paper.prototype[method];
}
}
}
-function arrayFirstValue(arr) {
- var res;
- for (var i = 0, ii = arr.length; i < ii; i++) {
- res = res || arr[i];
- if (res) {
- return res;
- }
- }
-}
-(function (elproto) {
- /*\
+ /*\
* Element.attr
[ method ]
**
- * Gets or sets given attributes of the element
+ * Gets or sets given attributes of the element.
**
- params (object) contains key-value pairs of attributes you want to set
* or
- param (string) name of the attribute
= (Element) the current element
@@ -2227,38 +2174,501 @@
> Usage
| el.attr({
| fill: "#fc0",
| stroke: "#000",
| strokeWidth: 2, // CamelCase...
- | "fill-opacity": 0.5 // or dash-separated names
+ | "fill-opacity": 0.5, // or dash-separated names
+ | width: "*=2" // prefixed values
| });
| console.log(el.attr("fill")); // #fc0
+ * Prefixed values in format `"+=10"` supported. All four operations
+ * (`+`, `-`, `*` and `/`) could be used. Optionally you can use units for `+`
+ * and `-`: `"+=2em"`.
\*/
- elproto.attr = function (params, value) {
+ Element.prototype.attr = function (params, value) {
var el = this,
node = el.node;
if (!params) {
- return el;
+ if (node.nodeType != 1) {
+ return {
+ text: node.nodeValue
+ };
+ }
+ var attr = node.attributes,
+ out = {};
+ for (var i = 0, ii = attr.length; i < ii; i++) {
+ out[attr[i].nodeName] = attr[i].nodeValue;
+ }
+ return out;
}
if (is(params, "string")) {
if (arguments.length > 1) {
var json = {};
json[params] = value;
params = json;
} else {
- return arrayFirstValue(eve("snap.util.getattr."+params, el));
+ return eve("snap.util.getattr." + params, el).firstDefined();
}
}
for (var att in params) {
if (params[has](att)) {
eve("snap.util.attr." + att, el, params[att]);
}
}
return el;
};
-// SIERRA Element.getBBox(): Unclear why you would want to express the dimension of the box as a path.
-// SIERRA Element.getBBox(): Unclear why you would want to use r0/r1/r2. Also, basic definitions: wouldn't the _smallest circle that can be enclosed_ be a zero-radius point?
+/*\
+ * Snap.parse
+ [ method ]
+ **
+ * Parses SVG fragment and converts it into a @Fragment
+ **
+ - svg (string) SVG string
+ = (Fragment) the @Fragment
+\*/
+Snap.parse = function (svg) {
+ var f = glob.doc.createDocumentFragment(),
+ full = true,
+ div = glob.doc.createElement("div");
+ svg = Str(svg);
+ if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) {
+ svg = "<svg>" + svg + "</svg>";
+ full = false;
+ }
+ div.innerHTML = svg;
+ svg = div.getElementsByTagName("svg")[0];
+ if (svg) {
+ if (full) {
+ f = svg;
+ } else {
+ while (svg.firstChild) {
+ f.appendChild(svg.firstChild);
+ }
+ }
+ }
+ return new Fragment(f);
+};
+function Fragment(frag) {
+ this.node = frag;
+}
+/*\
+ * Snap.fragment
+ [ method ]
+ **
+ * Creates a DOM fragment from a given list of elements or strings
+ **
+ - varargs (…) SVG string
+ = (Fragment) the @Fragment
+\*/
+Snap.fragment = function () {
+ var args = Array.prototype.slice.call(arguments, 0),
+ f = glob.doc.createDocumentFragment();
+ for (var i = 0, ii = args.length; i < ii; i++) {
+ var item = args[i];
+ if (item.node && item.node.nodeType) {
+ f.appendChild(item.node);
+ }
+ if (item.nodeType) {
+ f.appendChild(item);
+ }
+ if (typeof item == "string") {
+ f.appendChild(Snap.parse(item).node);
+ }
+ }
+ return new Fragment(f);
+};
+
+function make(name, parent) {
+ var res = $(name);
+ parent.appendChild(res);
+ var el = wrap(res);
+ return el;
+}
+function Paper(w, h) {
+ var res,
+ desc,
+ defs,
+ proto = Paper.prototype;
+ if (w && w.tagName == "svg") {
+ if (w.snap in hub) {
+ return hub[w.snap];
+ }
+ var doc = w.ownerDocument;
+ res = new Element(w);
+ desc = w.getElementsByTagName("desc")[0];
+ defs = w.getElementsByTagName("defs")[0];
+ if (!desc) {
+ desc = $("desc");
+ desc.appendChild(doc.createTextNode("Created with Snap"));
+ 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 {
+ res = make("svg", glob.doc.body);
+ $(res.node, {
+ height: h,
+ version: 1.1,
+ width: w,
+ xmlns: xmlns
+ });
+ }
+ return res;
+}
+function wrap(dom) {
+ if (!dom) {
+ return dom;
+ }
+ if (dom instanceof Element || dom instanceof Fragment) {
+ return dom;
+ }
+ if (dom.tagName && dom.tagName.toLowerCase() == "svg") {
+ return new Paper(dom);
+ }
+ if (dom.tagName && dom.tagName.toLowerCase() == "object" && dom.type == "image/svg+xml") {
+ return new Paper(dom.contentDocument.getElementsByTagName("svg")[0]);
+ }
+ return new Element(dom);
+}
+
+Snap._.make = make;
+Snap._.wrap = wrap;
+/*\
+ * Paper.el
+ [ method ]
+ **
+ * Creates an element on paper with a given name and no attributes
+ **
+ - name (string) tag name
+ - attr (object) attributes
+ = (Element) the current element
+ > Usage
+ | var c = paper.circle(10, 10, 10); // is the same as...
+ | var c = paper.el("circle").attr({
+ | cx: 10,
+ | cy: 10,
+ | r: 10
+ | });
+ | // and the same as
+ | var c = paper.el("circle", {
+ | cx: 10,
+ | cy: 10,
+ | r: 10
+ | });
+\*/
+Paper.prototype.el = function (name, attr) {
+ var el = make(name, this.node);
+ attr && el.attr(attr);
+ return el;
+};
+/*\
+ * Element.children
+ [ method ]
+ **
+ * Returns array of all the children of the element.
+ = (array) array of Elements
+\*/
+Element.prototype.children = function () {
+ var out = [],
+ ch = this.node.childNodes;
+ for (var i = 0, ii = ch.length; i < ii; i++) {
+ out[i] = Snap(ch[i]);
+ }
+ return out;
+};
+function jsonFiller(root, o) {
+ for (var i = 0, ii = root.length; i < ii; i++) {
+ var item = {
+ type: root[i].type,
+ attr: root[i].attr()
+ },
+ children = root[i].children();
+ o.push(item);
+ if (children.length) {
+ jsonFiller(children, item.childNodes = []);
+ }
+ }
+}
+/*\
+ * Element.toJSON
+ [ method ]
+ **
+ * Returns object representation of the given element and all its children.
+ = (object) in format
+ o {
+ o type (string) this.type,
+ o attr (object) attributes map,
+ o childNodes (array) optional array of children in the same format
+ o }
+\*/
+Element.prototype.toJSON = function () {
+ var out = [];
+ jsonFiller([this], out);
+ return out[0];
+};
+// default
+eve.on("snap.util.getattr", function () {
+ var att = eve.nt();
+ att = att.substring(att.lastIndexOf(".") + 1);
+ var css = att.replace(/[A-Z]/g, function (letter) {
+ return "-" + letter.toLowerCase();
+ });
+ if (cssAttr[has](css)) {
+ return this.node.ownerDocument.defaultView.getComputedStyle(this.node, null).getPropertyValue(css);
+ } else {
+ return $(this.node, att);
+ }
+});
+var cssAttr = {
+ "alignment-baseline": 0,
+ "baseline-shift": 0,
+ "clip": 0,
+ "clip-path": 0,
+ "clip-rule": 0,
+ "color": 0,
+ "color-interpolation": 0,
+ "color-interpolation-filters": 0,
+ "color-profile": 0,
+ "color-rendering": 0,
+ "cursor": 0,
+ "direction": 0,
+ "display": 0,
+ "dominant-baseline": 0,
+ "enable-background": 0,
+ "fill": 0,
+ "fill-opacity": 0,
+ "fill-rule": 0,
+ "filter": 0,
+ "flood-color": 0,
+ "flood-opacity": 0,
+ "font": 0,
+ "font-family": 0,
+ "font-size": 0,
+ "font-size-adjust": 0,
+ "font-stretch": 0,
+ "font-style": 0,
+ "font-variant": 0,
+ "font-weight": 0,
+ "glyph-orientation-horizontal": 0,
+ "glyph-orientation-vertical": 0,
+ "image-rendering": 0,
+ "kerning": 0,
+ "letter-spacing": 0,
+ "lighting-color": 0,
+ "marker": 0,
+ "marker-end": 0,
+ "marker-mid": 0,
+ "marker-start": 0,
+ "mask": 0,
+ "opacity": 0,
+ "overflow": 0,
+ "pointer-events": 0,
+ "shape-rendering": 0,
+ "stop-color": 0,
+ "stop-opacity": 0,
+ "stroke": 0,
+ "stroke-dasharray": 0,
+ "stroke-dashoffset": 0,
+ "stroke-linecap": 0,
+ "stroke-linejoin": 0,
+ "stroke-miterlimit": 0,
+ "stroke-opacity": 0,
+ "stroke-width": 0,
+ "text-anchor": 0,
+ "text-decoration": 0,
+ "text-rendering": 0,
+ "unicode-bidi": 0,
+ "visibility": 0,
+ "word-spacing": 0,
+ "writing-mode": 0
+};
+
+eve.on("snap.util.attr", function (value) {
+ var att = eve.nt(),
+ attr = {};
+ att = att.substring(att.lastIndexOf(".") + 1);
+ attr[att] = value;
+ var style = att.replace(/-(\w)/gi, function (all, letter) {
+ return letter.toUpperCase();
+ }),
+ css = att.replace(/[A-Z]/g, function (letter) {
+ return "-" + letter.toLowerCase();
+ });
+ if (cssAttr[has](css)) {
+ this.node.style[style] = value == null ? E : value;
+ } else {
+ $(this.node, attr);
+ }
+});
+(function (proto) {}(Paper.prototype));
+
+// simple ajax
+/*\
+ * Snap.ajax
+ [ method ]
+ **
+ * Simple implementation of Ajax
+ **
+ - url (string) URL
+ - postData (object|string) data for post request
+ - callback (function) callback
+ - scope (object) #optional scope of callback
+ * or
+ - url (string) URL
+ - callback (function) callback
+ - scope (object) #optional scope of callback
+ = (XMLHttpRequest) the XMLHttpRequest object, just in case
+\*/
+Snap.ajax = function (url, postData, callback, scope){
+ var req = new XMLHttpRequest,
+ id = ID();
+ if (req) {
+ if (is(postData, "function")) {
+ scope = callback;
+ callback = postData;
+ postData = null;
+ } else if (is(postData, "object")) {
+ var pd = [];
+ for (var key in postData) if (postData.hasOwnProperty(key)) {
+ pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key]));
+ }
+ postData = pd.join("&");
+ }
+ req.open((postData ? "POST" : "GET"), url, true);
+ if (postData) {
+ req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ }
+ if (callback) {
+ eve.once("snap.ajax." + id + ".0", callback);
+ eve.once("snap.ajax." + id + ".200", callback);
+ eve.once("snap.ajax." + id + ".304", callback);
+ }
+ req.onreadystatechange = function() {
+ if (req.readyState != 4) return;
+ eve("snap.ajax." + id + "." + req.status, scope, req);
+ };
+ if (req.readyState == 4) {
+ return req;
+ }
+ req.send(postData);
+ return req;
+ }
+};
+/*\
+ * Snap.load
+ [ method ]
+ **
+ * Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX)
+ **
+ - url (string) URL
+ - callback (function) callback
+ - scope (object) #optional scope of callback
+\*/
+Snap.load = function (url, callback, scope) {
+ Snap.ajax(url, function (req) {
+ var f = Snap.parse(req.responseText);
+ scope ? callback.call(scope, f) : callback(f);
+ });
+};
+var getOffset = function (elem) {
+ var box = elem.getBoundingClientRect(),
+ doc = elem.ownerDocument,
+ body = doc.body,
+ docElem = doc.documentElement,
+ clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
+ left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
+ return {
+ y: top,
+ x: left
+ };
+};
+/*\
+ * Snap.getElementByPoint
+ [ method ]
+ **
+ * Returns you topmost element under given point.
+ **
+ = (object) Snap element object
+ - x (number) x coordinate from the top left corner of the window
+ - y (number) y coordinate from the top left corner of the window
+ > Usage
+ | Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
+\*/
+Snap.getElementByPoint = function (x, y) {
+ var paper = this,
+ svg = paper.canvas,
+ target = glob.doc.elementFromPoint(x, y);
+ if (glob.win.opera && target.tagName == "svg") {
+ var so = getOffset(target),
+ sr = target.createSVGRect();
+ sr.x = x - so.x;
+ sr.y = y - so.y;
+ sr.width = sr.height = 1;
+ var hits = target.getIntersectionList(sr, null);
+ if (hits.length) {
+ target = hits[hits.length - 1];
+ }
+ }
+ if (!target) {
+ return null;
+ }
+ return wrap(target);
+};
+/*\
+ * Snap.plugin
+ [ method ]
+ **
+ * Let you write plugins. You pass in a function with five arguments, like this:
+ | Snap.plugin(function (Snap, Element, Paper, global, Fragment) {
+ | Snap.newmethod = function () {};
+ | Element.prototype.newmethod = function () {};
+ | Paper.prototype.newmethod = function () {};
+ | });
+ * Inside the function you have access to all main objects (and their
+ * prototypes). This allow you to extend anything you want.
+ **
+ - f (function) your plugin body
+\*/
+Snap.plugin = function (f) {
+ f(Snap, Element, Paper, glob, Fragment);
+};
+glob.win.Snap = Snap;
+return Snap;
+}(window || this));
+
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var elproto = Element.prototype,
+ is = Snap.is,
+ Str = String,
+ unit2px = Snap._unit2px,
+ $ = Snap._.$,
+ make = Snap._.make,
+ getSomeDefs = Snap._.getSomeDefs,
+ has = "hasOwnProperty",
+ wrap = Snap._.wrap;
/*\
* Element.getBBox
[ method ]
**
* Returns the bounding box descriptor for the given element
@@ -2281,33 +2691,81 @@
o y2: (number) y of the bottom edge,
o y: (number) y of the top edge
o }
\*/
elproto.getBBox = function (isWithoutTransform) {
- var el = this;
- if (el.type == "use") {
- el = el.original;
+ if (!Snap.Matrix || !Snap.path) {
+ return this.node.getBBox();
}
+ var el = this,
+ m = new Snap.Matrix;
if (el.removed) {
- return {};
+ return Snap._.box();
}
- var _ = el._;
- if (isWithoutTransform) {
- _.bboxwt = Snap.path.get[el.type] ? Snap.path.getBBox(el.realPath = Snap.path.get[el.type](el)) : Snap._.box(el.node.getBBox());
- return Snap._.box(_.bboxwt);
- } else {
- el.realPath = (Snap.path.get[el.type] || Snap.path.get.deflt)(el);
- _.bbox = Snap.path.getBBox(Snap.path.map(el.realPath, el.matrix));
+ while (el.type == "use") {
+ if (!isWithoutTransform) {
+ m = m.add(el.transform().localMatrix.translate(el.attr("x") || 0, el.attr("y") || 0));
+ }
+ if (el.original) {
+ el = el.original;
+ } else {
+ var href = el.attr("xlink:href");
+ el = el.original = el.node.ownerDocument.getElementById(href.substring(href.indexOf("#") + 1));
+ }
}
- return Snap._.box(_.bbox);
+ var _ = el._,
+ pathfinder = Snap.path.get[el.type] || Snap.path.get.deflt;
+ try {
+ if (isWithoutTransform) {
+ _.bboxwt = pathfinder ? Snap.path.getBBox(el.realPath = pathfinder(el)) : Snap._.box(el.node.getBBox());
+ return Snap._.box(_.bboxwt);
+ } else {
+ el.realPath = pathfinder(el);
+ el.matrix = el.transform().localMatrix;
+ _.bbox = Snap.path.getBBox(Snap.path.map(el.realPath, m.add(el.matrix)));
+ return Snap._.box(_.bbox);
+ }
+ } catch (e) {
+ // Firefox doesn’t give you bbox of hidden element
+ return Snap._.box();
+ }
};
var propString = function () {
return this.string;
};
-// SIERRA Element.transform(): seems to allow two return values, one of which (_Element_) is undefined.
-// SIERRA Element.transform(): if this only accepts one argument, it's unclear how it can both _get_ and _set_ a transform.
-// SIERRA Element.transform(): Unclear how Snap transform string format differs from SVG's.
+ function extractTransform(el, tstr) {
+ if (tstr == null) {
+ var doReturn = true;
+ if (el.type == "linearGradient" || el.type == "radialGradient") {
+ tstr = el.node.getAttribute("gradientTransform");
+ } else if (el.type == "pattern") {
+ tstr = el.node.getAttribute("patternTransform");
+ } else {
+ tstr = el.node.getAttribute("transform");
+ }
+ if (!tstr) {
+ return new Snap.Matrix;
+ }
+ tstr = Snap._.svgTransform2string(tstr);
+ } else {
+ if (!Snap._.rgTransform.test(tstr)) {
+ tstr = Snap._.svgTransform2string(tstr);
+ } else {
+ tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || "");
+ }
+ if (is(tstr, "array")) {
+ tstr = Snap.path ? Snap.path.toString.call(tstr) : Str(tstr);
+ }
+ el._.transform = tstr;
+ }
+ var m = Snap._.transform2matrix(tstr, el.getBBox(1));
+ if (doReturn) {
+ return m;
+ } else {
+ el.matrix = m;
+ }
+ }
/*\
* Element.transform
[ method ]
**
* Gets or sets transformation of the element
@@ -2327,31 +2785,44 @@
o }
\*/
elproto.transform = function (tstr) {
var _ = this._;
if (tstr == null) {
- var global = new Matrix(this.node.getCTM()),
+ var papa = this,
+ global = new Snap.Matrix(this.node.getCTM()),
local = extractTransform(this),
+ ms = [local],
+ m = new Snap.Matrix,
+ i,
localString = local.toTransformString(),
string = Str(local) == Str(this.matrix) ?
- _.transform : localString;
+ Str(_.transform) : localString;
+ while (papa.type != "svg" && (papa = papa.parent())) {
+ ms.push(extractTransform(papa));
+ }
+ i = ms.length;
+ while (i--) {
+ m.add(ms[i]);
+ }
return {
string: string,
globalMatrix: global,
+ totalMatrix: m,
localMatrix: local,
diffMatrix: global.clone().add(local.invert()),
global: global.toTransformString(),
+ total: m.toTransformString(),
local: localString,
toString: propString
};
}
- if (tstr instanceof Matrix) {
- // may be need to apply it directly
- // TODO: investigate
- tstr = tstr.toTransformString();
+ if (tstr instanceof Snap.Matrix) {
+ this.matrix = tstr;
+ this._.transform = tstr.toTransformString();
+ } else {
+ extractTransform(this, tstr);
}
- extractTransform(this, tstr);
if (this.node) {
if (this.type == "linearGradient" || this.type == "radialGradient") {
$(this.node, {gradientTransform: this.matrix});
} else if (this.type == "pattern") {
@@ -2429,10 +2900,23 @@
- el (Element) element to prepend
= (Element) the parent element
\*/
elproto.prepend = function (el) {
if (el) {
+ if (el.type == "set") {
+ var it = this,
+ first;
+ el.forEach(function (el) {
+ if (first) {
+ first.after(el);
+ } else {
+ it.prepend(el);
+ }
+ first = el;
+ });
+ return this;
+ }
el = wrap(el);
var parent = el.parent();
this.node.insertBefore(el.node, this.node.firstChild);
this.add && this.add();
el.paper = this.paper;
@@ -2629,18 +3113,10 @@
"xlink:href": "#" + id
});
use.original = this;
return use;
};
- /*\
- * Element.clone
- [ method ]
- **
- * Creates a clone of the element and inserts it after the element
- **
- = (Element) the clone
- \*/
function fixids(el) {
var els = el.selectAll("*"),
it,
url = /^\s*url\(("|'|)(.*)\1\)\s*$/,
ids = [],
@@ -2699,37 +3175,42 @@
fs[j](ids[i].id);
}
}
}
}
+ /*\
+ * Element.clone
+ [ method ]
+ **
+ * Creates a clone of the element and inserts it after the element
+ **
+ = (Element) the clone
+ \*/
elproto.clone = function () {
var clone = wrap(this.node.cloneNode(true));
if ($(clone.node, "id")) {
$(clone.node, {id: clone.id});
}
fixids(clone);
clone.insertAfter(this);
return clone;
};
-// SIERRA Element.toDefs(): If this _moves_ an element to the <defs> region, why is the return value a _clone_? Also unclear why it's called the _relative_ <defs> section. Perhaps _shared_?
/*\
* Element.toDefs
[ method ]
**
* Moves element to the shared `<defs>` area
**
- = (Element) the clone
+ = (Element) the element
\*/
elproto.toDefs = function () {
var defs = getSomeDefs(this);
defs.appendChild(this.node);
return this;
};
-// SIERRA Element.pattern(): x/y/width/height data types are listed as both String and Number. Is that an error, or does it mean strings are coerced?
-// SIERRA Element.pattern(): clarify that x/y are offsets that e.g., may add gutters between the tiles.
/*\
- * Element.pattern
+ * Element.toPattern
[ method ]
**
* Creates a `<pattern>` element from the current element
**
* To create a pattern you have to specify the pattern rect:
@@ -2747,11 +3228,11 @@
| c = paper.circle(200, 200, 100);
| c.attr({
| fill: p
| });
\*/
- elproto.pattern = function (x, y, width, height) {
+ elproto.pattern = elproto.toPattern = function (x, y, width, height) {
var p = make("pattern", getSomeDefs(this));
if (x == null) {
x = this.getBBox();
}
if (is(x, "object") && "x" in x) {
@@ -2803,11 +3284,11 @@
refX = x.refX || x.cx;
refY = x.refY || x.cy;
x = x.x;
}
$(p.node, {
- viewBox: [x, y, width, height].join(S),
+ viewBox: [x, y, width, height].join(" "),
markerWidth: width,
markerHeight: height,
orient: "auto",
refX: refX || 0,
refY: refY || 0,
@@ -2834,11 +3315,11 @@
this.attr = attr;
this.dur = ms;
easing && (this.easing = easing);
callback && (this.callback = callback);
};
- // SIERRA All object methods should feature sample code. This is just one instance.
+ Snap._.Animation = Animation;
/*\
* Snap.animation
[ method ]
**
* Creates an animation object
@@ -2859,10 +3340,11 @@
* Returns a set of animations that may be able to manipulate the current element
**
= (object) in format:
o {
o anim (object) animation object,
+ o mina (object) @mina object,
o curStatus (number) 0..1 — status of the animation: 0 — just started, 1 — just finished,
o status (function) gets or sets the status of the animation,
o stop (function) stops the animation
o }
\*/
@@ -2871,10 +3353,11 @@
res = [];
for (var id in el.anims) if (el.anims[has](id)) {
(function (a) {
res.push({
anim: new Animation(a._attrs, a.dur, a.easing, a._callback),
+ mina: a,
curStatus: a.status(),
status: function (val) {
return a.status(val);
},
stop: function () {
@@ -2938,12 +3421,10 @@
for (var i = 0, ii = anims.length; i < ii; i++) {
anims[i].stop();
}
return this;
};
- // SIERRA Element.animate(): For _attrs_, clarify if they represent the destination values, and if the animation executes relative to the element's current attribute values.
- // SIERRA would a _custom_ animation function be an SVG keySplines value?
/*\
* Element.animate
[ method ]
**
* Animates the given attributes of the element
@@ -2960,11 +3441,11 @@
easing = mina.linear;
}
if (attrs instanceof Animation) {
callback = attrs.callback;
easing = attrs.easing;
- ms = easing.dur;
+ ms = attrs.dur;
attrs = attrs.attr;
}
var fkeys = [], tkeys = [], keys = {}, from, to, f, eq,
el = this;
for (var key in attrs) if (attrs[has](key)) {
@@ -2991,10 +3472,11 @@
el.attr(attr);
}, easing);
el.anims[anim.id] = anim;
anim._attrs = attrs;
anim._callback = callback;
+ eve("snap.animcreated." + el.id, anim);
eve.once("mina.finish." + anim.id, function () {
delete el.anims[anim.id];
callback && callback.call(el);
});
eve.once("mina.stop." + anim.id, function () {
@@ -3112,282 +3594,1014 @@
type && (res += "/>");
}
return res;
};
}
-}(Element.prototype));
-// SIERRA Snap.parse() accepts & returns a fragment, but there's no info on what it does in between. What if it doesn't parse?
-/*\
- * Snap.parse
- [ method ]
- **
- * Parses SVG fragment and converts it into a @Fragment
- **
- - svg (string) SVG string
- = (Fragment) the @Fragment
-\*/
-Snap.parse = function (svg) {
- var f = glob.doc.createDocumentFragment(),
- full = true,
- div = glob.doc.createElement("div");
- svg = Str(svg);
- if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) {
- svg = "<svg>" + svg + "</svg>";
- full = false;
- }
- div.innerHTML = svg;
- svg = div.getElementsByTagName("svg")[0];
- if (svg) {
- if (full) {
- f = svg;
- } else {
- while (svg.firstChild) {
- f.appendChild(svg.firstChild);
- }
+ elproto.toDataURL = function () {
+ if (window && window.btoa) {
+ var bb = this.getBBox(),
+ svg = Snap.format('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}" viewBox="{x} {y} {width} {height}">{contents}</svg>', {
+ x: +bb.x.toFixed(3),
+ y: +bb.y.toFixed(3),
+ width: +bb.width.toFixed(3),
+ height: +bb.height.toFixed(3),
+ contents: this.outerSVG()
+ });
+ return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svg)));
}
- }
- div.innerHTML = E;
- return new Fragment(f);
-};
-function Fragment(frag) {
- this.node = frag;
-}
-/*\
- * Fragment.select
- [ method ]
- **
- * See @Element.select
-\*/
-Fragment.prototype.select = Element.prototype.select;
-/*\
- * Fragment.selectAll
- [ method ]
- **
- * See @Element.selectAll
-\*/
-Fragment.prototype.selectAll = Element.prototype.selectAll;
-// SIERRA Snap.fragment() could especially use a code example
-/*\
- * Snap.fragment
- [ method ]
- **
- * Creates a DOM fragment from a given list of elements or strings
- **
- - varargs (…) SVG string
- = (Fragment) the @Fragment
-\*/
-Snap.fragment = function () {
- var args = Array.prototype.slice.call(arguments, 0),
- f = glob.doc.createDocumentFragment();
- for (var i = 0, ii = args.length; i < ii; i++) {
- var item = args[i];
- if (item.node && item.node.nodeType) {
- f.appendChild(item.node);
+ };
+ /*\
+ * Fragment.select
+ [ method ]
+ **
+ * See @Element.select
+ \*/
+ Fragment.prototype.select = elproto.select;
+ /*\
+ * Fragment.selectAll
+ [ method ]
+ **
+ * See @Element.selectAll
+ \*/
+ Fragment.prototype.selectAll = elproto.selectAll;
+});
+
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var objectToString = Object.prototype.toString,
+ Str = String,
+ math = Math,
+ E = "";
+ function Matrix(a, b, c, d, e, f) {
+ if (b == null && objectToString.call(a) == "[object SVGMatrix]") {
+ this.a = a.a;
+ this.b = a.b;
+ this.c = a.c;
+ this.d = a.d;
+ this.e = a.e;
+ this.f = a.f;
+ return;
}
- if (item.nodeType) {
- f.appendChild(item);
+ if (a != null) {
+ this.a = +a;
+ this.b = +b;
+ this.c = +c;
+ this.d = +d;
+ this.e = +e;
+ this.f = +f;
+ } else {
+ this.a = 1;
+ this.b = 0;
+ this.c = 0;
+ this.d = 1;
+ this.e = 0;
+ this.f = 0;
}
- if (typeof item == "string") {
- f.appendChild(Snap.parse(item).node);
- }
}
- return new Fragment(f);
-};
+ (function (matrixproto) {
+ /*\
+ * Matrix.add
+ [ method ]
+ **
+ * Adds the given matrix to existing one
+ - a (number)
+ - b (number)
+ - c (number)
+ - d (number)
+ - e (number)
+ - f (number)
+ * or
+ - matrix (object) @Matrix
+ \*/
+ matrixproto.add = function (a, b, c, d, e, f) {
+ var out = [[], [], []],
+ m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
+ matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
+ x, y, z, res;
-function make(name, parent) {
- var res = $(name);
- parent.appendChild(res);
- var el = wrap(res);
- el.type = name;
- return el;
-}
-function Paper(w, h) {
- var res,
- desc,
- defs,
- proto = Paper.prototype;
- if (w && w.tagName == "svg") {
- if (w.snap in hub) {
- return hub[w.snap];
+ if (a && a instanceof Matrix) {
+ matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
+ }
+
+ for (x = 0; x < 3; x++) {
+ for (y = 0; y < 3; y++) {
+ res = 0;
+ for (z = 0; z < 3; z++) {
+ res += m[x][z] * matrix[z][y];
+ }
+ out[x][y] = res;
+ }
+ }
+ this.a = out[0][0];
+ this.b = out[1][0];
+ this.c = out[0][1];
+ this.d = out[1][1];
+ this.e = out[0][2];
+ this.f = out[1][2];
+ return this;
+ };
+ /*\
+ * Matrix.invert
+ [ method ]
+ **
+ * Returns an inverted version of the matrix
+ = (object) @Matrix
+ \*/
+ matrixproto.invert = function () {
+ var me = this,
+ x = me.a * me.d - me.b * me.c;
+ return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
+ };
+ /*\
+ * Matrix.clone
+ [ method ]
+ **
+ * Returns a copy of the matrix
+ = (object) @Matrix
+ \*/
+ matrixproto.clone = function () {
+ return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
+ };
+ /*\
+ * Matrix.translate
+ [ method ]
+ **
+ * Translate the matrix
+ - x (number) horizontal offset distance
+ - y (number) vertical offset distance
+ \*/
+ matrixproto.translate = function (x, y) {
+ return this.add(1, 0, 0, 1, x, y);
+ };
+ /*\
+ * Matrix.scale
+ [ method ]
+ **
+ * Scales the matrix
+ - x (number) amount to be scaled, with `1` resulting in no change
+ - y (number) #optional amount to scale along the vertical axis. (Otherwise `x` applies to both axes.)
+ - cx (number) #optional horizontal origin point from which to scale
+ - cy (number) #optional vertical origin point from which to scale
+ * Default cx, cy is the middle point of the element.
+ \*/
+ matrixproto.scale = function (x, y, cx, cy) {
+ y == null && (y = x);
+ (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
+ this.add(x, 0, 0, y, 0, 0);
+ (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
+ return this;
+ };
+ /*\
+ * Matrix.rotate
+ [ method ]
+ **
+ * Rotates the matrix
+ - a (number) angle of rotation, in degrees
+ - x (number) horizontal origin point from which to rotate
+ - y (number) vertical origin point from which to rotate
+ \*/
+ matrixproto.rotate = function (a, x, y) {
+ a = Snap.rad(a);
+ x = x || 0;
+ y = y || 0;
+ var cos = +math.cos(a).toFixed(9),
+ sin = +math.sin(a).toFixed(9);
+ this.add(cos, sin, -sin, cos, x, y);
+ return this.add(1, 0, 0, 1, -x, -y);
+ };
+ /*\
+ * Matrix.x
+ [ method ]
+ **
+ * Returns x coordinate for given point after transformation described by the matrix. See also @Matrix.y
+ - x (number)
+ - y (number)
+ = (number) x
+ \*/
+ matrixproto.x = function (x, y) {
+ return x * this.a + y * this.c + this.e;
+ };
+ /*\
+ * Matrix.y
+ [ method ]
+ **
+ * Returns y coordinate for given point after transformation described by the matrix. See also @Matrix.x
+ - x (number)
+ - y (number)
+ = (number) y
+ \*/
+ matrixproto.y = function (x, y) {
+ return x * this.b + y * this.d + this.f;
+ };
+ matrixproto.get = function (i) {
+ return +this[Str.fromCharCode(97 + i)].toFixed(4);
+ };
+ matrixproto.toString = function () {
+ return "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")";
+ };
+ matrixproto.offset = function () {
+ return [this.e.toFixed(4), this.f.toFixed(4)];
+ };
+ function norm(a) {
+ return a[0] * a[0] + a[1] * a[1];
}
- res = new Element(w);
- desc = w.getElementsByTagName("desc")[0];
- defs = w.getElementsByTagName("defs")[0];
- if (!desc) {
- desc = $("desc");
- desc.appendChild(glob.doc.createTextNode("Created with Snap"));
- res.node.appendChild(desc);
+ function normalize(a) {
+ var mag = math.sqrt(norm(a));
+ a[0] && (a[0] /= mag);
+ a[1] && (a[1] /= mag);
}
- if (!defs) {
- defs = $("defs");
- res.node.appendChild(defs);
+ /*\
+ * Matrix.determinant
+ [ method ]
+ **
+ * Finds determinant of the given matrix.
+ = (number) determinant
+ \*/
+ matrixproto.determinant = function () {
+ return this.a * this.d - this.b * this.c;
+ };
+ /*\
+ * Matrix.split
+ [ method ]
+ **
+ * Splits matrix into primitive transformations
+ = (object) in format:
+ o dx (number) translation by x
+ o dy (number) translation by y
+ o scalex (number) scale by x
+ o scaley (number) scale by y
+ o shear (number) shear
+ o rotate (number) rotation in deg
+ o isSimple (boolean) could it be represented via simple transformations
+ \*/
+ matrixproto.split = function () {
+ var out = {};
+ // translation
+ out.dx = this.e;
+ out.dy = this.f;
+
+ // scale and shear
+ var row = [[this.a, this.c], [this.b, this.d]];
+ out.scalex = math.sqrt(norm(row[0]));
+ normalize(row[0]);
+
+ out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
+ row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];
+
+ out.scaley = math.sqrt(norm(row[1]));
+ normalize(row[1]);
+ out.shear /= out.scaley;
+
+ if (this.determinant() < 0) {
+ out.scalex = -out.scalex;
+ }
+
+ // rotation
+ var sin = -row[0][1],
+ cos = row[1][1];
+ if (cos < 0) {
+ out.rotate = Snap.deg(math.acos(cos));
+ if (sin < 0) {
+ out.rotate = 360 - out.rotate;
+ }
+ } else {
+ out.rotate = Snap.deg(math.asin(sin));
+ }
+
+ out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
+ out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
+ out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
+ return out;
+ };
+ /*\
+ * Matrix.toTransformString
+ [ method ]
+ **
+ * Returns transform string that represents given matrix
+ = (string) transform string
+ \*/
+ matrixproto.toTransformString = function (shorter) {
+ var s = shorter || this.split();
+ if (!+s.shear.toFixed(9)) {
+ s.scalex = +s.scalex.toFixed(4);
+ s.scaley = +s.scaley.toFixed(4);
+ s.rotate = +s.rotate.toFixed(4);
+ return (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) +
+ (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E) +
+ (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E);
+ } else {
+ return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
+ }
+ };
+ })(Matrix.prototype);
+ /*\
+ * Snap.Matrix
+ [ method ]
+ **
+ * Matrix constructor, extend on your own risk.
+ * To create matrices use @Snap.matrix.
+ \*/
+ Snap.Matrix = Matrix;
+ /*\
+ * Snap.matrix
+ [ method ]
+ **
+ * Utility method
+ **
+ * Returns a matrix based on the given parameters
+ - a (number)
+ - b (number)
+ - c (number)
+ - d (number)
+ - e (number)
+ - f (number)
+ * or
+ - svgMatrix (SVGMatrix)
+ = (object) @Matrix
+ \*/
+ Snap.matrix = function (a, b, c, d, e, f) {
+ return new Matrix(a, b, c, d, e, f);
+ };
+});
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var has = "hasOwnProperty",
+ make = Snap._.make,
+ wrap = Snap._.wrap,
+ is = Snap.is,
+ getSomeDefs = Snap._.getSomeDefs,
+ reURLValue = /^url\(#?([^)]+)\)$/,
+ $ = Snap._.$,
+ URL = Snap.url,
+ Str = String,
+ separator = Snap._.separator,
+ E = "";
+ // Attributes event handlers
+ eve.on("snap.util.attr.mask", function (value) {
+ if (value instanceof Element || value instanceof Fragment) {
+ eve.stop();
+ if (value instanceof Fragment && value.node.childNodes.length == 1) {
+ value = value.node.firstChild;
+ getSomeDefs(this).appendChild(value);
+ value = wrap(value);
+ }
+ if (value.type == "mask") {
+ var mask = value;
+ } else {
+ mask = make("mask", getSomeDefs(this));
+ mask.node.appendChild(value.node);
+ }
+ !mask.node.id && $(mask.node, {
+ id: mask.id
+ });
+ $(this.node, {
+ mask: URL(mask.id)
+ });
}
- res.defs = defs;
- for (var key in proto) if (proto[has](key)) {
- res[key] = proto[key];
+ });
+ (function (clipIt) {
+ eve.on("snap.util.attr.clip", clipIt);
+ eve.on("snap.util.attr.clip-path", clipIt);
+ eve.on("snap.util.attr.clipPath", clipIt);
+ }(function (value) {
+ if (value instanceof Element || value instanceof Fragment) {
+ eve.stop();
+ if (value.type == "clipPath") {
+ var clip = value;
+ } else {
+ clip = make("clipPath", getSomeDefs(this));
+ clip.node.appendChild(value.node);
+ !clip.node.id && $(clip.node, {
+ id: clip.id
+ });
+ }
+ $(this.node, {
+ "clip-path": URL(clip.node.id || clip.id)
+ });
}
- res.paper = res.root = res;
- } else {
- res = make("svg", glob.doc.body);
- $(res.node, {
- height: h,
- version: 1.1,
- width: w,
- xmlns: xmlns
- });
- }
- return res;
-}
-function wrap(dom) {
- if (!dom) {
- return dom;
- }
- if (dom instanceof Element || dom instanceof Fragment) {
- return dom;
- }
- if (dom.tagName == "svg") {
- return new Paper(dom);
- }
- return new Element(dom);
-}
-// gradients' helpers
-function Gstops() {
- return this.selectAll("stop");
-}
-function GaddStop(color, offset) {
- var stop = $("stop"),
- attr = {
- offset: +offset + "%"
+ }));
+ function fillStroke(name) {
+ return function (value) {
+ eve.stop();
+ if (value instanceof Fragment && value.node.childNodes.length == 1 &&
+ (value.node.firstChild.tagName == "radialGradient" ||
+ value.node.firstChild.tagName == "linearGradient" ||
+ value.node.firstChild.tagName == "pattern")) {
+ value = value.node.firstChild;
+ getSomeDefs(this).appendChild(value);
+ value = wrap(value);
+ }
+ if (value instanceof Element) {
+ if (value.type == "radialGradient" || value.type == "linearGradient"
+ || value.type == "pattern") {
+ if (!value.node.id) {
+ $(value.node, {
+ id: value.id
+ });
+ }
+ var fill = URL(value.node.id);
+ } else {
+ fill = value.attr(name);
+ }
+ } else {
+ fill = Snap.color(value);
+ if (fill.error) {
+ var grad = Snap(getSomeDefs(this).ownerSVGElement).gradient(value);
+ if (grad) {
+ if (!grad.node.id) {
+ $(grad.node, {
+ id: grad.id
+ });
+ }
+ fill = URL(grad.node.id);
+ } else {
+ fill = value;
+ }
+ } else {
+ fill = Str(fill);
+ }
+ }
+ var attrs = {};
+ attrs[name] = fill;
+ $(this.node, attrs);
+ this.node.style[name] = E;
};
- color = Snap.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 Snap._.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 Snap._.box(cx - r, cy - r, r * 2, r * 2);
- }
-}
-function gradient(defs, str) {
- var grad = arrayFirstValue(eve("snap.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"
+ eve.on("snap.util.attr.fill", fillStroke("fill"));
+ eve.on("snap.util.attr.stroke", fillStroke("stroke"));
+ var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i;
+ eve.on("snap.util.grad.parse", function parseGrad(string) {
+ string = Str(string);
+ var tokens = string.match(gradrg);
+ if (!tokens) {
+ return null;
+ }
+ var type = tokens[1],
+ params = tokens[2],
+ stops = tokens[3];
+ params = params.split(/\s*,\s*/).map(function (el) {
+ return +el == el ? +el : el;
});
- }
- 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);
+ if (params.length == 1 && params[0] == 0) {
+ params = [];
}
- 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
+ stops = stops.split("-");
+ stops = stops.map(function (el) {
+ el = el.split(":");
+ var out = {
+ color: el[0]
+ };
+ if (el[1]) {
+ out.offset = parseFloat(el[1]);
+ }
+ return out;
});
- }
- 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
+ return {
+ type: type,
+ params: params,
+ stops: stops
+ };
+ });
+
+ eve.on("snap.util.attr.d", function (value) {
+ eve.stop();
+ if (is(value, "array") && is(value[0], "array")) {
+ value = Snap.path.toString.call(value);
+ }
+ value = Str(value);
+ if (value.match(/[ruo]/i)) {
+ value = Snap.path.toAbsolute(value);
+ }
+ $(this.node, {d: value});
+ })(-1);
+ eve.on("snap.util.attr.#text", function (value) {
+ eve.stop();
+ value = Str(value);
+ var txt = glob.doc.createTextNode(value);
+ while (this.node.firstChild) {
+ this.node.removeChild(this.node.firstChild);
+ }
+ this.node.appendChild(txt);
+ })(-1);
+ eve.on("snap.util.attr.path", function (value) {
+ eve.stop();
+ this.attr({d: value});
+ })(-1);
+ eve.on("snap.util.attr.class", function (value) {
+ eve.stop();
+ this.node.className.baseVal = value;
+ })(-1);
+ eve.on("snap.util.attr.viewBox", function (value) {
+ var vb;
+ if (is(value, "object") && "x" in value) {
+ vb = [value.x, value.y, value.width, value.height].join(" ");
+ } else if (is(value, "array")) {
+ vb = value.join(" ");
+ } else {
+ vb = value;
+ }
+ $(this.node, {
+ viewBox: vb
});
+ eve.stop();
+ })(-1);
+ eve.on("snap.util.attr.transform", function (value) {
+ this.transform(value);
+ eve.stop();
+ })(-1);
+ eve.on("snap.util.attr.r", function (value) {
+ if (this.type == "rect") {
+ eve.stop();
+ $(this.node, {
+ rx: value,
+ ry: value
+ });
+ }
+ })(-1);
+ eve.on("snap.util.attr.textpath", function (value) {
+ eve.stop();
+ if (this.type == "text") {
+ var id, tp, node;
+ if (!value && this.textPath) {
+ tp = this.textPath;
+ while (tp.node.firstChild) {
+ this.node.appendChild(tp.node.firstChild);
+ }
+ tp.remove();
+ delete this.textPath;
+ return;
+ }
+ if (is(value, "string")) {
+ var defs = getSomeDefs(this),
+ path = wrap(defs.parentNode).path(value);
+ defs.appendChild(path.node);
+ id = path.id;
+ path.attr({id: id});
+ } else {
+ value = wrap(value);
+ if (value instanceof Element) {
+ id = value.attr("id");
+ if (!id) {
+ id = value.id;
+ value.attr({id: id});
+ }
+ }
+ }
+ if (id) {
+ tp = this.textPath;
+ node = this.node;
+ if (tp) {
+ tp.attr({"xlink:href": "#" + id});
+ } else {
+ tp = $("textPath", {
+ "xlink:href": "#" + id
+ });
+ while (node.firstChild) {
+ tp.appendChild(node.firstChild);
+ }
+ node.appendChild(tp);
+ this.textPath = wrap(tp);
+ }
+ }
+ }
+ })(-1);
+ eve.on("snap.util.attr.text", function (value) {
+ if (this.type == "text") {
+ var i = 0,
+ node = this.node,
+ tuner = function (chunk) {
+ var out = $("tspan");
+ if (is(chunk, "array")) {
+ for (var i = 0; i < chunk.length; i++) {
+ out.appendChild(tuner(chunk[i]));
+ }
+ } else {
+ out.appendChild(glob.doc.createTextNode(chunk));
+ }
+ out.normalize && out.normalize();
+ return out;
+ };
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ var tuned = tuner(value);
+ while (tuned.firstChild) {
+ node.appendChild(tuned.firstChild);
+ }
+ }
+ eve.stop();
+ })(-1);
+ function setFontSize(value) {
+ eve.stop();
+ if (value == +value) {
+ value += "px";
+ }
+ this.node.style.fontSize = value;
}
- if (fx != null && fy != null) {
- $(el.node, {
- fx: fx,
- fy: fy
- });
+ eve.on("snap.util.attr.fontSize", setFontSize)(-1);
+ eve.on("snap.util.attr.font-size", setFontSize)(-1);
+
+
+ eve.on("snap.util.getattr.transform", function () {
+ eve.stop();
+ return this.transform();
+ })(-1);
+ eve.on("snap.util.getattr.textpath", function () {
+ eve.stop();
+ return this.textPath;
+ })(-1);
+ // Markers
+ (function () {
+ function getter(end) {
+ return function () {
+ eve.stop();
+ var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end);
+ if (style == "none") {
+ return style;
+ } else {
+ return Snap(glob.doc.getElementById(style.match(reURLValue)[1]));
+ }
+ };
+ }
+ function setter(end) {
+ return function (value) {
+ eve.stop();
+ var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1);
+ if (value == "" || !value) {
+ this.node.style[name] = "none";
+ return;
+ }
+ if (value.type == "marker") {
+ var id = value.node.id;
+ if (!id) {
+ $(value.node, {id: value.id});
+ }
+ this.node.style[name] = URL(id);
+ return;
+ }
+ };
+ }
+ eve.on("snap.util.getattr.marker-end", getter("end"))(-1);
+ eve.on("snap.util.getattr.markerEnd", getter("end"))(-1);
+ eve.on("snap.util.getattr.marker-start", getter("start"))(-1);
+ eve.on("snap.util.getattr.markerStart", getter("start"))(-1);
+ eve.on("snap.util.getattr.marker-mid", getter("mid"))(-1);
+ eve.on("snap.util.getattr.markerMid", getter("mid"))(-1);
+ eve.on("snap.util.attr.marker-end", setter("end"))(-1);
+ eve.on("snap.util.attr.markerEnd", setter("end"))(-1);
+ eve.on("snap.util.attr.marker-start", setter("start"))(-1);
+ eve.on("snap.util.attr.markerStart", setter("start"))(-1);
+ eve.on("snap.util.attr.marker-mid", setter("mid"))(-1);
+ eve.on("snap.util.attr.markerMid", setter("mid"))(-1);
+ }());
+ eve.on("snap.util.getattr.r", function () {
+ if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) {
+ eve.stop();
+ return $(this.node, "rx");
+ }
+ })(-1);
+ function textExtract(node) {
+ var out = [];
+ var children = node.childNodes;
+ for (var i = 0, ii = children.length; i < ii; i++) {
+ var chi = children[i];
+ if (chi.nodeType == 3) {
+ out.push(chi.nodeValue);
+ }
+ if (chi.tagName == "tspan") {
+ if (chi.childNodes.length == 1 && chi.firstChild.nodeType == 3) {
+ out.push(chi.firstChild.nodeValue);
+ } else {
+ out.push(textExtract(chi));
+ }
+ }
+ }
+ return out;
}
- return el;
-}
-// Paper prototype methods
-(function (proto) {
+ eve.on("snap.util.getattr.text", function () {
+ if (this.type == "text" || this.type == "tspan") {
+ eve.stop();
+ var out = textExtract(this.node);
+ return out.length == 1 ? out[0] : out;
+ }
+ })(-1);
+ eve.on("snap.util.getattr.#text", function () {
+ return this.node.textContent;
+ })(-1);
+ eve.on("snap.util.getattr.viewBox", function () {
+ eve.stop();
+ var vb = $(this.node, "viewBox");
+ if (vb) {
+ vb = vb.split(separator);
+ return Snap._.box(+vb[0], +vb[1], +vb[2], +vb[3]);
+ } else {
+ return;
+ }
+ })(-1);
+ eve.on("snap.util.getattr.points", function () {
+ var p = $(this.node, "points");
+ eve.stop();
+ if (p) {
+ return p.split(separator);
+ } else {
+ return;
+ }
+ })(-1);
+ eve.on("snap.util.getattr.path", function () {
+ var p = $(this.node, "d");
+ eve.stop();
+ return p;
+ })(-1);
+ eve.on("snap.util.getattr.class", function () {
+ return this.node.className.baseVal;
+ })(-1);
+ function getFontSize() {
+ eve.stop();
+ return this.node.style.fontSize;
+ }
+ eve.on("snap.util.getattr.fontSize", getFontSize)(-1);
+ eve.on("snap.util.getattr.font-size", getFontSize)(-1);
+});
+
+// Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var rgNotSpace = /\S+/g,
+ rgBadSpace = /[\t\r\n\f]/g,
+ rgTrim = /(^\s+|\s+$)/g,
+ Str = String,
+ elproto = Element.prototype;
/*\
- * Paper.el
+ * Element.addClass
[ method ]
**
- * Creates an element on paper with a given name and no attributes
+ * Adds given class name or list of class names to the element.
+ - value (string) class name or space separated list of class names
**
- - name (string) tag name
- - attr (object) attributes
- = (Element) the current element
- > Usage
- | var c = paper.circle(10, 10, 10); // is the same as...
- | var c = paper.el("circle").attr({
- | cx: 10,
- | cy: 10,
- | r: 10
- | });
- | // and the same as
- | var c = paper.el("circle", {
- | cx: 10,
- | cy: 10,
- | r: 10
- | });
+ = (Element) original element.
\*/
- proto.el = function (name, attr) {
- return make(name, this.node).attr(attr);
+ elproto.addClass = function (value) {
+ var classes = Str(value || "").match(rgNotSpace) || [],
+ elem = this.node,
+ className = elem.className.baseVal,
+ curClasses = className.match(rgNotSpace) || [],
+ j,
+ pos,
+ clazz,
+ finalValue;
+
+ if (classes.length) {
+ j = 0;
+ while ((clazz = classes[j++])) {
+ pos = curClasses.indexOf(clazz);
+ if (!~pos) {
+ curClasses.push(clazz);
+ }
+ }
+
+ finalValue = curClasses.join(" ");
+ if (className != finalValue) {
+ elem.className.baseVal = finalValue;
+ }
+ }
+ return this;
};
/*\
+ * Element.removeClass
+ [ method ]
+ **
+ * Removes given class name or list of class names from the element.
+ - value (string) class name or space separated list of class names
+ **
+ = (Element) original element.
+ \*/
+ elproto.removeClass = function (value) {
+ var classes = Str(value || "").match(rgNotSpace) || [],
+ elem = this.node,
+ className = elem.className.baseVal,
+ curClasses = className.match(rgNotSpace) || [],
+ j,
+ pos,
+ clazz,
+ finalValue;
+ if (curClasses.length) {
+ j = 0;
+ while ((clazz = classes[j++])) {
+ pos = curClasses.indexOf(clazz);
+ if (~pos) {
+ curClasses.splice(pos, 1);
+ }
+ }
+
+ finalValue = curClasses.join(" ");
+ if (className != finalValue) {
+ elem.className.baseVal = finalValue;
+ }
+ }
+ return this;
+ };
+ /*\
+ * Element.hasClass
+ [ method ]
+ **
+ * Checks if the element has a given class name in the list of class names applied to it.
+ - value (string) class name
+ **
+ = (boolean) `true` if the element has given class
+ \*/
+ elproto.hasClass = function (value) {
+ var elem = this.node,
+ className = elem.className.baseVal,
+ curClasses = className.match(rgNotSpace) || [];
+ return !!~curClasses.indexOf(value);
+ };
+ /*\
+ * Element.toggleClass
+ [ method ]
+ **
+ * Add or remove one or more classes from the element, depending on either
+ * the class’s presence or the value of the `flag` argument.
+ - value (string) class name or space separated list of class names
+ - flag (boolean) value to determine whether the class should be added or removed
+ **
+ = (Element) original element.
+ \*/
+ elproto.toggleClass = function (value, flag) {
+ if (flag != null) {
+ if (flag) {
+ return this.addClass(value);
+ } else {
+ return this.removeClass(value);
+ }
+ }
+ var classes = (value || "").match(rgNotSpace) || [],
+ elem = this.node,
+ className = elem.className.baseVal,
+ curClasses = className.match(rgNotSpace) || [],
+ j,
+ pos,
+ clazz,
+ finalValue;
+ j = 0;
+ while ((clazz = classes[j++])) {
+ pos = curClasses.indexOf(clazz);
+ if (~pos) {
+ curClasses.splice(pos, 1);
+ } else {
+ curClasses.push(clazz);
+ }
+ }
+
+ finalValue = curClasses.join(" ");
+ if (className != finalValue) {
+ elem.className.baseVal = finalValue;
+ }
+ return this;
+ };
+});
+
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var operators = {
+ "+": function (x, y) {
+ return x + y;
+ },
+ "-": function (x, y) {
+ return x - y;
+ },
+ "/": function (x, y) {
+ return x / y;
+ },
+ "*": function (x, y) {
+ return x * y;
+ }
+ },
+ Str = String,
+ reUnit = /[a-z]+$/i,
+ reAddon = /^\s*([+\-\/*])\s*=\s*([\d.eE+\-]+)\s*([^\d\s]+)?\s*$/;
+ function getNumber(val) {
+ return val;
+ }
+ function getUnit(unit) {
+ return function (val) {
+ return +val.toFixed(3) + unit;
+ };
+ }
+ eve.on("snap.util.attr", function (val) {
+ var plus = Str(val).match(reAddon);
+ if (plus) {
+ var evnt = eve.nt(),
+ name = evnt.substring(evnt.lastIndexOf(".") + 1),
+ a = this.attr(name),
+ atr = {};
+ eve.stop();
+ var unit = plus[3] || "",
+ aUnit = a.match(reUnit),
+ op = operators[plus[1]];
+ if (aUnit && aUnit == unit) {
+ val = op(parseFloat(a), +plus[2]);
+ } else {
+ a = this.asPX(name);
+ val = op(this.asPX(name), this.asPX(name, plus[2] + unit));
+ }
+ if (isNaN(a) || isNaN(val)) {
+ return;
+ }
+ atr[name] = val;
+ this.attr(atr);
+ }
+ })(-10);
+ eve.on("snap.util.equal", function (name, b) {
+ var A, B, a = Str(this.attr(name) || ""),
+ el = this,
+ bplus = Str(b).match(reAddon);
+ if (bplus) {
+ eve.stop();
+ var unit = bplus[3] || "",
+ aUnit = a.match(reUnit),
+ op = operators[bplus[1]];
+ if (aUnit && aUnit == unit) {
+ return {
+ from: parseFloat(a),
+ to: op(parseFloat(a), +bplus[2]),
+ f: getUnit(aUnit)
+ };
+ } else {
+ a = this.asPX(name);
+ return {
+ from: a,
+ to: op(a, this.asPX(name, bplus[2] + unit)),
+ f: getNumber
+ };
+ }
+ }
+ })(-10);
+});
+// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var proto = Paper.prototype,
+ is = Snap.is;
+ /*\
* Paper.rect
[ method ]
*
* Draws a rectangle
**
@@ -3408,11 +4622,11 @@
proto.rect = function (x, y, w, h, rx, ry) {
var attr;
if (ry == null) {
ry = rx;
}
- if (is(x, "object") && "x" in x) {
+ if (is(x, "object") && x == "[object Object]") {
attr = x;
} else if (x != null) {
attr = {
x: x,
y: y,
@@ -3440,11 +4654,11 @@
> Usage
| var c = paper.circle(50, 50, 40);
\*/
proto.circle = function (cx, cy, r) {
var attr;
- if (is(cx, "object") && "cx" in cx) {
+ if (is(cx, "object") && cx == "[object Object]") {
attr = cx;
} else if (cx != null) {
attr = {
cx: cx,
cy: cy,
@@ -3452,10 +4666,29 @@
};
}
return this.el("circle", attr);
};
+ var preload = (function () {
+ function onerror() {
+ this.parentNode.removeChild(this);
+ }
+ return function (src, f) {
+ var img = glob.doc.createElement("img"),
+ body = glob.doc.body;
+ img.style.cssText = "position:absolute;left:-9999em;top:-9999em";
+ img.onload = function () {
+ f.call(img);
+ img.onload = img.onerror = null;
+ body.removeChild(img);
+ };
+ img.onerror = onerror;
+ body.appendChild(img);
+ img.src = src;
+ };
+ }());
+
/*\
* Paper.image
[ method ]
**
* Places an image on the surface
@@ -3471,11 +4704,11 @@
**
> Usage
| var c = paper.image("apple.png", 10, 10, 80, 80);
\*/
proto.image = function (src, x, y, width, height) {
- var el = make("image", this.node);
+ var el = this.el("image");
if (is(src, "object") && "src" in src) {
el.attr(src);
} else if (src != null) {
var set = {
"xlink:href": src,
@@ -3488,17 +4721,17 @@
if (width != null && height != null) {
set.width = width;
set.height = height;
} else {
preload(src, function () {
- $(el.node, {
+ Snap._.$(el.node, {
width: this.offsetWidth,
height: this.offsetHeight
});
});
}
- $(el.node, set);
+ Snap._.$(el.node, set);
}
return el;
};
/*\
* Paper.ellipse
@@ -3514,22 +4747,22 @@
**
> Usage
| var c = paper.ellipse(50, 50, 40, 20);
\*/
proto.ellipse = function (cx, cy, rx, ry) {
- var el = make("ellipse", this.node);
- if (is(cx, "object") && "cx" in cx) {
- el.attr(cx);
+ var attr;
+ if (is(cx, "object") && cx == "[object Object]") {
+ attr = cx;
} else if (cx != null) {
- el.attr({
+ attr ={
cx: cx,
cy: cy,
rx: rx,
ry: ry
- });
+ };
}
- return el;
+ return this.el("ellipse", attr);
};
// SIERRA Paper.path(): Unclear from the link what a Catmull-Rom curveto is, and why it would make life any easier.
/*\
* Paper.path
[ method ]
@@ -3559,21 +4792,18 @@
| var c = paper.path("M10 10L90 90");
| // draw a diagonal line:
| // move to 10,10, line to 90,90
\*/
proto.path = function (d) {
- var el = make("path", this.node);
+ var attr;
if (is(d, "object") && !is(d, "array")) {
- el.attr(d);
+ attr = d;
} else if (d) {
- el.attr({
- d: d
- });
+ attr = {d: d};
}
- return el;
+ return this.el("path", attr);
};
-// SIERRA Paper.g(): Don't understand the code comment about the order being _different._ Wouldn't it be a rect followed by a circle?
/*\
* Paper.g
[ method ]
**
* Creates a group element
@@ -3596,23 +4826,169 @@
[ method ]
**
* See @Paper.g
\*/
proto.group = proto.g = function (first) {
- var el = make("g", this.node);
- el.add = add2group;
- for (var method in proto) if (proto[has](method)) {
- el[method] = proto[method];
+ var attr,
+ el = this.el("g");
+ if (arguments.length == 1 && first && !first.type) {
+ el.attr(first);
+ } else if (arguments.length) {
+ el.add(Array.prototype.slice.call(arguments, 0));
}
+ return el;
+ };
+ /*\
+ * Paper.svg
+ [ method ]
+ **
+ * Creates a nested SVG element.
+ - x (number) @optional X of the element
+ - y (number) @optional Y of the element
+ - width (number) @optional width of the element
+ - height (number) @optional height of the element
+ - vbx (number) @optional viewbox X
+ - vby (number) @optional viewbox Y
+ - vbw (number) @optional viewbox width
+ - vbh (number) @optional viewbox height
+ **
+ = (object) the `svg` element
+ **
+ \*/
+ proto.svg = function (x, y, width, height, vbx, vby, vbw, vbh) {
+ var attrs = {};
+ if (is(x, "object") && y == null) {
+ attrs = x;
+ } else {
+ if (x != null) {
+ attrs.x = x;
+ }
+ if (y != null) {
+ attrs.y = y;
+ }
+ if (width != null) {
+ attrs.width = width;
+ }
+ if (height != null) {
+ attrs.height = height;
+ }
+ if (vbx != null && vby != null && vbw != null && vbh != null) {
+ attrs.viewBox = [vbx, vby, vbw, vbh];
+ }
+ }
+ return this.el("svg", attrs);
+ };
+ /*\
+ * Paper.mask
+ [ method ]
+ **
+ * Equivalent in behaviour to @Paper.g, except it’s a mask.
+ **
+ = (object) the `mask` element
+ **
+ \*/
+ proto.mask = function (first) {
+ var attr,
+ el = this.el("mask");
if (arguments.length == 1 && first && !first.type) {
el.attr(first);
} else if (arguments.length) {
el.add(Array.prototype.slice.call(arguments, 0));
}
return el;
};
/*\
+ * Paper.ptrn
+ [ method ]
+ **
+ * Equivalent in behaviour to @Paper.g, except it’s a pattern.
+ - x (number) @optional X of the element
+ - y (number) @optional Y of the element
+ - width (number) @optional width of the element
+ - height (number) @optional height of the element
+ - vbx (number) @optional viewbox X
+ - vby (number) @optional viewbox Y
+ - vbw (number) @optional viewbox width
+ - vbh (number) @optional viewbox height
+ **
+ = (object) the `pattern` element
+ **
+ \*/
+ proto.ptrn = function (x, y, width, height, vx, vy, vw, vh) {
+ if (is(x, "object")) {
+ var attr = x;
+ } else {
+ attr = {patternUnits: "userSpaceOnUse"};
+ if (x) {
+ attr.x = x;
+ }
+ if (y) {
+ attr.y = y;
+ }
+ if (width != null) {
+ attr.width = width;
+ }
+ if (height != null) {
+ attr.height = height;
+ }
+ if (vx != null && vy != null && vw != null && vh != null) {
+ attr.viewBox = [vx, vy, vw, vh];
+ } else {
+ attr.viewBox = [x || 0, y || 0, width || 0, height || 0];
+ }
+ }
+ return this.el("pattern", attr);
+ };
+ /*\
+ * Paper.use
+ [ method ]
+ **
+ * Creates a <use> element.
+ - id (string) @optional id of element to link
+ * or
+ - id (Element) @optional element to link
+ **
+ = (object) the `use` element
+ **
+ \*/
+ proto.use = function (id) {
+ if (id != null) {
+ if (id instanceof Element) {
+ if (!id.attr("id")) {
+ id.attr({id: Snap._.id(id)});
+ }
+ id = id.attr("id");
+ }
+ if (String(id).charAt() == "#") {
+ id = id.substring(1);
+ }
+ return this.el("use", {"xlink:href": "#" + id});
+ } else {
+ return Element.prototype.use.call(this);
+ }
+ };
+ /*\
+ * Paper.symbol
+ [ method ]
+ **
+ * Creates a <symbol> element.
+ - vbx (number) @optional viewbox X
+ - vby (number) @optional viewbox Y
+ - vbw (number) @optional viewbox width
+ - vbh (number) @optional viewbox height
+ = (object) the `symbol` element
+ **
+ \*/
+ proto.symbol = function (vx, vy, vw, vh) {
+ var attr = {};
+ if (vx != null && vy != null && vw != null && vh != null) {
+ attr.viewBox = [vx, vy, vw, vh];
+ }
+
+ return this.el("symbol", attr);
+ };
+ /*\
* Paper.text
[ method ]
**
* Draws a text string
**
@@ -3629,21 +5005,21 @@
| // or
| var pth = paper.path("M10,10L100,100");
| t1.attr({textpath: pth});
\*/
proto.text = function (x, y, text) {
- var el = make("text", this.node);
+ var attr = {};
if (is(x, "object")) {
- el.attr(x);
+ attr = x;
} else if (x != null) {
- el.attr({
+ attr = {
x: x,
y: y,
text: text || ""
- });
+ };
}
- return el;
+ return this.el("text", attr);
};
/*\
* Paper.line
[ method ]
**
@@ -3657,22 +5033,22 @@
**
> Usage
| var t1 = paper.line(50, 50, 100, 100);
\*/
proto.line = function (x1, y1, x2, y2) {
- var el = make("line", this.node);
+ var attr = {};
if (is(x1, "object")) {
- el.attr(x1);
+ attr = x1;
} else if (x1 != null) {
- el.attr({
+ attr = {
x1: x1,
x2: x2,
y1: y1,
y2: y2
- });
+ };
}
- return el;
+ return this.el("line", attr);
};
/*\
* Paper.polyline
[ method ]
**
@@ -3689,19 +5065,17 @@
\*/
proto.polyline = function (points) {
if (arguments.length > 1) {
points = Array.prototype.slice.call(arguments, 0);
}
- var el = make("polyline", this.node);
+ var attr = {};
if (is(points, "object") && !is(points, "array")) {
- el.attr(points);
+ attr = points;
} else if (points != null) {
- el.attr({
- points: points
- });
+ attr = {points: points};
}
- return el;
+ return this.el("polyline", attr);
};
/*\
* Paper.polygon
[ method ]
**
@@ -3709,22 +5083,129 @@
\*/
proto.polygon = function (points) {
if (arguments.length > 1) {
points = Array.prototype.slice.call(arguments, 0);
}
- var el = make("polygon", this.node);
+ var attr = {};
if (is(points, "object") && !is(points, "array")) {
- el.attr(points);
+ attr = points;
} else if (points != null) {
- el.attr({
- points: points
- });
+ attr = {points: points};
}
- return el;
+ return this.el("polygon", attr);
};
// gradients
(function () {
+ var $ = Snap._.$;
+ // gradients' helpers
+ function Gstops() {
+ return this.selectAll("stop");
+ }
+ function GaddStop(color, offset) {
+ var stop = $("stop"),
+ attr = {
+ offset: +offset + "%"
+ };
+ color = Snap.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 Snap._.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 Snap._.box(cx - r, cy - r, r * 2, r * 2);
+ }
+ }
+ function gradient(defs, str) {
+ var grad = eve("snap.util.grad.parse", null, str).firstDefined(),
+ 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 = Snap._.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 = Snap._.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.gradient
[ method ]
**
* Creates a gradient element
@@ -3748,11 +5229,11 @@
* Linear gradient, relative from top-left corner to bottom-right
* corner, from black through red to white:
| var g = paper.gradient("l(0, 0, 1, 1)#000-#f00-#fff");
* Linear gradient, absolute from (0, 0) to (100, 100), from black
* through red at 25% to white:
- | var g = paper.gradient("L(0, 0, 100, 100)#000-#f00:25%-#fff");
+ | var g = paper.gradient("L(0, 0, 100, 100)#000-#f00:25-#fff");
* Radial gradient, relative from the center of the element with radius
* half the width, from black to white:
| var g = paper.gradient("r(0.5, 0.5, 0.5)#000-#fff");
* To apply the gradient:
| paper.circle(50, 50, 40).attr({
@@ -3775,22 +5256,35 @@
**
* Returns SVG code for the @Paper
= (string) SVG code for the @Paper
\*/
proto.toString = function () {
- var f = glob.doc.createDocumentFragment(),
- d = glob.doc.createElement("div"),
+ var doc = this.node.ownerDocument,
+ f = doc.createDocumentFragment(),
+ d = doc.createElement("div"),
svg = this.node.cloneNode(true),
res;
f.appendChild(d);
d.appendChild(svg);
- $(svg, {xmlns: xmlns});
+ Snap._.$(svg, {xmlns: "http://www.w3.org/2000/svg"});
res = d.innerHTML;
f.removeChild(f.firstChild);
return res;
};
/*\
+ * Paper.toDataURL
+ [ method ]
+ **
+ * Returns SVG code for the @Paper as Data URI string.
+ = (string) Data URI string
+ \*/
+ proto.toDataURL = function () {
+ if (window && window.btoa) {
+ return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(this)));
+ }
+ };
+ /*\
* Paper.clear
[ method ]
**
* Removes all child nodes of the paper, except <defs>.
\*/
@@ -3799,601 +5293,19 @@
next;
while (node) {
next = node.nextSibling;
if (node.tagName != "defs") {
node.parentNode.removeChild(node);
+ } else {
+ proto.clear.call({node: node});
}
node = next;
}
};
}());
-}(Paper.prototype));
-
-// simple ajax
-/*\
- * Snap.ajax
- [ method ]
- **
- * Simple implementation of Ajax
- **
- - url (string) URL
- - postData (object|string) data for post request
- - callback (function) callback
- - scope (object) #optional scope of callback
- * or
- - url (string) URL
- - callback (function) callback
- - scope (object) #optional scope of callback
- = (XMLHttpRequest) the XMLHttpRequest object, just in case
-\*/
-Snap.ajax = function (url, postData, callback, scope){
- var req = new XMLHttpRequest,
- id = ID();
- if (req) {
- if (is(postData, "function")) {
- scope = callback;
- callback = postData;
- postData = null;
- } else if (is(postData, "object")) {
- var pd = [];
- for (var key in postData) if (postData.hasOwnProperty(key)) {
- pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key]));
- }
- postData = pd.join("&");
- }
- req.open((postData ? "POST" : "GET"), url, true);
- req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
- if (postData) {
- req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
- }
- if (callback) {
- eve.once("snap.ajax." + id + ".0", callback);
- eve.once("snap.ajax." + id + ".200", callback);
- eve.once("snap.ajax." + id + ".304", callback);
- }
- req.onreadystatechange = function() {
- if (req.readyState != 4) return;
- eve("snap.ajax." + id + "." + req.status, scope, req);
- };
- if (req.readyState == 4) {
- return req;
- }
- req.send(postData);
- return req;
- }
-};
-/*\
- * Snap.load
- [ method ]
- **
- * Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX)
- **
- - url (string) URL
- - callback (function) callback
- - scope (object) #optional scope of callback
-\*/
-Snap.load = function (url, callback, scope) {
- Snap.ajax(url, function (req) {
- var f = Snap.parse(req.responseText);
- scope ? callback.call(scope, f) : callback(f);
- });
-};
-
-// Attributes event handlers
-eve.on("snap.util.attr.mask", function (value) {
- if (value instanceof Element || value instanceof Fragment) {
- eve.stop();
- if (value instanceof Fragment && value.node.childNodes.length == 1) {
- value = value.node.firstChild;
- getSomeDefs(this).appendChild(value);
- value = wrap(value);
- }
- if (value.type == "mask") {
- var mask = value;
- } else {
- mask = make("mask", getSomeDefs(this));
- mask.node.appendChild(value.node);
- !mask.node.id && $(mask.node, {
- id: mask.id
- });
- }
- $(this.node, {
- mask: URL(mask.id)
- });
- }
});
-(function (clipIt) {
- eve.on("snap.util.attr.clip", clipIt);
- eve.on("snap.util.attr.clip-path", clipIt);
- eve.on("snap.util.attr.clipPath", clipIt);
-}(function (value) {
- if (value instanceof Element || value instanceof Fragment) {
- eve.stop();
- if (value.type == "clipPath") {
- var clip = value;
- } else {
- clip = make("clipPath", getSomeDefs(this));
- clip.node.appendChild(value.node);
- !clip.node.id && $(clip.node, {
- id: clip.id
- });
- }
- $(this.node, {
- "clip-path": URL(clip.id)
- });
- }
-}));
-function fillStroke(name) {
- return function (value) {
- eve.stop();
- if (value instanceof Fragment && value.node.childNodes.length == 1 &&
- (value.node.firstChild.tagName == "radialGradient" ||
- value.node.firstChild.tagName == "linearGradient" ||
- value.node.firstChild.tagName == "pattern")) {
- value = value.node.firstChild;
- getSomeDefs(this).appendChild(value);
- value = wrap(value);
- }
- if (value instanceof Element) {
- if (value.type == "radialGradient" || value.type == "linearGradient"
- || value.type == "pattern") {
- if (!value.node.id) {
- $(value.node, {
- id: value.id
- });
- }
- var fill = URL(value.node.id);
- } else {
- fill = value.attr(name);
- }
- } else {
- fill = Snap.color(value);
- if (fill.error) {
- var grad = gradient(getSomeDefs(this), value);
- if (grad) {
- if (!grad.node.id) {
- $(grad.node, {
- id: grad.id
- });
- }
- fill = URL(grad.node.id);
- } else {
- fill = value;
- }
- } else {
- fill = Str(fill);
- }
- }
- var attrs = {};
- attrs[name] = fill;
- $(this.node, attrs);
- this.node.style[name] = E;
- };
-}
-eve.on("snap.util.attr.fill", fillStroke("fill"));
-eve.on("snap.util.attr.stroke", fillStroke("stroke"));
-var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i;
-eve.on("snap.util.grad.parse", function parseGrad(string) {
- string = Str(string);
- var tokens = string.match(gradrg);
- if (!tokens) {
- return null;
- }
- var type = tokens[1],
- params = tokens[2],
- stops = tokens[3];
- params = params.split(/\s*,\s*/).map(function (el) {
- return +el == el ? +el : el;
- });
- if (params.length == 1 && params[0] == 0) {
- params = [];
- }
- stops = stops.split("-");
- stops = stops.map(function (el) {
- el = el.split(":");
- var out = {
- color: el[0]
- };
- if (el[1]) {
- out.offset = el[1];
- }
- return out;
- });
- return {
- type: type,
- params: params,
- stops: stops
- };
-});
-eve.on("snap.util.attr.d", function (value) {
- eve.stop();
- if (is(value, "array") && is(value[0], "array")) {
- value = Snap.path.toString.call(value);
- }
- value = Str(value);
- if (value.match(/[ruo]/i)) {
- value = Snap.path.toAbsolute(value);
- }
- $(this.node, {d: value});
-})(-1);
-eve.on("snap.util.attr.#text", function (value) {
- eve.stop();
- value = Str(value);
- var txt = glob.doc.createTextNode(value);
- while (this.node.firstChild) {
- this.node.removeChild(this.node.firstChild);
- }
- this.node.appendChild(txt);
-})(-1);
-eve.on("snap.util.attr.path", function (value) {
- eve.stop();
- this.attr({d: value});
-})(-1);
-eve.on("snap.util.attr.viewBox", function (value) {
- var vb;
- if (is(value, "object") && "x" in value) {
- vb = [value.x, value.y, value.width, value.height].join(" ");
- } else if (is(value, "array")) {
- vb = value.join(" ");
- } else {
- vb = value;
- }
- $(this.node, {
- viewBox: vb
- });
- eve.stop();
-})(-1);
-eve.on("snap.util.attr.transform", function (value) {
- this.transform(value);
- eve.stop();
-})(-1);
-eve.on("snap.util.attr.r", function (value) {
- if (this.type == "rect") {
- eve.stop();
- $(this.node, {
- rx: value,
- ry: value
- });
- }
-})(-1);
-eve.on("snap.util.attr.textpath", function (value) {
- eve.stop();
- if (this.type == "text") {
- var id, tp, node;
- if (!value && this.textPath) {
- tp = this.textPath;
- while (tp.node.firstChild) {
- this.node.appendChild(tp.node.firstChild);
- }
- tp.remove();
- delete this.textPath;
- return;
- }
- if (is(value, "string")) {
- var defs = getSomeDefs(this),
- path = wrap(defs.parentNode).path(value);
- defs.appendChild(path.node);
- id = path.id;
- path.attr({id: id});
- } else {
- value = wrap(value);
- if (value instanceof Element) {
- id = value.attr("id");
- if (!id) {
- id = value.id;
- value.attr({id: id});
- }
- }
- }
- if (id) {
- tp = this.textPath;
- node = this.node;
- if (tp) {
- tp.attr({"xlink:href": "#" + id});
- } else {
- tp = $("textPath", {
- "xlink:href": "#" + id
- });
- while (node.firstChild) {
- tp.appendChild(node.firstChild);
- }
- node.appendChild(tp);
- this.textPath = wrap(tp);
- }
- }
- }
-})(-1);
-eve.on("snap.util.attr.text", function (value) {
- if (this.type == "text") {
- var i = 0,
- node = this.node,
- tuner = function (chunk) {
- var out = $("tspan");
- if (is(chunk, "array")) {
- for (var i = 0; i < chunk.length; i++) {
- out.appendChild(tuner(chunk[i]));
- }
- } else {
- out.appendChild(glob.doc.createTextNode(chunk));
- }
- out.normalize && out.normalize();
- return out;
- };
- while (node.firstChild) {
- node.removeChild(node.firstChild);
- }
- var tuned = tuner(value);
- while (tuned.firstChild) {
- node.appendChild(tuned.firstChild);
- }
- }
- eve.stop();
-})(-1);
-// default
-var cssAttr = {
- "alignment-baseline": 0,
- "baseline-shift": 0,
- "clip": 0,
- "clip-path": 0,
- "clip-rule": 0,
- "color": 0,
- "color-interpolation": 0,
- "color-interpolation-filters": 0,
- "color-profile": 0,
- "color-rendering": 0,
- "cursor": 0,
- "direction": 0,
- "display": 0,
- "dominant-baseline": 0,
- "enable-background": 0,
- "fill": 0,
- "fill-opacity": 0,
- "fill-rule": 0,
- "filter": 0,
- "flood-color": 0,
- "flood-opacity": 0,
- "font": 0,
- "font-family": 0,
- "font-size": 0,
- "font-size-adjust": 0,
- "font-stretch": 0,
- "font-style": 0,
- "font-variant": 0,
- "font-weight": 0,
- "glyph-orientation-horizontal": 0,
- "glyph-orientation-vertical": 0,
- "image-rendering": 0,
- "kerning": 0,
- "letter-spacing": 0,
- "lighting-color": 0,
- "marker": 0,
- "marker-end": 0,
- "marker-mid": 0,
- "marker-start": 0,
- "mask": 0,
- "opacity": 0,
- "overflow": 0,
- "pointer-events": 0,
- "shape-rendering": 0,
- "stop-color": 0,
- "stop-opacity": 0,
- "stroke": 0,
- "stroke-dasharray": 0,
- "stroke-dashoffset": 0,
- "stroke-linecap": 0,
- "stroke-linejoin": 0,
- "stroke-miterlimit": 0,
- "stroke-opacity": 0,
- "stroke-width": 0,
- "text-anchor": 0,
- "text-decoration": 0,
- "text-rendering": 0,
- "unicode-bidi": 0,
- "visibility": 0,
- "word-spacing": 0,
- "writing-mode": 0
-};
-
-eve.on("snap.util.attr", function (value) {
- var att = eve.nt(),
- attr = {};
- att = att.substring(att.lastIndexOf(".") + 1);
- attr[att] = value;
- var style = att.replace(/-(\w)/gi, function (all, letter) {
- return letter.toUpperCase();
- }),
- css = att.replace(/[A-Z]/g, function (letter) {
- return "-" + letter.toLowerCase();
- });
- if (cssAttr[has](css)) {
- this.node.style[style] = value == null ? E : value;
- } else {
- $(this.node, attr);
- }
-});
-eve.on("snap.util.getattr.transform", function () {
- eve.stop();
- return this.transform();
-})(-1);
-eve.on("snap.util.getattr.textpath", function () {
- eve.stop();
- return this.textPath;
-})(-1);
-// Markers
-(function () {
- function getter(end) {
- return function () {
- eve.stop();
- var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end);
- if (style == "none") {
- return style;
- } else {
- return Snap(glob.doc.getElementById(style.match(reURLValue)[1]));
- }
- };
- }
- function setter(end) {
- return function (value) {
- eve.stop();
- var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1);
- if (value == "" || !value) {
- this.node.style[name] = "none";
- return;
- }
- if (value.type == "marker") {
- var id = value.node.id;
- if (!id) {
- $(value.node, {id: value.id});
- }
- this.node.style[name] = URL(id);
- return;
- }
- };
- }
- eve.on("snap.util.getattr.marker-end", getter("end"))(-1);
- eve.on("snap.util.getattr.markerEnd", getter("end"))(-1);
- eve.on("snap.util.getattr.marker-start", getter("start"))(-1);
- eve.on("snap.util.getattr.markerStart", getter("start"))(-1);
- eve.on("snap.util.getattr.marker-mid", getter("mid"))(-1);
- eve.on("snap.util.getattr.markerMid", getter("mid"))(-1);
- eve.on("snap.util.attr.marker-end", setter("end"))(-1);
- eve.on("snap.util.attr.markerEnd", setter("end"))(-1);
- eve.on("snap.util.attr.marker-start", setter("start"))(-1);
- eve.on("snap.util.attr.markerStart", setter("start"))(-1);
- eve.on("snap.util.attr.marker-mid", setter("mid"))(-1);
- eve.on("snap.util.attr.markerMid", setter("mid"))(-1);
-}());
-eve.on("snap.util.getattr.r", function () {
- if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) {
- eve.stop();
- return $(this.node, "rx");
- }
-})(-1);
-function textExtract(node) {
- var out = [];
- var children = node.childNodes;
- for (var i = 0, ii = children.length; i < ii; i++) {
- var chi = children[i];
- if (chi.nodeType == 3) {
- out.push(chi.nodeValue);
- }
- if (chi.tagName == "tspan") {
- if (chi.childNodes.length == 1 && chi.firstChild.nodeType == 3) {
- out.push(chi.firstChild.nodeValue);
- } else {
- out.push(textExtract(chi));
- }
- }
- }
- return out;
-}
-eve.on("snap.util.getattr.text", function () {
- if (this.type == "text" || this.type == "tspan") {
- eve.stop();
- var out = textExtract(this.node);
- return out.length == 1 ? out[0] : out;
- }
-})(-1);
-eve.on("snap.util.getattr.#text", function () {
- return this.node.textContent;
-})(-1);
-eve.on("snap.util.getattr.viewBox", function () {
- eve.stop();
- var vb = $(this.node, "viewBox").split(separator);
- return Snap._.box(+vb[0], +vb[1], +vb[2], +vb[3]);
- // TODO: investigate why I need to z-index it
-})(-1);
-eve.on("snap.util.getattr.points", function () {
- var p = $(this.node, "points");
- eve.stop();
- return p.split(separator);
-});
-eve.on("snap.util.getattr.path", function () {
- var p = $(this.node, "d");
- eve.stop();
- return p;
-});
-// default
-eve.on("snap.util.getattr", function () {
- var att = eve.nt();
- att = att.substring(att.lastIndexOf(".") + 1);
- var css = att.replace(/[A-Z]/g, function (letter) {
- return "-" + letter.toLowerCase();
- });
- if (cssAttr[has](css)) {
- return glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue(css);
- } else {
- return $(this.node, att);
- }
-});
-var getOffset = function (elem) {
- var box = elem.getBoundingClientRect(),
- doc = elem.ownerDocument,
- body = doc.body,
- docElem = doc.documentElement,
- clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
- top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop ) - clientTop,
- left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
- return {
- y: top,
- x: left
- };
-};
-/*\
- * Snap.getElementByPoint
- [ method ]
- **
- * Returns you topmost element under given point.
- **
- = (object) Snap element object
- - x (number) x coordinate from the top left corner of the window
- - y (number) y coordinate from the top left corner of the window
- > Usage
- | Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
-\*/
-Snap.getElementByPoint = function (x, y) {
- var paper = this,
- svg = paper.canvas,
- target = glob.doc.elementFromPoint(x, y);
- if (glob.win.opera && target.tagName == "svg") {
- var so = getOffset(target),
- sr = target.createSVGRect();
- sr.x = x - so.x;
- sr.y = y - so.y;
- sr.width = sr.height = 1;
- var hits = target.getIntersectionList(sr, null);
- if (hits.length) {
- target = hits[hits.length - 1];
- }
- }
- if (!target) {
- return null;
- }
- return wrap(target);
-};
-/*\
- * Snap.plugin
- [ method ]
- **
- * Let you write plugins. You pass in a function with four arguments, like this:
- | Snap.plugin(function (Snap, Element, Paper, global) {
- | Snap.newmethod = function () {};
- | Element.prototype.newmethod = function () {};
- | Paper.prototype.newmethod = function () {};
- | });
- * Inside the function you have access to all main objects (and their
- * prototypes). This allow you to extend anything you want.
- **
- - f (function) your plugin body
-\*/
-Snap.plugin = function (f) {
- f(Snap, Element, Paper, glob);
-};
-glob.win.Snap = Snap;
-return Snap;
-}());
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -4692,12 +5604,12 @@
if (!isBBoxIntersect(bbox1, bbox2)) {
return justCount ? 0 : [];
}
var l1 = bezlen.apply(0, bez1),
l2 = bezlen.apply(0, bez2),
- n1 = ~~(l1 / 5),
- n2 = ~~(l2 / 5),
+ n1 = ~~(l1 / 8),
+ n2 = ~~(l2 / 8),
dots1 = [],
dots2 = [],
xy = {},
res = justCount ? 0 : [];
for (var i = 0; i < n1 + 1; i++) {
@@ -4843,11 +5755,11 @@
return bb;
}
function rectPath(x, y, w, h, r) {
if (r) {
return [
- ["M", x + r, y],
+ ["M", +x + (+r), y],
["l", w - r * 2, 0],
["a", r, r, 0, 0, 1, r, r],
["l", 0, h - r * 2],
["a", r, r, 0, 0, 1, -r, r],
["l", r * 2 - w, 0],
@@ -4863,10 +5775,14 @@
}
function ellipsePath(x, y, rx, ry, a) {
if (a == null && ry == null) {
ry = rx;
}
+ x = +x;
+ y = +y;
+ rx = +rx;
+ ry = +ry;
if (a != null) {
var rad = Math.PI / 180,
x1 = x + rx * Math.cos(-ry * rad),
x2 = x + rx * Math.cos(-a * rad),
y1 = y + rx * Math.sin(-ry * rad),
@@ -4893,45 +5809,29 @@
var attr = unit2px(el);
return ellipsePath(attr.cx, attr.cy, attr.r);
},
ellipse: function (el) {
var attr = unit2px(el);
- return ellipsePath(attr.cx, attr.cy, attr.rx, attr.ry);
+ return ellipsePath(attr.cx || 0, attr.cy || 0, attr.rx, attr.ry);
},
rect: function (el) {
var attr = unit2px(el);
- return rectPath(attr.x, attr.y, attr.width, attr.height, attr.rx, attr.ry);
+ return rectPath(attr.x || 0, attr.y || 0, attr.width, attr.height, attr.rx, attr.ry);
},
image: function (el) {
var attr = unit2px(el);
- return rectPath(attr.x, attr.y, attr.width, attr.height);
+ return rectPath(attr.x || 0, attr.y || 0, attr.width, attr.height);
},
- text: function (el) {
- var bbox = el.node.getBBox();
- return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
- },
- g: function (el) {
- var bbox = el.node.getBBox();
- return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
- },
- symbol: function (el) {
- var bbox = el.getBBox();
- return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
- },
line: function (el) {
- return "M" + [el.attr("x1"), el.attr("y1"), el.attr("x2"), el.attr("y2")];
+ return "M" + [el.attr("x1") || 0, el.attr("y1") || 0, el.attr("x2"), el.attr("y2")];
},
polyline: function (el) {
return "M" + el.attr("points");
},
polygon: function (el) {
return "M" + el.attr("points") + "z";
},
- svg: function (el) {
- var bbox = el.node.getBBox();
- return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
- },
deflt: function (el) {
var bbox = el.node.getBBox();
return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
}
};
@@ -5056,12 +5956,12 @@
r[1] = pa[1];
r[2] = pa[2];
r[3] = pa[3];
r[4] = pa[4];
r[5] = pa[5];
- r[6] = +(pa[6] + x);
- r[7] = +(pa[7] + y);
+ r[6] = +pa[6] + x;
+ r[7] = +pa[7] + y;
break;
case "V":
r[1] = +pa[1] + y;
break;
case "H":
@@ -5116,12 +6016,12 @@
}
pa0 = pa0.toUpperCase();
if (pa0 != "O") {
switch (r[0]) {
case "Z":
- x = mx;
- y = my;
+ x = +mx;
+ y = +my;
break;
case "H":
x = r[1];
break;
case "V":
@@ -5249,84 +6149,120 @@
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
};
}
- function curveDim(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),
- c = p1x - c1x,
- t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
- t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
- y = [p1y, p2y],
- x = [p1x, p2x],
- dot;
- 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);
- y.push(dot.y);
+
+ // Returns bounding box of cubic bezier curve.
+ // Source: http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
+ // Original version: NISHIO Hirokazu
+ // Modifications: https://github.com/timo22345
+ function curveDim(x0, y0, x1, y1, x2, y2, x3, y3) {
+ var tvalues = [],
+ bounds = [[], []],
+ a, b, c, t, t1, t2, b2ac, sqrtb2ac;
+ for (var i = 0; i < 2; ++i) {
+ if (i == 0) {
+ b = 6 * x0 - 12 * x1 + 6 * x2;
+ a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
+ c = 3 * x1 - 3 * x0;
+ } else {
+ b = 6 * y0 - 12 * y1 + 6 * y2;
+ a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
+ c = 3 * y1 - 3 * y0;
+ }
+ if (abs(a) < 1e-12) {
+ if (abs(b) < 1e-12) {
+ continue;
+ }
+ t = -c / b;
+ if (0 < t && t < 1) {
+ tvalues.push(t);
+ }
+ continue;
+ }
+ b2ac = b * b - 4 * c * a;
+ sqrtb2ac = math.sqrt(b2ac);
+ if (b2ac < 0) {
+ continue;
+ }
+ t1 = (-b + sqrtb2ac) / (2 * a);
+ if (0 < t1 && t1 < 1) {
+ tvalues.push(t1);
+ }
+ t2 = (-b - sqrtb2ac) / (2 * a);
+ if (0 < t2 && t2 < 1) {
+ tvalues.push(t2);
+ }
}
- if (t2 > 0 && t2 < 1) {
- dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
- x.push(dot.x);
- y.push(dot.y);
+
+ var x, y, j = tvalues.length,
+ jlen = j,
+ mt;
+ while (j--) {
+ t = tvalues[j];
+ mt = 1 - t;
+ bounds[0][j] = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
+ bounds[1][j] = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
}
- a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
- b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
- 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;
- 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);
- y.push(dot.y);
- }
- if (t2 > 0 && t2 < 1) {
- dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
- x.push(dot.x);
- y.push(dot.y);
- }
+
+ bounds[0][jlen] = x0;
+ bounds[1][jlen] = y0;
+ bounds[0][jlen + 1] = x3;
+ bounds[1][jlen + 1] = y3;
+ bounds[0].length = bounds[1].length = jlen + 2;
+
+
return {
- min: {x: mmin.apply(0, x), y: mmin.apply(0, y)},
- max: {x: mmax.apply(0, x), y: mmax.apply(0, y)}
+ min: {x: mmin.apply(0, bounds[0]), y: mmin.apply(0, bounds[1])},
+ max: {x: mmax.apply(0, bounds[0]), y: mmax.apply(0, bounds[1])}
};
}
+
function path2curve(path, path2) {
var pth = !path2 && paths(path);
if (!path2 && pth.curve) {
return pathClone(pth.curve);
}
var p = pathToAbsolute(path),
p2 = path2 && pathToAbsolute(path2),
attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
- processPath = function (path, d) {
+ processPath = function (path, d, pcom) {
var nx, ny;
if (!path) {
return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
}
- !(path[0] in {T:1, Q:1}) && (d.qx = d.qy = null);
+ !(path[0] in {T: 1, Q: 1}) && (d.qx = d.qy = null);
switch (path[0]) {
case "M":
d.X = path[1];
d.Y = path[2];
break;
case "A":
path = ["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))));
break;
case "S":
- nx = d.x + (d.x - (d.bx || d.x));
- ny = d.y + (d.y - (d.by || d.y));
+ if (pcom == "C" || pcom == "S") { // In "S" case we have to take into account, if the previous command is C/S.
+ nx = d.x * 2 - d.bx; // And reflect the previous
+ ny = d.y * 2 - d.by; // command's control point relative to the current point.
+ }
+ else { // or some else or nothing
+ nx = d.x;
+ ny = d.y;
+ }
path = ["C", nx, ny].concat(path.slice(1));
break;
case "T":
- d.qx = d.x + (d.x - (d.qx || d.x));
- d.qy = d.y + (d.y - (d.qy || d.y));
+ if (pcom == "Q" || pcom == "T") { // In "T" case we have to take into account, if the previous command is Q/T.
+ d.qx = d.x * 2 - d.qx; // And make a reflection similar
+ d.qy = d.y * 2 - d.qy; // to case "S".
+ }
+ else { // or something else or nothing
+ d.qx = d.x;
+ d.qy = d.y;
+ }
path = ["C"].concat(q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
break;
case "Q":
d.qx = path[1];
d.qy = path[2];
@@ -5350,10 +6286,12 @@
fixArc = function (pp, i) {
if (pp[i].length > 7) {
pp[i].shift();
var pi = pp[i];
while (pi.length) {
+ pcoms1[i] = "A"; // if created multiple C:s, their original seg is saved
+ p2 && (pcoms2[i] = "A"); // the same as above
pp.splice(i++, 0, ["C"].concat(pi.splice(0, 6)));
}
pp.splice(i, 1);
ii = mmax(p.length, p2 && p2.length || 0);
}
@@ -5365,16 +6303,45 @@
a1.by = 0;
a1.x = path1[i][1];
a1.y = path1[i][2];
ii = mmax(p.length, p2 && p2.length || 0);
}
- };
+ },
+ pcoms1 = [], // path commands of original path p
+ pcoms2 = [], // path commands of original path p2
+ pfirst = "", // temporary holder for original path command
+ pcom = ""; // holder for previous path command of original path
for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
- p[i] = processPath(p[i], attrs);
- fixArc(p, i);
- p2 && (p2[i] = processPath(p2[i], attrs2));
- p2 && fixArc(p2, i);
+ p[i] && (pfirst = p[i][0]); // save current path command
+
+ if (pfirst != "C") // C is not saved yet, because it may be result of conversion
+ {
+ pcoms1[i] = pfirst; // Save current path command
+ i && ( pcom = pcoms1[i - 1]); // Get previous path command pcom
+ }
+ p[i] = processPath(p[i], attrs, pcom); // Previous path command is inputted to processPath
+
+ if (pcoms1[i] != "A" && pfirst == "C") pcoms1[i] = "C"; // A is the only command
+ // which may produce multiple C:s
+ // so we have to make sure that C is also C in original path
+
+ fixArc(p, i); // fixArc adds also the right amount of A:s to pcoms1
+
+ if (p2) { // the same procedures is done to p2
+ p2[i] && (pfirst = p2[i][0]);
+ if (pfirst != "C") {
+ pcoms2[i] = pfirst;
+ i && (pcom = pcoms2[i - 1]);
+ }
+ p2[i] = processPath(p2[i], attrs2, pcom);
+
+ if (pcoms2[i] != "A" && pfirst == "C") {
+ pcoms2[i] = "C";
+ }
+
+ fixArc(p2, i);
+ }
fixM(p, p2, attrs, attrs2, i);
fixM(p2, p, attrs2, attrs, i);
var seg = p[i],
seg2 = p2 && p2[i],
seglen = seg.length,
@@ -5626,10 +6593,55 @@
- x (string) x coordinate of the point
- y (string) y coordinate of the point
= (boolean) `true` if point is inside
\*/
Snap.path.isPointInsideBBox = isPointInsideBBox;
+ Snap.closest = function (x, y, X, Y) {
+ var r = 100,
+ b = box(x - r / 2, y - r / 2, r, r),
+ inside = [],
+ getter = X[0].hasOwnProperty("x") ? function (i) {
+ return {
+ x: X[i].x,
+ y: X[i].y
+ };
+ } : function (i) {
+ return {
+ x: X[i],
+ y: Y[i]
+ };
+ },
+ found = 0;
+ while (r <= 1e6 && !found) {
+ for (var i = 0, ii = X.length; i < ii; i++) {
+ var xy = getter(i);
+ if (isPointInsideBBox(b, xy.x, xy.y)) {
+ found++;
+ inside.push(xy);
+ break;
+ }
+ }
+ if (!found) {
+ r *= 2;
+ b = box(x - r / 2, y - r / 2, r, r)
+ }
+ }
+ if (r == 1e6) {
+ return;
+ }
+ var len = Infinity,
+ res;
+ for (i = 0, ii = inside.length; i < ii; i++) {
+ var l = Snap.len(x, y, inside[i].x, inside[i].y);
+ if (len > l) {
+ len = l;
+ inside[i].len = l;
+ res = inside[i];
+ }
+ }
+ return res;
+ };
/*\
* Snap.path.isBBoxIntersect
[ method ]
**
* Utility method
@@ -5744,10 +6756,11 @@
\*/
Snap.path.map = mapPath;
Snap.path.toString = toString;
Snap.path.clone = pathClone;
});
+
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -5764,10 +6777,11 @@
mmin = Math.min;
// Set
var Set = function (items) {
this.items = [];
+ this.bindings = {};
this.length = 0;
this.type = "set";
if (items) {
for (var i = 0, ii = items.length; i < ii; i++) {
if (items[i]) {
@@ -5827,19 +6841,119 @@
return this;
}
}
return this;
};
+ /*\
+ * Set.animate
+ [ method ]
+ **
+ * Animates each element in set in sync.
+ *
+ **
+ - attrs (object) key-value pairs of destination attributes
+ - duration (number) duration of the animation in milliseconds
+ - easing (function) #optional easing function from @mina or custom
+ - callback (function) #optional callback function that executes when the animation ends
+ * or
+ - animation (array) array of animation parameter for each element in set in format `[attrs, duration, easing, callback]`
+ > Usage
+ | // animate all elements in set to radius 10
+ | set.animate({r: 10}, 500, mina.easein);
+ | // or
+ | // animate first element to radius 10, but second to radius 20 and in different time
+ | set.animate([{r: 10}, 500, mina.easein], [{r: 20}, 1500, mina.easein]);
+ = (Element) the current element
+ \*/
+ setproto.animate = function (attrs, ms, easing, callback) {
+ if (typeof easing == "function" && !easing.length) {
+ callback = easing;
+ easing = mina.linear;
+ }
+ if (attrs instanceof Snap._.Animation) {
+ callback = attrs.callback;
+ easing = attrs.easing;
+ ms = easing.dur;
+ attrs = attrs.attr;
+ }
+ var args = arguments;
+ if (Snap.is(attrs, "array") && Snap.is(args[args.length - 1], "array")) {
+ var each = true;
+ }
+ var begin,
+ handler = function () {
+ if (begin) {
+ this.b = begin;
+ } else {
+ begin = this.b;
+ }
+ },
+ cb = 0,
+ set = this,
+ callbacker = callback && function () {
+ if (++cb == set.length) {
+ callback.call(this);
+ }
+ };
+ return this.forEach(function (el, i) {
+ eve.once("snap.animcreated." + el.id, handler);
+ if (each) {
+ args[i] && el.animate.apply(el, args[i]);
+ } else {
+ el.animate(attrs, ms, easing, callbacker);
+ }
+ });
+ };
setproto.remove = function () {
while (this.length) {
this.pop().remove();
}
return this;
};
+ /*\
+ * Set.bind
+ [ method ]
+ **
+ * Specifies how to handle a specific attribute when applied
+ * to a set.
+ *
+ **
+ - attr (string) attribute name
+ - callback (function) function to run
+ * or
+ - attr (string) attribute name
+ - element (Element) specific element in the set to apply the attribute to
+ * or
+ - attr (string) attribute name
+ - element (Element) specific element in the set to apply the attribute to
+ - eattr (string) attribute on the element to bind the attribute to
+ = (object) Set object
+ \*/
+ setproto.bind = function (attr, a, b) {
+ var data = {};
+ if (typeof a == "function") {
+ this.bindings[attr] = a;
+ } else {
+ var aname = b || attr;
+ this.bindings[attr] = function (v) {
+ data[aname] = v;
+ a.attr(data);
+ };
+ }
+ return this;
+ };
setproto.attr = function (value) {
+ var unbound = {};
+ for (var k in value) {
+ if (this.bindings[k]) {
+ this.bindings[k](value[k]);
+ } else {
+ unbound[k] = value[k];
+ }
+ }
for (var i = 0, ii = this.items.length; i < ii; i++) {
- this.items[i].attr(value);
+ this.items[i].attr(unbound);
}
return this;
};
/*\
* Set.clear
@@ -5949,18 +7063,20 @@
setproto.toString = function () {
return "Snap\u2018s set";
};
setproto.type = "set";
// export
+ Snap.Set = Set;
Snap.set = function () {
var set = new Set;
if (arguments.length) {
set.push.apply(set, Array.prototype.slice.call(arguments, 0));
}
return set;
};
});
+
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -6037,10 +7153,13 @@
function getUnit(unit) {
return function (val) {
return +val.toFixed(3) + unit;
};
}
+ function getViewBox(val) {
+ return val.join(" ");
+ }
function getColour(clr) {
return Snap.rgb(clr[0], clr[1], clr[2]);
}
function getPath(path) {
var k = 0, i, ii, j, jj, out, a, b = [];
@@ -6062,17 +7181,29 @@
out.push(path[i][j]);
}
}
return out;
}
+ function isNumeric(obj) {
+ return isFinite(parseFloat(obj));
+ }
+ function arrayEqual(arr1, arr2) {
+ if (!Snap.is(arr1, "array") || !Snap.is(arr2, "array")) {
+ return false;
+ }
+ return arr1.toString() == arr2.toString();
+ }
Element.prototype.equal = function (name, b) {
+ return eve("snap.util.equal", this, name, b).firstDefined();
+ };
+ eve.on("snap.util.equal", function (name, b) {
var A, B, a = Str(this.attr(name) || ""),
el = this;
- if (a == +a && b == +b) {
+ if (isNumeric(a) && isNumeric(b)) {
return {
- from: +a,
- to: +b,
+ from: parseFloat(a),
+ to: parseFloat(b),
f: getNumber
};
}
if (names[name] == "colour") {
A = Snap.color(a);
@@ -6081,10 +7212,19 @@
from: [A.r, A.g, A.b, A.opacity],
to: [B.r, B.g, B.b, B.opacity],
f: getColour
};
}
+ if (name == "viewBox") {
+ A = this.attr(name).vb.split(" ").map(Number);
+ B = b.split(" ").map(Number);
+ return {
+ from: A,
+ to: B,
+ f: getViewBox
+ };
+ }
if (name == "transform" || name == "gradientTransform" || name == "patternTransform") {
if (b instanceof Snap.Matrix) {
b = b.toTransformString();
}
if (!Snap._.rgTransform.test(b)) {
@@ -6101,21 +7241,21 @@
to: path2array(A[1]),
f: getPath(A[0])
};
}
if (name == "points") {
- A = Str(a).split(",");
- B = Str(b).split(",");
+ A = Str(a).split(Snap._.separator);
+ B = Str(b).split(Snap._.separator);
return {
from: A,
to: B,
f: function (val) { return val; }
};
}
var aUnit = a.match(reUnit),
bUnit = Str(b).match(reUnit);
- if (aUnit && aUnit == bUnit) {
+ if (aUnit && arrayEqual(aUnit, bUnit)) {
return {
from: parseFloat(a),
to: parseFloat(b),
f: getUnit(aUnit)
};
@@ -6124,12 +7264,13 @@
from: this.asPX(name),
to: this.asPX(name, b),
f: getNumber
};
}
- };
+ });
});
+
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -6153,13 +7294,14 @@
touchMap = {
mousedown: "touchstart",
mousemove: "touchmove",
mouseup: "touchend"
},
- getScroll = function (xy) {
- var name = xy == "y" ? "scrollTop" : "scrollLeft";
- return glob.doc.documentElement[name] || glob.doc.body[name];
+ getScroll = function (xy, el) {
+ var name = xy == "y" ? "scrollTop" : "scrollLeft",
+ doc = el && el.node ? el.node.ownerDocument : glob.doc;
+ return doc[name in doc.documentElement ? "documentElement" : "body"][name];
},
preventDefault = function () {
this.returnValue = false;
},
preventTouch = function () {
@@ -6169,70 +7311,47 @@
this.cancelBubble = true;
},
stopTouch = function () {
return this.originalEvent.stopPropagation();
},
- addEvent = (function () {
- if (glob.doc.addEventListener) {
- return function (obj, type, fn, element) {
- var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
- f = function (e) {
- var scrollY = getScroll("y"),
- scrollX = getScroll("x");
- if (supportsTouch && touchMap[has](type)) {
- for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
- if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) {
- var olde = e;
- e = e.targetTouches[i];
- e.originalEvent = olde;
- e.preventDefault = preventTouch;
- e.stopPropagation = stopTouch;
- break;
- }
- }
+ addEvent = function (obj, type, fn, element) {
+ var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
+ f = function (e) {
+ var scrollY = getScroll("y", element),
+ scrollX = getScroll("x", element);
+ if (supportsTouch && touchMap[has](type)) {
+ for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
+ if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) {
+ var olde = e;
+ e = e.targetTouches[i];
+ e.originalEvent = olde;
+ e.preventDefault = preventTouch;
+ e.stopPropagation = stopTouch;
+ break;
}
- var x = e.clientX + scrollX,
- y = e.clientY + scrollY;
- return fn.call(element, e, x, y);
- };
-
- if (type !== realName) {
- obj.addEventListener(type, f, false);
+ }
}
+ var x = e.clientX + scrollX,
+ y = e.clientY + scrollY;
+ return fn.call(element, e, x, y);
+ };
- obj.addEventListener(realName, f, false);
+ if (type !== realName) {
+ obj.addEventListener(type, f, false);
+ }
- return function () {
- if (type !== realName) {
- obj.removeEventListener(type, f, false);
- }
+ obj.addEventListener(realName, f, false);
- obj.removeEventListener(realName, f, false);
- return true;
- };
- };
- } else if (glob.doc.attachEvent) {
- return function (obj, type, fn, element) {
- var f = function (e) {
- e = e || glob.win.event;
- var scrollY = getScroll("y"),
- scrollX = getScroll("x"),
- x = e.clientX + scrollX,
- y = e.clientY + scrollY;
- e.preventDefault = e.preventDefault || preventDefault;
- e.stopPropagation = e.stopPropagation || stopPropagation;
- return fn.call(element, e, x, y);
- };
- obj.attachEvent("on" + type, f);
- var detacher = function () {
- obj.detachEvent("on" + type, f);
- return true;
- };
- return detacher;
- };
- }
- })(),
+ return function () {
+ if (type !== realName) {
+ obj.removeEventListener(type, f, false);
+ }
+
+ obj.removeEventListener(realName, f, false);
+ return true;
+ };
+ },
drag = [],
dragMove = function (e) {
var x = e.clientX,
y = e.clientY,
scrollY = getScroll("y"),
@@ -6256,11 +7375,10 @@
} else {
e.preventDefault();
}
var node = dragi.el.node,
o,
- glob = Snap._.glob,
next = node.nextSibling,
parent = node.parentNode,
display = node.style.display;
// glob.win.opera && parent.removeChild(node);
// node.style.display = "none";
@@ -6279,10 +7397,11 @@
dragi;
while (i--) {
dragi = drag[i];
dragi.el._drag = {};
eve("snap.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
+ eve.off("snap.drag.*." + dragi.el.id);
}
drag = [];
};
/*\
* Element.click
@@ -6476,12 +7595,18 @@
if (Snap.is(fn, "function")) {
this.events = this.events || [];
this.events.push({
name: eventName,
f: fn,
- unbind: addEvent(this.shape || this.node || glob.doc, eventName, fn, scope || this)
+ unbind: addEvent(this.node || document, eventName, fn, scope || this)
});
+ } else {
+ for (var i = 0, ii = this.events.length; i < ii; i++) if (this.events[i].name == eventName) {
+ try {
+ this.events[i].f.call(this);
+ } catch (e) {}
+ }
}
return this;
};
Snap["un" + eventName] =
elproto["un" + eventName] = function (fn) {
@@ -6558,36 +7683,41 @@
* End event and end handler are called in specified context or in context of the element with following parameters:
o event (object) DOM event object
= (object) @Element
\*/
elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
+ var el = this;
if (!arguments.length) {
var origTransform;
- return this.drag(function (dx, dy) {
+ return el.drag(function (dx, dy) {
this.attr({
transform: origTransform + (origTransform ? "T" : "t") + [dx, dy]
});
}, function () {
origTransform = this.transform().local;
});
}
function start(e, x, y) {
(e.originalEvent || e).preventDefault();
- this._drag.x = x;
- this._drag.y = y;
- this._drag.id = e.identifier;
+ el._drag.x = x;
+ el._drag.y = y;
+ el._drag.id = e.identifier;
!drag.length && Snap.mousemove(dragMove).mouseup(dragUp);
- drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
- onstart && eve.on("snap.drag.start." + this.id, onstart);
- onmove && eve.on("snap.drag.move." + this.id, onmove);
- onend && eve.on("snap.drag.end." + this.id, onend);
- eve("snap.drag.start." + this.id, start_scope || move_scope || this, x, y, e);
+ drag.push({el: el, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
+ onstart && eve.on("snap.drag.start." + el.id, onstart);
+ onmove && eve.on("snap.drag.move." + el.id, onmove);
+ onend && eve.on("snap.drag.end." + el.id, onend);
+ eve("snap.drag.start." + el.id, start_scope || move_scope || el, x, y, e);
}
- this._drag = {};
- draggable.push({el: this, start: start});
- this.mousedown(start);
- return this;
+ function init(e, x, y) {
+ eve("snap.draginit." + el.id, el, e, x, y);
+ }
+ eve.on("snap.draginit." + el.id, start);
+ el._drag = {};
+ draggable.push({el: el, start: start, init: init});
+ el.mousedown(init);
+ return el;
};
/*
* Element.onDragOver
[ method ]
**
@@ -6604,18 +7734,20 @@
* Removes all drag event handlers from the given element
\*/
elproto.undrag = function () {
var i = draggable.length;
while (i--) if (draggable[i].el == this) {
- this.unmousedown(draggable[i].start);
+ this.unmousedown(draggable[i].init);
draggable.splice(i, 1);
eve.unbind("snap.drag.*." + this.id);
+ eve.unbind("snap.draginit." + this.id);
}
!draggable.length && Snap.unmousemove(dragMove).unmouseup(dragUp);
return this;
};
});
+
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
@@ -6632,11 +7764,10 @@
pproto = Paper.prototype,
rgurl = /^\s*url\((.+)\)/,
Str = String,
$ = Snap._.$;
Snap.filter = {};
-// SIERRA Paper.filter(): I don't understand the note. Does that mean an HTML should dedicate a separate SVG region for a filter definition? What's the advantage over a DEFS?
/*\
* Paper.filter
[ method ]
**
* Creates a `<filter>` element
@@ -6725,43 +7856,62 @@
* Snap.filter.shadow
[ method ]
**
* Returns an SVG markup string for the shadow filter
**
- - dx (number) horizontal shift of the shadow, in pixels
- - dy (number) vertical shift of the shadow, in pixels
+ - dx (number) #optional horizontal shift of the shadow, in pixels
+ - dy (number) #optional vertical shift of the shadow, in pixels
- blur (number) #optional amount of blur
- color (string) #optional color of the shadow
+ - opacity (number) #optional `0..1` opacity of the shadow
+ * or
+ - dx (number) #optional horizontal shift of the shadow, in pixels
+ - dy (number) #optional vertical shift of the shadow, in pixels
+ - color (string) #optional color of the shadow
+ - opacity (number) #optional `0..1` opacity of the shadow
+ * which makes blur default to `4`. Or
+ - dx (number) #optional horizontal shift of the shadow, in pixels
+ - dy (number) #optional vertical shift of the shadow, in pixels
+ - opacity (number) #optional `0..1` opacity of the shadow
= (string) filter representation
> Usage
| var f = paper.filter(Snap.filter.shadow(0, 2, 3)),
| c = paper.circle(10, 10, 10).attr({
| filter: f
| });
\*/
- Snap.filter.shadow = function (dx, dy, blur, color) {
+ Snap.filter.shadow = function (dx, dy, blur, color, opacity) {
+ if (typeof blur == "string") {
+ color = blur;
+ opacity = color;
+ blur = 4;
+ }
+ if (typeof color != "string") {
+ opacity = color;
+ color = "#000";
+ }
color = color || "#000";
if (blur == null) {
blur = 4;
}
- if (typeof blur == "string") {
- color = blur;
- blur = 4;
+ if (opacity == null) {
+ opacity = 1;
}
if (dx == null) {
dx = 0;
dy = 2;
}
if (dy == null) {
dy = dx;
}
color = Snap.color(color);
- return Snap.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 Snap.format('<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feComponentTransfer><feFuncA type="linear" slope="{opacity}"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>', {
color: color,
dx: dx,
dy: dy,
- blur: blur
+ blur: blur,
+ opacity: opacity
});
};
Snap.filter.shadow.toString = function () {
return this();
};
@@ -6869,10 +8019,11 @@
\*/
Snap.filter.invert = function (amount) {
if (amount == null) {
amount = 1;
}
+// <feColorMatrix type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" color-interpolation-filters="sRGB"/>
return Snap.format('<feComponentTransfer><feFuncR type="table" tableValues="{amount} {amount2}"/><feFuncG type="table" tableValues="{amount} {amount2}"/><feFuncB type="table" tableValues="{amount} {amount2}"/></feComponentTransfer>', {
amount: amount,
amount2: 1 - amount
});
};
@@ -6919,7 +8070,101 @@
};
Snap.filter.contrast.toString = function () {
return this();
};
});
+
+// Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
+ var box = Snap._.box,
+ is = Snap.is,
+ firstLetter = /^[^a-z]*([tbmlrc])/i,
+ toString = function () {
+ return "T" + this.dx + "," + this.dy;
+ };
+ /*\
+ * Element.getAlign
+ [ method ]
+ **
+ * Returns shift needed to align the element relatively to given element.
+ * If no elements specified, parent `<svg>` container will be used.
+ - el (object) @optional alignment element
+ - way (string) one of six values: `"top"`, `"middle"`, `"bottom"`, `"left"`, `"center"`, `"right"`
+ = (object|string) Object in format `{dx: , dy: }` also has a string representation as a transformation string
+ > Usage
+ | el.transform(el.getAlign(el2, "top"));
+ * or
+ | var dy = el.getAlign(el2, "top").dy;
+ \*/
+ Element.prototype.getAlign = function (el, way) {
+ if (way == null && is(el, "string")) {
+ way = el;
+ el = null;
+ }
+ el = el || this.paper;
+ var bx = el.getBBox ? el.getBBox() : box(el),
+ bb = this.getBBox(),
+ out = {};
+ way = way && way.match(firstLetter);
+ way = way ? way[1].toLowerCase() : "c";
+ switch (way) {
+ case "t":
+ out.dx = 0;
+ out.dy = bx.y - bb.y;
+ break;
+ case "b":
+ out.dx = 0;
+ out.dy = bx.y2 - bb.y2;
+ break;
+ case "m":
+ out.dx = 0;
+ out.dy = bx.cy - bb.cy;
+ break;
+ case "l":
+ out.dx = bx.x - bb.x;
+ out.dy = 0;
+ break;
+ case "r":
+ out.dx = bx.x2 - bb.x2;
+ out.dy = 0;
+ break;
+ default:
+ out.dx = bx.cx - bb.cx;
+ out.dy = 0;
+ break;
+ }
+ out.toString = toString;
+ return out;
+ };
+ /*\
+ * Element.align
+ [ method ]
+ **
+ * Aligns the element relatively to given one via transformation.
+ * If no elements specified, parent `<svg>` container will be used.
+ - el (object) @optional alignment element
+ - way (string) one of six values: `"top"`, `"middle"`, `"bottom"`, `"left"`, `"center"`, `"right"`
+ = (object) this element
+ > Usage
+ | el.align(el2, "top");
+ * or
+ | el.align("middle");
+ \*/
+ Element.prototype.align = function (el, way) {
+ return this.transform("..." + this.getAlign(el, way));
+ };
+});
+
return Snap;
-}));
\ No newline at end of file
+}));