Added toDataURL() methods to paper and element. Extracted element methods as module.

master
baranovs 2014-08-13 22:46:40 +10:00
parent 28b7a7a753
commit 59e4a15cda
11 changed files with 2458 additions and 2309 deletions

View File

@ -31,6 +31,7 @@ module.exports = function(grunt) {
"./src/amd-banner.js",
"./src/mina.js",
"./src/svg.js",
"./src/element.js",
"./src/matrix.js",
"./src/attr.js",
"./src/class.js",

10
dist/snap.svg-min.js vendored

File diff suppressed because one or more lines are too long

898
dist/snap.svg.js vendored
View File

@ -1,6 +1,6 @@
// Snap.svg 0.3.0
//
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
// Copyright (c) 2013 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.
@ -14,7 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
// build: 2014-08-04
// build: 2014-08-13
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -1732,38 +1732,6 @@ function transform2matrix(tstr, bbox) {
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 Snap.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) {
@ -2004,7 +1972,6 @@ function Element(el) {
}
}
}
(function (elproto) {
/*\
* Element.attr
[ method ]
@ -2030,7 +1997,7 @@ function Element(el) {
* (`+`, `-`, `*` 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) {
@ -2052,6 +2019,414 @@ function Element(el) {
}
return el;
};
/*\
* 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);
}
div.innerHTML = E;
}
}
return new Fragment(f);
};
function Fragment(frag) {
this.node = frag;
}
// 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);
}
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;
};
// 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 four 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;
}());
// 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 ]
@ -2117,6 +2492,38 @@ function Element(el) {
var propString = function () {
return this.string;
};
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 || E);
}
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 ]
@ -2645,7 +3052,7 @@ function Element(el) {
x = x.x;
}
$(p.node, {
viewBox: [x, y, width, height].join(S),
viewBox: [x, y, width, height].join(" "),
markerWidth: width,
markerHeight: height,
orient: "auto",
@ -2955,406 +3362,36 @@ function Element(el) {
return res;
};
}
}(Element.prototype));
/*\
* 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;
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()
});
console.log(svg);
return "data:image/svg+xml;utf8," + svg;
return "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svg)));
}
div.innerHTML = svg;
svg = div.getElementsByTagName("svg")[0];
if (svg) {
if (full) {
f = svg;
} else {
while (svg.firstChild) {
f.appendChild(svg.firstChild);
}
}
}
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.prototype.select = elproto.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);
}
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
Fragment.prototype.selectAll = elproto.selectAll;
});
}
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;
};
// 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 four 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;
}());
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@ -4688,6 +4725,9 @@ Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
}
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);
@ -4997,6 +5037,18 @@ Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
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 ]
@ -6733,6 +6785,7 @@ Snap.plugin(function (Snap, Element, Paper, glob) {
};
setproto.type = "set";
// export
Snap.Set = Set;
Snap.set = function () {
var set = new Set;
if (arguments.length) {
@ -7420,7 +7473,6 @@ Snap.plugin(function (Snap, Element, Paper, glob) {
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 ]

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,9 @@
"files": [{
"url": "src/svg.js",
"link": "https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js"
}, {
"url": "src/element.js",
"link": "https://github.com/adobe-webplatform/Snap.svg/blob/master/src/element.js"
}, {
"url": "src/matrix.js",
"link": "https://github.com/adobe-webplatform/Snap.svg/blob/master/src/matrix.js"

View File

@ -1,6 +1,6 @@
// Snap.svg @VERSION
//
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
// Copyright (c) 2013 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.

986
src/element.js Normal file
View File

@ -0,0 +1,986 @@
// 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
**
= (object) bounding box descriptor:
o {
o cx: (number) x of the center,
o cy: (number) x of the center,
o h: (number) height,
o height: (number) height,
o path: (string) path command for the box,
o r0: (number) radius of a circle that fully encloses the box,
o r1: (number) radius of the smallest circle that can be enclosed,
o r2: (number) radius of the largest circle that can be enclosed,
o vb: (string) box as a viewbox command,
o w: (number) width,
o width: (number) width,
o x2: (number) x of the right side,
o x: (number) x of the left side,
o y2: (number) y of the bottom edge,
o y: (number) y of the top edge
o }
\*/
elproto.getBBox = function (isWithoutTransform) {
if (!Snap.Matrix || !Snap.path) {
return this.node.getBBox();
}
var el = this,
m = new Snap.Matrix;
if (el.removed) {
return Snap._.box();
}
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));
}
}
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 doesnt give you bbox of hidden element
return Snap._.box();
}
};
var propString = function () {
return this.string;
};
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 || E);
}
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
**
- tstr (string) transform string in Snap or SVG format
= (Element) the current element
* or
= (object) transformation descriptor:
o {
o string (string) transform string,
o globalMatrix (Matrix) matrix of all transformations applied to element or its parents,
o localMatrix (Matrix) matrix of transformations applied only to the element,
o diffMatrix (Matrix) matrix of difference between global and local transformations,
o global (string) global transformation as string,
o local (string) local transformation as string,
o toString (function) returns `string` property
o }
\*/
elproto.transform = function (tstr) {
var _ = this._;
if (tstr == null) {
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) ?
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 Snap.Matrix) {
this.matrix = tstr;
} else {
extractTransform(this, tstr);
}
if (this.node) {
if (this.type == "linearGradient" || this.type == "radialGradient") {
$(this.node, {gradientTransform: this.matrix});
} else if (this.type == "pattern") {
$(this.node, {patternTransform: this.matrix});
} else {
$(this.node, {transform: this.matrix});
}
}
return this;
};
/*\
* Element.parent
[ method ]
**
* Returns the element's parent
**
= (Element) the parent element
\*/
elproto.parent = function () {
return wrap(this.node.parentNode);
};
/*\
* Element.append
[ method ]
**
* Appends the given element to current one
**
- el (Element|Set) element to append
= (Element) the parent element
\*/
/*\
* Element.add
[ method ]
**
* See @Element.append
\*/
elproto.append = elproto.add = function (el) {
if (el) {
if (el.type == "set") {
var it = this;
el.forEach(function (el) {
it.add(el);
});
return this;
}
el = wrap(el);
this.node.appendChild(el.node);
el.paper = this.paper;
}
return this;
};
/*\
* Element.appendTo
[ method ]
**
* Appends the current element to the given one
**
- el (Element) parent element to append to
= (Element) the child element
\*/
elproto.appendTo = function (el) {
if (el) {
el = wrap(el);
el.append(this);
}
return this;
};
/*\
* Element.prepend
[ method ]
**
* Prepends the given element to the current one
**
- 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;
this.parent() && this.parent().add();
parent && parent.add();
}
return this;
};
/*\
* Element.prependTo
[ method ]
**
* Prepends the current element to the given one
**
- el (Element) parent element to prepend to
= (Element) the child element
\*/
elproto.prependTo = function (el) {
el = wrap(el);
el.prepend(this);
return this;
};
/*\
* Element.before
[ method ]
**
* Inserts given element before the current one
**
- el (Element) element to insert
= (Element) the parent element
\*/
elproto.before = function (el) {
if (el.type == "set") {
var it = this;
el.forEach(function (el) {
var parent = el.parent();
it.node.parentNode.insertBefore(el.node, it.node);
parent && parent.add();
});
this.parent().add();
return this;
}
el = wrap(el);
var parent = el.parent();
this.node.parentNode.insertBefore(el.node, this.node);
this.parent() && this.parent().add();
parent && parent.add();
el.paper = this.paper;
return this;
};
/*\
* Element.after
[ method ]
**
* Inserts given element after the current one
**
- el (Element) element to insert
= (Element) the parent element
\*/
elproto.after = function (el) {
el = wrap(el);
var parent = el.parent();
if (this.node.nextSibling) {
this.node.parentNode.insertBefore(el.node, this.node.nextSibling);
} else {
this.node.parentNode.appendChild(el.node);
}
this.parent() && this.parent().add();
parent && parent.add();
el.paper = this.paper;
return this;
};
/*\
* Element.insertBefore
[ method ]
**
* Inserts the element after the given one
**
- el (Element) element next to whom insert to
= (Element) the parent element
\*/
elproto.insertBefore = function (el) {
el = wrap(el);
var parent = this.parent();
el.node.parentNode.insertBefore(this.node, el.node);
this.paper = el.paper;
parent && parent.add();
el.parent() && el.parent().add();
return this;
};
/*\
* Element.insertAfter
[ method ]
**
* Inserts the element after the given one
**
- el (Element) element next to whom insert to
= (Element) the parent element
\*/
elproto.insertAfter = function (el) {
el = wrap(el);
var parent = this.parent();
el.node.parentNode.insertBefore(this.node, el.node.nextSibling);
this.paper = el.paper;
parent && parent.add();
el.parent() && el.parent().add();
return this;
};
/*\
* Element.remove
[ method ]
**
* Removes element from the DOM
= (Element) the detached element
\*/
elproto.remove = function () {
var parent = this.parent();
this.node.parentNode && this.node.parentNode.removeChild(this.node);
delete this.paper;
this.removed = true;
parent && parent.add();
return this;
};
/*\
* Element.select
[ method ]
**
* Gathers the nested @Element matching the given set of CSS selectors
**
- query (string) CSS selector
= (Element) result of query selection
\*/
elproto.select = function (query) {
query = Str(query).replace(/([^\\]):/g, "$1\\:");
return wrap(this.node.querySelector(query));
};
/*\
* Element.selectAll
[ method ]
**
* Gathers nested @Element objects matching the given set of CSS selectors
**
- query (string) CSS selector
= (Set|array) result of query selection
\*/
elproto.selectAll = function (query) {
var nodelist = this.node.querySelectorAll(query),
set = (Snap.set || Array)();
for (var i = 0; i < nodelist.length; i++) {
set.push(wrap(nodelist[i]));
}
return set;
};
/*\
* Element.asPX
[ method ]
**
* Returns given attribute of the element as a `px` value (not %, em, etc.)
**
- attr (string) attribute name
- value (string) #optional attribute value
= (Element) result of query selection
\*/
elproto.asPX = function (attr, value) {
if (value == null) {
value = this.attr(attr);
}
return +unit2px(this, attr, value);
};
// SIERRA Element.use(): I suggest adding a note about how to access the original element the returned <use> instantiates. It's a part of SVG with which ordinary web developers may be least familiar.
/*\
* Element.use
[ method ]
**
* Creates a `<use>` element linked to the current element
**
= (Element) the `<use>` element
\*/
elproto.use = function () {
var use,
id = this.node.id;
if (!id) {
id = this.id;
$(this.node, {
id: id
});
}
if (this.type == "linearGradient" || this.type == "radialGradient" ||
this.type == "pattern") {
use = make(this.type, this.node.parentNode);
} else {
use = make("use", this.node.parentNode);
}
$(use.node, {
"xlink:href": "#" + id
});
use.original = this;
return use;
};
function fixids(el) {
var els = el.selectAll("*"),
it,
url = /^\s*url\(("|'|)(.*)\1\)\s*$/,
ids = [],
uses = {};
function urltest(it, name) {
var val = $(it.node, name);
val = val && val.match(url);
val = val && val[2];
if (val && val.charAt() == "#") {
val = val.substring(1);
} else {
return;
}
if (val) {
uses[val] = (uses[val] || []).concat(function (id) {
var attr = {};
attr[name] = URL(id);
$(it.node, attr);
});
}
}
function linktest(it) {
var val = $(it.node, "xlink:href");
if (val && val.charAt() == "#") {
val = val.substring(1);
} else {
return;
}
if (val) {
uses[val] = (uses[val] || []).concat(function (id) {
it.attr("xlink:href", "#" + id);
});
}
}
for (var i = 0, ii = els.length; i < ii; i++) {
it = els[i];
urltest(it, "fill");
urltest(it, "stroke");
urltest(it, "filter");
urltest(it, "mask");
urltest(it, "clip-path");
linktest(it);
var oldid = $(it.node, "id");
if (oldid) {
$(it.node, {id: it.id});
ids.push({
old: oldid,
id: it.id
});
}
}
for (i = 0, ii = ids.length; i < ii; i++) {
var fs = uses[ids[i].old];
if (fs) {
for (var j = 0, jj = fs.length; j < jj; j++) {
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;
};
/*\
* Element.toDefs
[ method ]
**
* Moves element to the shared `<defs>` area
**
= (Element) the element
\*/
elproto.toDefs = function () {
var defs = getSomeDefs(this);
defs.appendChild(this.node);
return this;
};
/*\
* Element.pattern
[ method ]
**
* Depricated. Use @Element.toPattern instead.
\*/
/*\
* Element.toPattern
[ method ]
**
* Creates a `<pattern>` element from the current element
**
* To create a pattern you have to specify the pattern rect:
- x (string|number)
- y (string|number)
- width (string|number)
- height (string|number)
= (Element) the `<pattern>` element
* You can use pattern later on as an argument for `fill` attribute:
| var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
| fill: "none",
| stroke: "#bada55",
| strokeWidth: 5
| }).pattern(0, 0, 10, 10),
| c = paper.circle(200, 200, 100);
| c.attr({
| fill: p
| });
\*/
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) {
y = x.y;
width = x.width;
height = x.height;
x = x.x;
}
$(p.node, {
x: x,
y: y,
width: width,
height: height,
patternUnits: "userSpaceOnUse",
id: p.id,
viewBox: [x, y, width, height].join(" ")
});
p.node.appendChild(this.node);
return p;
};
// SIERRA Element.marker(): clarify what a reference point is. E.g., helps you offset the object from its edge such as when centering it over a path.
// SIERRA Element.marker(): I suggest the method should accept default reference point values. Perhaps centered with (refX = width/2) and (refY = height/2)? Also, couldn't it assume the element's current _width_ and _height_? And please specify what _x_ and _y_ mean: offsets? If so, from where? Couldn't they also be assigned default values?
/*\
* Element.marker
[ method ]
**
* Creates a `<marker>` element from the current element
**
* To create a marker you have to specify the bounding rect and reference point:
- x (number)
- y (number)
- width (number)
- height (number)
- refX (number)
- refY (number)
= (Element) the `<marker>` element
* You can specify the marker later as an argument for `marker-start`, `marker-end`, `marker-mid`, and `marker` attributes. The `marker` attribute places the marker at every point along the path, and `marker-mid` places them at every point except the start and end.
\*/
// TODO add usage for markers
elproto.marker = function (x, y, width, height, refX, refY) {
var p = make("marker", getSomeDefs(this));
if (x == null) {
x = this.getBBox();
}
if (is(x, "object") && "x" in x) {
y = x.y;
width = x.width;
height = x.height;
refX = x.refX || x.cx;
refY = x.refY || x.cy;
x = x.x;
}
$(p.node, {
viewBox: [x, y, width, height].join(" "),
markerWidth: width,
markerHeight: height,
orient: "auto",
refX: refX || 0,
refY: refY || 0,
id: p.id
});
p.node.appendChild(this.node);
return p;
};
// animation
function slice(from, to, f) {
return function (arr) {
var res = arr.slice(from, to);
if (res.length == 1) {
res = res[0];
}
return f ? f(res) : res;
};
}
var Animation = function (attr, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
this.attr = attr;
this.dur = ms;
easing && (this.easing = easing);
callback && (this.callback = callback);
};
Snap._.Animation = Animation;
/*\
* Snap.animation
[ method ]
**
* Creates an animation object
**
- attr (object) attributes of final destination
- duration (number) duration of the animation, in milliseconds
- easing (function) #optional one of easing functions of @mina or custom one
- callback (function) #optional callback function that fires when animation ends
= (object) animation object
\*/
Snap.animation = function (attr, ms, easing, callback) {
return new Animation(attr, ms, easing, callback);
};
/*\
* Element.inAnim
[ method ]
**
* 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 }
\*/
elproto.inAnim = function () {
var el = this,
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 () {
a.stop();
}
});
}(el.anims[id]));
}
return res;
};
/*\
* Snap.animate
[ method ]
**
* Runs generic animation of one number into another with a caring function
**
- from (number|array) number or array of numbers
- to (number|array) number or array of numbers
- setter (function) caring function that accepts one number argument
- duration (number) duration, in milliseconds
- easing (function) #optional easing function from @mina or custom
- callback (function) #optional callback function to execute when animation ends
= (object) animation object in @mina format
o {
o id (string) animation id, consider it read-only,
o duration (function) gets or sets the duration of the animation,
o easing (function) easing,
o speed (function) gets or sets the speed of the animation,
o status (function) gets or sets the status of the animation,
o stop (function) stops the animation
o }
| var rect = Snap().rect(0, 0, 10, 10);
| Snap.animate(0, 10, function (val) {
| rect.attr({
| x: val
| });
| }, 1000);
| // in given context is equivalent to
| rect.animate({x: 10}, 1000);
\*/
Snap.animate = function (from, to, setter, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
var now = mina.time(),
anim = mina(from, to, now, now + ms, mina.time, setter, easing);
callback && eve.once("mina.finish." + anim.id, callback);
return anim;
};
/*\
* Element.stop
[ method ]
**
* Stops all the animations for the current element
**
= (Element) the current element
\*/
elproto.stop = function () {
var anims = this.inAnim();
for (var i = 0, ii = anims.length; i < ii; i++) {
anims[i].stop();
}
return this;
};
/*\
* Element.animate
[ method ]
**
* Animates the given attributes of the element
**
- 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
= (Element) the current element
\*/
elproto.animate = function (attrs, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
if (attrs instanceof Animation) {
callback = attrs.callback;
easing = attrs.easing;
ms = easing.dur;
attrs = attrs.attr;
}
var fkeys = [], tkeys = [], keys = {}, from, to, f, eq,
el = this;
for (var key in attrs) if (attrs[has](key)) {
if (el.equal) {
eq = el.equal(key, Str(attrs[key]));
from = eq.from;
to = eq.to;
f = eq.f;
} else {
from = +el.attr(key);
to = +attrs[key];
}
var len = is(from, "array") ? from.length : 1;
keys[key] = slice(fkeys.length, fkeys.length + len, f);
fkeys = fkeys.concat(from);
tkeys = tkeys.concat(to);
}
var now = mina.time(),
anim = mina(fkeys, tkeys, now, now + ms, mina.time, function (val) {
var attr = {};
for (var key in keys) if (keys[has](key)) {
attr[key] = keys[key](val);
}
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 () {
delete el.anims[anim.id];
});
return el;
};
var eldata = {};
/*\
* Element.data
[ method ]
**
* Adds or retrieves given value associated with given key. (Dont confuse
* with `data-` attributes)
*
* See also @Element.removeData
- key (string) key to store data
- value (any) #optional value to store
= (object) @Element
* or, if value is not specified:
= (any) value
> Usage
| for (var i = 0, i < 5, i++) {
| paper.circle(10 + 15 * i, 10, 10)
| .attr({fill: "#000"})
| .data("i", i)
| .click(function () {
| alert(this.data("i"));
| });
| }
\*/
elproto.data = function (key, value) {
var data = eldata[this.id] = eldata[this.id] || {};
if (arguments.length == 0){
eve("snap.data.get." + this.id, this, data, null);
return data;
}
if (arguments.length == 1) {
if (Snap.is(key, "object")) {
for (var i in key) if (key[has](i)) {
this.data(i, key[i]);
}
return this;
}
eve("snap.data.get." + this.id, this, data[key], key);
return data[key];
}
data[key] = value;
eve("snap.data.set." + this.id, this, value, key);
return this;
};
/*\
* Element.removeData
[ method ]
**
* Removes value associated with an element by given key.
* If key is not provided, removes all the data of the element.
- key (string) #optional key
= (object) @Element
\*/
elproto.removeData = function (key) {
if (key == null) {
eldata[this.id] = {};
} else {
eldata[this.id] && delete eldata[this.id][key];
}
return this;
};
/*\
* Element.outerSVG
[ method ]
**
* Returns SVG code for the element, equivalent to HTML's `outerHTML`.
*
* See also @Element.innerSVG
= (string) SVG code for the element
\*/
/*\
* Element.toString
[ method ]
**
* See @Element.outerSVG
\*/
elproto.outerSVG = elproto.toString = toString(1);
/*\
* Element.innerSVG
[ method ]
**
* Returns SVG code for the element's contents, equivalent to HTML's `innerHTML`
= (string) SVG code for the element
\*/
elproto.innerSVG = toString();
function toString(type) {
return function () {
var res = type ? "<" + this.type : "",
attr = this.node.attributes,
chld = this.node.childNodes;
if (type) {
for (var i = 0, ii = attr.length; i < ii; i++) {
res += " " + attr[i].name + '="' +
attr[i].value.replace(/"/g, '\\"') + '"';
}
}
if (chld.length) {
type && (res += ">");
for (i = 0, ii = chld.length; i < ii; i++) {
if (chld[i].nodeType == 3) {
res += chld[i].nodeValue;
} else if (chld[i].nodeType == 1) {
res += wrap(chld[i]).toString();
}
}
type && (res += "</" + this.type + ">");
} else {
type && (res += "/>");
}
return res;
};
}
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)));
}
};
/*\
* Fragment.select
[ method ]
**
* See @Element.select
\*/
Fragment.prototype.select = elproto.select;
/*\
* Fragment.selectAll
[ method ]
**
* See @Element.selectAll
\*/
Fragment.prototype.selectAll = elproto.selectAll;
});

View File

@ -18,7 +18,6 @@ Snap.plugin(function (Snap, Element, Paper, glob) {
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 ]

View File

@ -685,6 +685,18 @@ Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
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 ]

View File

@ -303,6 +303,7 @@ Snap.plugin(function (Snap, Element, Paper, glob) {
};
setproto.type = "set";
// export
Snap.Set = Set;
Snap.set = function () {
var set = new Set;
if (arguments.length) {

View File

@ -942,38 +942,6 @@ function transform2matrix(tstr, bbox) {
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 Snap.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) {
@ -1214,7 +1182,6 @@ function Element(el) {
}
}
}
(function (elproto) {
/*\
* Element.attr
[ method ]
@ -1240,7 +1207,7 @@ function Element(el) {
* (`+`, `-`, `*` 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) {
@ -1262,910 +1229,6 @@ function Element(el) {
}
return el;
};
/*\
* Element.getBBox
[ method ]
**
* Returns the bounding box descriptor for the given element
**
= (object) bounding box descriptor:
o {
o cx: (number) x of the center,
o cy: (number) x of the center,
o h: (number) height,
o height: (number) height,
o path: (string) path command for the box,
o r0: (number) radius of a circle that fully encloses the box,
o r1: (number) radius of the smallest circle that can be enclosed,
o r2: (number) radius of the largest circle that can be enclosed,
o vb: (string) box as a viewbox command,
o w: (number) width,
o width: (number) width,
o x2: (number) x of the right side,
o x: (number) x of the left side,
o y2: (number) y of the bottom edge,
o y: (number) y of the top edge
o }
\*/
elproto.getBBox = function (isWithoutTransform) {
if (!Snap.Matrix || !Snap.path) {
return this.node.getBBox();
}
var el = this,
m = new Snap.Matrix;
if (el.removed) {
return Snap._.box();
}
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));
}
}
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 doesnt give you bbox of hidden element
return Snap._.box();
}
};
var propString = function () {
return this.string;
};
/*\
* Element.transform
[ method ]
**
* Gets or sets transformation of the element
**
- tstr (string) transform string in Snap or SVG format
= (Element) the current element
* or
= (object) transformation descriptor:
o {
o string (string) transform string,
o globalMatrix (Matrix) matrix of all transformations applied to element or its parents,
o localMatrix (Matrix) matrix of transformations applied only to the element,
o diffMatrix (Matrix) matrix of difference between global and local transformations,
o global (string) global transformation as string,
o local (string) local transformation as string,
o toString (function) returns `string` property
o }
\*/
elproto.transform = function (tstr) {
var _ = this._;
if (tstr == null) {
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) ?
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 Snap.Matrix) {
this.matrix = tstr;
} else {
extractTransform(this, tstr);
}
if (this.node) {
if (this.type == "linearGradient" || this.type == "radialGradient") {
$(this.node, {gradientTransform: this.matrix});
} else if (this.type == "pattern") {
$(this.node, {patternTransform: this.matrix});
} else {
$(this.node, {transform: this.matrix});
}
}
return this;
};
/*\
* Element.parent
[ method ]
**
* Returns the element's parent
**
= (Element) the parent element
\*/
elproto.parent = function () {
return wrap(this.node.parentNode);
};
/*\
* Element.append
[ method ]
**
* Appends the given element to current one
**
- el (Element|Set) element to append
= (Element) the parent element
\*/
/*\
* Element.add
[ method ]
**
* See @Element.append
\*/
elproto.append = elproto.add = function (el) {
if (el) {
if (el.type == "set") {
var it = this;
el.forEach(function (el) {
it.add(el);
});
return this;
}
el = wrap(el);
this.node.appendChild(el.node);
el.paper = this.paper;
}
return this;
};
/*\
* Element.appendTo
[ method ]
**
* Appends the current element to the given one
**
- el (Element) parent element to append to
= (Element) the child element
\*/
elproto.appendTo = function (el) {
if (el) {
el = wrap(el);
el.append(this);
}
return this;
};
/*\
* Element.prepend
[ method ]
**
* Prepends the given element to the current one
**
- 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;
this.parent() && this.parent().add();
parent && parent.add();
}
return this;
};
/*\
* Element.prependTo
[ method ]
**
* Prepends the current element to the given one
**
- el (Element) parent element to prepend to
= (Element) the child element
\*/
elproto.prependTo = function (el) {
el = wrap(el);
el.prepend(this);
return this;
};
/*\
* Element.before
[ method ]
**
* Inserts given element before the current one
**
- el (Element) element to insert
= (Element) the parent element
\*/
elproto.before = function (el) {
if (el.type == "set") {
var it = this;
el.forEach(function (el) {
var parent = el.parent();
it.node.parentNode.insertBefore(el.node, it.node);
parent && parent.add();
});
this.parent().add();
return this;
}
el = wrap(el);
var parent = el.parent();
this.node.parentNode.insertBefore(el.node, this.node);
this.parent() && this.parent().add();
parent && parent.add();
el.paper = this.paper;
return this;
};
/*\
* Element.after
[ method ]
**
* Inserts given element after the current one
**
- el (Element) element to insert
= (Element) the parent element
\*/
elproto.after = function (el) {
el = wrap(el);
var parent = el.parent();
if (this.node.nextSibling) {
this.node.parentNode.insertBefore(el.node, this.node.nextSibling);
} else {
this.node.parentNode.appendChild(el.node);
}
this.parent() && this.parent().add();
parent && parent.add();
el.paper = this.paper;
return this;
};
/*\
* Element.insertBefore
[ method ]
**
* Inserts the element after the given one
**
- el (Element) element next to whom insert to
= (Element) the parent element
\*/
elproto.insertBefore = function (el) {
el = wrap(el);
var parent = this.parent();
el.node.parentNode.insertBefore(this.node, el.node);
this.paper = el.paper;
parent && parent.add();
el.parent() && el.parent().add();
return this;
};
/*\
* Element.insertAfter
[ method ]
**
* Inserts the element after the given one
**
- el (Element) element next to whom insert to
= (Element) the parent element
\*/
elproto.insertAfter = function (el) {
el = wrap(el);
var parent = this.parent();
el.node.parentNode.insertBefore(this.node, el.node.nextSibling);
this.paper = el.paper;
parent && parent.add();
el.parent() && el.parent().add();
return this;
};
/*\
* Element.remove
[ method ]
**
* Removes element from the DOM
= (Element) the detached element
\*/
elproto.remove = function () {
var parent = this.parent();
this.node.parentNode && this.node.parentNode.removeChild(this.node);
delete this.paper;
this.removed = true;
parent && parent.add();
return this;
};
/*\
* Element.select
[ method ]
**
* Gathers the nested @Element matching the given set of CSS selectors
**
- query (string) CSS selector
= (Element) result of query selection
\*/
elproto.select = function (query) {
query = Str(query).replace(/([^\\]):/g, "$1\\:");
return wrap(this.node.querySelector(query));
};
/*\
* Element.selectAll
[ method ]
**
* Gathers nested @Element objects matching the given set of CSS selectors
**
- query (string) CSS selector
= (Set|array) result of query selection
\*/
elproto.selectAll = function (query) {
var nodelist = this.node.querySelectorAll(query),
set = (Snap.set || Array)();
for (var i = 0; i < nodelist.length; i++) {
set.push(wrap(nodelist[i]));
}
return set;
};
/*\
* Element.asPX
[ method ]
**
* Returns given attribute of the element as a `px` value (not %, em, etc.)
**
- attr (string) attribute name
- value (string) #optional attribute value
= (Element) result of query selection
\*/
elproto.asPX = function (attr, value) {
if (value == null) {
value = this.attr(attr);
}
return +unit2px(this, attr, value);
};
// SIERRA Element.use(): I suggest adding a note about how to access the original element the returned <use> instantiates. It's a part of SVG with which ordinary web developers may be least familiar.
/*\
* Element.use
[ method ]
**
* Creates a `<use>` element linked to the current element
**
= (Element) the `<use>` element
\*/
elproto.use = function () {
var use,
id = this.node.id;
if (!id) {
id = this.id;
$(this.node, {
id: id
});
}
if (this.type == "linearGradient" || this.type == "radialGradient" ||
this.type == "pattern") {
use = make(this.type, this.node.parentNode);
} else {
use = make("use", this.node.parentNode);
}
$(use.node, {
"xlink:href": "#" + id
});
use.original = this;
return use;
};
function fixids(el) {
var els = el.selectAll("*"),
it,
url = /^\s*url\(("|'|)(.*)\1\)\s*$/,
ids = [],
uses = {};
function urltest(it, name) {
var val = $(it.node, name);
val = val && val.match(url);
val = val && val[2];
if (val && val.charAt() == "#") {
val = val.substring(1);
} else {
return;
}
if (val) {
uses[val] = (uses[val] || []).concat(function (id) {
var attr = {};
attr[name] = URL(id);
$(it.node, attr);
});
}
}
function linktest(it) {
var val = $(it.node, "xlink:href");
if (val && val.charAt() == "#") {
val = val.substring(1);
} else {
return;
}
if (val) {
uses[val] = (uses[val] || []).concat(function (id) {
it.attr("xlink:href", "#" + id);
});
}
}
for (var i = 0, ii = els.length; i < ii; i++) {
it = els[i];
urltest(it, "fill");
urltest(it, "stroke");
urltest(it, "filter");
urltest(it, "mask");
urltest(it, "clip-path");
linktest(it);
var oldid = $(it.node, "id");
if (oldid) {
$(it.node, {id: it.id});
ids.push({
old: oldid,
id: it.id
});
}
}
for (i = 0, ii = ids.length; i < ii; i++) {
var fs = uses[ids[i].old];
if (fs) {
for (var j = 0, jj = fs.length; j < jj; j++) {
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;
};
/*\
* Element.toDefs
[ method ]
**
* Moves element to the shared `<defs>` area
**
= (Element) the element
\*/
elproto.toDefs = function () {
var defs = getSomeDefs(this);
defs.appendChild(this.node);
return this;
};
/*\
* Element.pattern
[ method ]
**
* Depricated. Use @Element.toPattern instead.
\*/
/*\
* Element.toPattern
[ method ]
**
* Creates a `<pattern>` element from the current element
**
* To create a pattern you have to specify the pattern rect:
- x (string|number)
- y (string|number)
- width (string|number)
- height (string|number)
= (Element) the `<pattern>` element
* You can use pattern later on as an argument for `fill` attribute:
| var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
| fill: "none",
| stroke: "#bada55",
| strokeWidth: 5
| }).pattern(0, 0, 10, 10),
| c = paper.circle(200, 200, 100);
| c.attr({
| fill: p
| });
\*/
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) {
y = x.y;
width = x.width;
height = x.height;
x = x.x;
}
$(p.node, {
x: x,
y: y,
width: width,
height: height,
patternUnits: "userSpaceOnUse",
id: p.id,
viewBox: [x, y, width, height].join(" ")
});
p.node.appendChild(this.node);
return p;
};
// SIERRA Element.marker(): clarify what a reference point is. E.g., helps you offset the object from its edge such as when centering it over a path.
// SIERRA Element.marker(): I suggest the method should accept default reference point values. Perhaps centered with (refX = width/2) and (refY = height/2)? Also, couldn't it assume the element's current _width_ and _height_? And please specify what _x_ and _y_ mean: offsets? If so, from where? Couldn't they also be assigned default values?
/*\
* Element.marker
[ method ]
**
* Creates a `<marker>` element from the current element
**
* To create a marker you have to specify the bounding rect and reference point:
- x (number)
- y (number)
- width (number)
- height (number)
- refX (number)
- refY (number)
= (Element) the `<marker>` element
* You can specify the marker later as an argument for `marker-start`, `marker-end`, `marker-mid`, and `marker` attributes. The `marker` attribute places the marker at every point along the path, and `marker-mid` places them at every point except the start and end.
\*/
// TODO add usage for markers
elproto.marker = function (x, y, width, height, refX, refY) {
var p = make("marker", getSomeDefs(this));
if (x == null) {
x = this.getBBox();
}
if (is(x, "object") && "x" in x) {
y = x.y;
width = x.width;
height = x.height;
refX = x.refX || x.cx;
refY = x.refY || x.cy;
x = x.x;
}
$(p.node, {
viewBox: [x, y, width, height].join(S),
markerWidth: width,
markerHeight: height,
orient: "auto",
refX: refX || 0,
refY: refY || 0,
id: p.id
});
p.node.appendChild(this.node);
return p;
};
// animation
function slice(from, to, f) {
return function (arr) {
var res = arr.slice(from, to);
if (res.length == 1) {
res = res[0];
}
return f ? f(res) : res;
};
}
var Animation = function (attr, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
this.attr = attr;
this.dur = ms;
easing && (this.easing = easing);
callback && (this.callback = callback);
};
Snap._.Animation = Animation;
/*\
* Snap.animation
[ method ]
**
* Creates an animation object
**
- attr (object) attributes of final destination
- duration (number) duration of the animation, in milliseconds
- easing (function) #optional one of easing functions of @mina or custom one
- callback (function) #optional callback function that fires when animation ends
= (object) animation object
\*/
Snap.animation = function (attr, ms, easing, callback) {
return new Animation(attr, ms, easing, callback);
};
/*\
* Element.inAnim
[ method ]
**
* 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 }
\*/
elproto.inAnim = function () {
var el = this,
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 () {
a.stop();
}
});
}(el.anims[id]));
}
return res;
};
/*\
* Snap.animate
[ method ]
**
* Runs generic animation of one number into another with a caring function
**
- from (number|array) number or array of numbers
- to (number|array) number or array of numbers
- setter (function) caring function that accepts one number argument
- duration (number) duration, in milliseconds
- easing (function) #optional easing function from @mina or custom
- callback (function) #optional callback function to execute when animation ends
= (object) animation object in @mina format
o {
o id (string) animation id, consider it read-only,
o duration (function) gets or sets the duration of the animation,
o easing (function) easing,
o speed (function) gets or sets the speed of the animation,
o status (function) gets or sets the status of the animation,
o stop (function) stops the animation
o }
| var rect = Snap().rect(0, 0, 10, 10);
| Snap.animate(0, 10, function (val) {
| rect.attr({
| x: val
| });
| }, 1000);
| // in given context is equivalent to
| rect.animate({x: 10}, 1000);
\*/
Snap.animate = function (from, to, setter, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
var now = mina.time(),
anim = mina(from, to, now, now + ms, mina.time, setter, easing);
callback && eve.once("mina.finish." + anim.id, callback);
return anim;
};
/*\
* Element.stop
[ method ]
**
* Stops all the animations for the current element
**
= (Element) the current element
\*/
elproto.stop = function () {
var anims = this.inAnim();
for (var i = 0, ii = anims.length; i < ii; i++) {
anims[i].stop();
}
return this;
};
/*\
* Element.animate
[ method ]
**
* Animates the given attributes of the element
**
- 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
= (Element) the current element
\*/
elproto.animate = function (attrs, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
if (attrs instanceof Animation) {
callback = attrs.callback;
easing = attrs.easing;
ms = easing.dur;
attrs = attrs.attr;
}
var fkeys = [], tkeys = [], keys = {}, from, to, f, eq,
el = this;
for (var key in attrs) if (attrs[has](key)) {
if (el.equal) {
eq = el.equal(key, Str(attrs[key]));
from = eq.from;
to = eq.to;
f = eq.f;
} else {
from = +el.attr(key);
to = +attrs[key];
}
var len = is(from, "array") ? from.length : 1;
keys[key] = slice(fkeys.length, fkeys.length + len, f);
fkeys = fkeys.concat(from);
tkeys = tkeys.concat(to);
}
var now = mina.time(),
anim = mina(fkeys, tkeys, now, now + ms, mina.time, function (val) {
var attr = {};
for (var key in keys) if (keys[has](key)) {
attr[key] = keys[key](val);
}
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 () {
delete el.anims[anim.id];
});
return el;
};
var eldata = {};
/*\
* Element.data
[ method ]
**
* Adds or retrieves given value associated with given key. (Dont confuse
* with `data-` attributes)
*
* See also @Element.removeData
- key (string) key to store data
- value (any) #optional value to store
= (object) @Element
* or, if value is not specified:
= (any) value
> Usage
| for (var i = 0, i < 5, i++) {
| paper.circle(10 + 15 * i, 10, 10)
| .attr({fill: "#000"})
| .data("i", i)
| .click(function () {
| alert(this.data("i"));
| });
| }
\*/
elproto.data = function (key, value) {
var data = eldata[this.id] = eldata[this.id] || {};
if (arguments.length == 0){
eve("snap.data.get." + this.id, this, data, null);
return data;
}
if (arguments.length == 1) {
if (Snap.is(key, "object")) {
for (var i in key) if (key[has](i)) {
this.data(i, key[i]);
}
return this;
}
eve("snap.data.get." + this.id, this, data[key], key);
return data[key];
}
data[key] = value;
eve("snap.data.set." + this.id, this, value, key);
return this;
};
/*\
* Element.removeData
[ method ]
**
* Removes value associated with an element by given key.
* If key is not provided, removes all the data of the element.
- key (string) #optional key
= (object) @Element
\*/
elproto.removeData = function (key) {
if (key == null) {
eldata[this.id] = {};
} else {
eldata[this.id] && delete eldata[this.id][key];
}
return this;
};
/*\
* Element.outerSVG
[ method ]
**
* Returns SVG code for the element, equivalent to HTML's `outerHTML`.
*
* See also @Element.innerSVG
= (string) SVG code for the element
\*/
/*\
* Element.toString
[ method ]
**
* See @Element.outerSVG
\*/
elproto.outerSVG = elproto.toString = toString(1);
/*\
* Element.innerSVG
[ method ]
**
* Returns SVG code for the element's contents, equivalent to HTML's `innerHTML`
= (string) SVG code for the element
\*/
elproto.innerSVG = toString();
function toString(type) {
return function () {
var res = type ? "<" + this.type : "",
attr = this.node.attributes,
chld = this.node.childNodes;
if (type) {
for (var i = 0, ii = attr.length; i < ii; i++) {
res += " " + attr[i].name + '="' +
attr[i].value.replace(/"/g, '\\"') + '"';
}
}
if (chld.length) {
type && (res += ">");
for (i = 0, ii = chld.length; i < ii; i++) {
if (chld[i].nodeType == 3) {
res += chld[i].nodeValue;
} else if (chld[i].nodeType == 1) {
res += wrap(chld[i]).toString();
}
}
type && (res += "</" + this.type + ">");
} else {
type && (res += "/>");
}
return res;
};
}
}(Element.prototype));
/*\
* Snap.parse
[ method ]
@ -2201,20 +1264,6 @@ Snap.parse = function (svg) {
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