master
yutent 2024-03-05 13:02:24 +08:00
commit d57e58a63e
25 changed files with 9670 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
dist
node_modules
._*
.Spotlight-V100
.Trashes
.DS_Store
.AppleDouble
.LSOverride

9
.gitmodules vendored Normal file
View File

@ -0,0 +1,9 @@
[submodule "third-party/mocha"]
path = third-party/mocha
url = https://github.com/visionmedia/mocha.git
[submodule "third-party/eve"]
path = third-party/eve
url = https://github.com/adobe-webplatform/eve.git
[submodule "third-party/expect"]
path = third-party/expect
url = https://github.com/LearnBoost/expect.js.git

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2013 Adobe Systems Incorporated
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.

190
NOTICE Normal file
View File

@ -0,0 +1,190 @@
Snap.svg is licensed under the Apache license version 2.0, January 2004 (see LICENSE file).
Snap.svg uses the following third party libraries that may have licenses
differing from that of Snap.svg itself. You can find the libraries and their
respective licenses below.
- eve ./node_modules/eve
https://github.com/adobe-webplatform/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
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.
- Mocha ./node_modules/mocha
https://github.com/visionmedia/mocha/
(The MIT License)
Copyright (c) 2011-2013 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- Expect ./node_modules/expect.js
https://github.com/LearnBoost/expect.js
(The MIT License)
Copyright (c) 2011 Guillermo Rauch <guillermo@learnboost.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- Grunt ./node_modules/grunt
http://gruntjs.com
Copyright (c) 2013 "Cowboy" Ben Alman
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
- Backbone ./demos/animated-game/js/backbone.js
http://backbonejs.org/
(The MIT License)
Copyright (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- Underscore ./demos/animated-game/js/underscore.js
http://underscorejs.org
(The MIT License)
Copyright (c) 2010-2013 Jeremy Ashkenas, DocumentCloud
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- jQuery ./demos/animated-game/js/jquery-1.9.0.min.js
http://http://jquery.com/
(The MIT License)
Copyright 2013 jQuery Foundation and other contributors
http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

5
README.md Normal file
View File

@ -0,0 +1,5 @@
# @bytedo/snapsvg
一个功能强大的SVG库, fork于 [【Snap.svg】](https://github.com/adobe-webplatform/Snap.svg)。
原库已经年久失修, 有不少bug。

66
history.md Normal file
View File

@ -0,0 +1,66 @@
#0.5.2
* pack fix
* transform into esm
#0.5.1
* Bug fix
#0.5.0
* Added color palettes for Material and FlatUI
* Added methods for gradients: `Element.stops()`, `Element.addStop()`, `Element.setStops()`
* Fixed matrix splitting for better animation of matrices`
* Various bug fixes
* Better integration of tests and ESlint
#0.4.1
* Bug fixes.
#0.4.0
* Moved class and element related code into separate plugins
* Added `Element.align()` and `Element.getAlign()` methods
* Added animation support for `viewBox`
* Added support for `<symbol>`
* Added method `Paper.toDataURL()`
* Added method `Snap.closest()`
* Added methods to work with degrees instead of radians: `Snap.sin()`, `Snap.cos()`, `Snap.tan()`, `Snap.asin()`, `Snap.acos()`, `Snap.atan()` and `Snap.atan2()`
* Added methods `Snap.len()`, `Snap.len2()` and `Snap.closestPoint()`
* Added methods `Element.children()` and `Element.toJSON()`
* Various bug fixes
#0.3.0
* Added `.addClass()`, `.removeClass()`, `.toggleClass()` and `.hasClass()` APIs
* Added `Paper.mask()`, `Paper.ptrn()`, `Paper.use()`, `Paper.svg()`
* Mask & pattern elements are sharing paper methods (just like group)
* Added `Set.bind()` method
* Added syncronisation for `Set.animate()`
* Added opacity to the shadow filter
* Added ability to specify attributes as `"+=10"` or `"-=1em"` or `"*=2"`
* Fix negative scale
* Fix for `path2curve`
* Fixed shared `<defs>` issue
* Various bug fixes
#0.2.0
* Added support for text path
* Added `getBBox` method to the paper object
* Added `Element.appendTo()` and `Element.prependTo()`
* Added `getElementByPoint()`
* Added `Set.remove()` method
* Get rid of internal SVG parser in favor of the browser
* Fix for `xlink:href` setting for images
* Fix `Element.animate()`
* Fix for animate and stroke-dashoffset
* Absolute transforms fix
* Fix for animation of SVG transformations, matrices and polygon points
* Various bug fixes
#0.1.0
* Initial release

25
package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "@bytedo/snapsvg",
"type": "module",
"version": "0.5.2",
"description": "JavaScript Vector Library",
"repository": {
"type": "git",
"url": "git@git.wkit.fun:bytedo/snapsvg.git"
},
"scripts": {
"start": "esbuild src/index.js --watch --bundle --format=esm --target=esnext --outfile=dist/index.js",
"pack": "esbuild src/index.js --minify --bundle --format=esm --target=esnext --outfile=dist/index.js"
},
"author": "Dmitry Baranovskiy",
"contributors": [
{
"name": "yutent",
"email": "yutent.io@gmail.com"
}
],
"license": "Apache-2.0",
"devDependencies": {
"esbuild": "^0.20.1"
}
}

94
src/align.js Normal file
View File

@ -0,0 +1,94 @@
// 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.
import { Snap } from './svg.js'
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))
}
})

229
src/animation.js Normal file
View File

@ -0,0 +1,229 @@
// Copyright (c) 2016 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.
import eve from './eve.js'
import { Snap } from './svg.js'
import mina from './mina.js'
Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
var elproto = Element.prototype,
is = Snap.is,
Str = String,
has = 'hasOwnProperty'
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 = attrs.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 () {
eve.off('mina.*.' + anim.id)
delete el.anims[anim.id]
callback && callback.call(el)
})
eve.once('mina.stop.' + anim.id, function () {
eve.off('mina.*.' + anim.id)
delete el.anims[anim.id]
})
return el
}
})

480
src/attr.js Normal file
View File

@ -0,0 +1,480 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
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\((['"]?)([^)]+)\1\)$/,
$ = Snap._.$,
URL = Snap.url,
Str = String,
separator = Snap._.separator,
E = ''
/*\
* Snap.deurl
[ method ]
**
* Unwraps path from `"url(<path>)"`.
- value (string) url path
= (string) unwrapped path
\*/
Snap.deurl = function (value) {
var res = String(value).match(reURLValue)
return res ? res[2] : value
}
// 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()
var clip,
node = value.node
while (node) {
if (node.nodeName === 'clipPath') {
clip = new Element(node)
break
}
if (node.nodeName === 'svg') {
clip = undefined
break
}
node = node.parentNode
}
if (!clip) {
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)
})
}
})
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
}
}
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 = parseFloat(el[1])
}
return out
})
var 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)
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
}
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
}
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.fill', function (internal) {
if (internal) {
return
}
eve.stop()
var value = eve('snap.util.getattr.fill', this, true).firstDefined()
return Snap(Snap.deurl(value)) || value
})(-1)
eve.on('snap.util.getattr.stroke', function (internal) {
if (internal) {
return
}
eve.stop()
var value = eve('snap.util.getattr.stroke', this, true).firstDefined()
return Snap(Snap.deurl(value)) || value
})(-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)
})

94
src/attradd.js Normal file
View File

@ -0,0 +1,94 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
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)
})

148
src/class.js Normal file
View File

@ -0,0 +1,148 @@
// 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.
import { Snap } from './svg.js'
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
/*\
* Element.addClass
[ method ]
**
* Adds given class name or list of class names to the element.
- value (string) class name or space separated list of class names
**
= (Element) original element.
\*/
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 classs 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
}
})

149
src/colors.js Normal file
View File

@ -0,0 +1,149 @@
// Copyright (c) 2017 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.
import { Snap } from './svg.js'
Snap.plugin(function (Snap, Element, Paper, glob) {
// Colours are from https://www.materialui.co
var red =
'#ffebee#ffcdd2#ef9a9a#e57373#ef5350#f44336#e53935#d32f2f#c62828#b71c1c#ff8a80#ff5252#ff1744#d50000',
pink =
'#FCE4EC#F8BBD0#F48FB1#F06292#EC407A#E91E63#D81B60#C2185B#AD1457#880E4F#FF80AB#FF4081#F50057#C51162',
purple =
'#F3E5F5#E1BEE7#CE93D8#BA68C8#AB47BC#9C27B0#8E24AA#7B1FA2#6A1B9A#4A148C#EA80FC#E040FB#D500F9#AA00FF',
deeppurple =
'#EDE7F6#D1C4E9#B39DDB#9575CD#7E57C2#673AB7#5E35B1#512DA8#4527A0#311B92#B388FF#7C4DFF#651FFF#6200EA',
indigo =
'#E8EAF6#C5CAE9#9FA8DA#7986CB#5C6BC0#3F51B5#3949AB#303F9F#283593#1A237E#8C9EFF#536DFE#3D5AFE#304FFE',
blue =
'#E3F2FD#BBDEFB#90CAF9#64B5F6#64B5F6#2196F3#1E88E5#1976D2#1565C0#0D47A1#82B1FF#448AFF#2979FF#2962FF',
lightblue =
'#E1F5FE#B3E5FC#81D4FA#4FC3F7#29B6F6#03A9F4#039BE5#0288D1#0277BD#01579B#80D8FF#40C4FF#00B0FF#0091EA',
cyan =
'#E0F7FA#B2EBF2#80DEEA#4DD0E1#26C6DA#00BCD4#00ACC1#0097A7#00838F#006064#84FFFF#18FFFF#00E5FF#00B8D4',
teal =
'#E0F2F1#B2DFDB#80CBC4#4DB6AC#26A69A#009688#00897B#00796B#00695C#004D40#A7FFEB#64FFDA#1DE9B6#00BFA5',
green =
'#E8F5E9#C8E6C9#A5D6A7#81C784#66BB6A#4CAF50#43A047#388E3C#2E7D32#1B5E20#B9F6CA#69F0AE#00E676#00C853',
lightgreen =
'#F1F8E9#DCEDC8#C5E1A5#AED581#9CCC65#8BC34A#7CB342#689F38#558B2F#33691E#CCFF90#B2FF59#76FF03#64DD17',
lime =
'#F9FBE7#F0F4C3#E6EE9C#DCE775#D4E157#CDDC39#C0CA33#AFB42B#9E9D24#827717#F4FF81#EEFF41#C6FF00#AEEA00',
yellow =
'#FFFDE7#FFF9C4#FFF59D#FFF176#FFEE58#FFEB3B#FDD835#FBC02D#F9A825#F57F17#FFFF8D#FFFF00#FFEA00#FFD600',
amber =
'#FFF8E1#FFECB3#FFE082#FFD54F#FFCA28#FFC107#FFB300#FFA000#FF8F00#FF6F00#FFE57F#FFD740#FFC400#FFAB00',
orange =
'#FFF3E0#FFE0B2#FFCC80#FFB74D#FFA726#FF9800#FB8C00#F57C00#EF6C00#E65100#FFD180#FFAB40#FF9100#FF6D00',
deeporange =
'#FBE9E7#FFCCBC#FFAB91#FF8A65#FF7043#FF5722#F4511E#E64A19#D84315#BF360C#FF9E80#FF6E40#FF3D00#DD2C00',
brown =
'#EFEBE9#D7CCC8#BCAAA4#A1887F#8D6E63#795548#6D4C41#5D4037#4E342E#3E2723',
grey =
'#FAFAFA#F5F5F5#EEEEEE#E0E0E0#BDBDBD#9E9E9E#757575#616161#424242#212121',
bluegrey =
'#ECEFF1#CFD8DC#B0BEC5#90A4AE#78909C#607D8B#546E7A#455A64#37474F#263238'
/*\
* Snap.mui
[ property ]
**
* Contain Material UI colours.
| Snap().rect(0, 0, 10, 10).attr({fill: Snap.mui.deeppurple, stroke: Snap.mui.amber[600]});
# For colour reference: <a href="https://www.materialui.co">https://www.materialui.co</a>.
\*/
Snap.mui = {}
/*\
* Snap.flat
[ property ]
**
* Contain Flat UI colours.
| Snap().rect(0, 0, 10, 10).attr({fill: Snap.flat.carrot, stroke: Snap.flat.wetasphalt});
# For colour reference: <a href="https://www.materialui.co">https://www.materialui.co</a>.
\*/
Snap.flat = {}
function saveColor(colors) {
colors = colors.split(/(?=#)/)
var color = new String(colors[5])
color[50] = colors[0]
color[100] = colors[1]
color[200] = colors[2]
color[300] = colors[3]
color[400] = colors[4]
color[500] = colors[5]
color[600] = colors[6]
color[700] = colors[7]
color[800] = colors[8]
color[900] = colors[9]
if (colors[10]) {
color.A100 = colors[10]
color.A200 = colors[11]
color.A400 = colors[12]
color.A700 = colors[13]
}
return color
}
Snap.mui.red = saveColor(red)
Snap.mui.pink = saveColor(pink)
Snap.mui.purple = saveColor(purple)
Snap.mui.deeppurple = saveColor(deeppurple)
Snap.mui.indigo = saveColor(indigo)
Snap.mui.blue = saveColor(blue)
Snap.mui.lightblue = saveColor(lightblue)
Snap.mui.cyan = saveColor(cyan)
Snap.mui.teal = saveColor(teal)
Snap.mui.green = saveColor(green)
Snap.mui.lightgreen = saveColor(lightgreen)
Snap.mui.lime = saveColor(lime)
Snap.mui.yellow = saveColor(yellow)
Snap.mui.amber = saveColor(amber)
Snap.mui.orange = saveColor(orange)
Snap.mui.deeporange = saveColor(deeporange)
Snap.mui.brown = saveColor(brown)
Snap.mui.grey = saveColor(grey)
Snap.mui.bluegrey = saveColor(bluegrey)
Snap.flat.turquoise = '#1abc9c'
Snap.flat.greensea = '#16a085'
Snap.flat.sunflower = '#f1c40f'
Snap.flat.orange = '#f39c12'
Snap.flat.emerland = '#2ecc71'
Snap.flat.nephritis = '#27ae60'
Snap.flat.carrot = '#e67e22'
Snap.flat.pumpkin = '#d35400'
Snap.flat.peterriver = '#3498db'
Snap.flat.belizehole = '#2980b9'
Snap.flat.alizarin = '#e74c3c'
Snap.flat.pomegranate = '#c0392b'
Snap.flat.amethyst = '#9b59b6'
Snap.flat.wisteria = '#8e44ad'
Snap.flat.clouds = '#ecf0f1'
Snap.flat.silver = '#bdc3c7'
Snap.flat.wetasphalt = '#34495e'
Snap.flat.midnightblue = '#2c3e50'
Snap.flat.concrete = '#95a5a6'
Snap.flat.asbestos = '#7f8c8d'
/*\
* Snap.importMUIColors
[ method ]
**
* Imports Material UI colours into global object.
| Snap.importMUIColors();
| Snap().rect(0, 0, 10, 10).attr({fill: deeppurple, stroke: amber[600]});
# For colour reference: <a href="https://www.materialui.co">https://www.materialui.co</a>.
\*/
Snap.importMUIColors = function () {
for (var color in Snap.mui) {
if (Snap.mui.hasOwnProperty(color)) {
window[color] = Snap.mui[color]
}
}
}
})

815
src/element.js Normal file
View File

@ -0,0 +1,815 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
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 (this.type == 'tspan') {
return Snap._.box(this.node.getClientRects().item(0))
}
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 || '')
}
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
this._.transform = tstr.toTransformString()
} 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) {
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] = Snap.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.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
}
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
})

227
src/equal.js Normal file
View File

@ -0,0 +1,227 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
Snap.plugin(function (Snap, Element, Paper, glob) {
var names = {},
reUnit = /[%a-z]+$/i,
Str = String
names.stroke = names.fill = 'colour'
function getEmpty(item) {
var l = item[0]
switch (l.toLowerCase()) {
case 't':
return [l, 0, 0]
case 'm':
return [l, 1, 0, 0, 1, 0, 0]
case 'r':
if (item.length == 4) {
return [l, 0, item[2], item[3]]
} else {
return [l, 0]
}
case 's':
if (item.length == 5) {
return [l, 1, 1, item[3], item[4]]
} else if (item.length == 3) {
return [l, 1, 1]
} else {
return [l, 1]
}
}
}
function equaliseTransform(t1, t2, getBBox) {
t1 = t1 || new Snap.Matrix()
t2 = t2 || new Snap.Matrix()
t1 = Snap.parseTransformString(t1.toTransformString()) || []
t2 = Snap.parseTransformString(t2.toTransformString()) || []
var maxlength = Math.max(t1.length, t2.length),
from = [],
to = [],
i = 0,
j,
jj,
tt1,
tt2
for (; i < maxlength; i++) {
tt1 = t1[i] || getEmpty(t2[i])
tt2 = t2[i] || getEmpty(tt1)
if (
tt1[0] != tt2[0] ||
(tt1[0].toLowerCase() == 'r' &&
(tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
(tt1[0].toLowerCase() == 's' && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
) {
t1 = Snap._.transform2matrix(t1, getBBox())
t2 = Snap._.transform2matrix(t2, getBBox())
from = [['m', t1.a, t1.b, t1.c, t1.d, t1.e, t1.f]]
to = [['m', t2.a, t2.b, t2.c, t2.d, t2.e, t2.f]]
break
}
from[i] = []
to[i] = []
for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) {
j in tt1 && (from[i][j] = tt1[j])
j in tt2 && (to[i][j] = tt2[j])
}
}
return {
from: path2array(from),
to: path2array(to),
f: getPath(from)
}
}
function getNumber(val) {
return val
}
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], clr[3])
}
function getPath(path) {
var k = 0,
i,
ii,
j,
jj,
out,
a,
b = []
for (i = 0, ii = path.length; i < ii; i++) {
out = '['
a = ['"' + path[i][0] + '"']
for (j = 1, jj = path[i].length; j < jj; j++) {
a[j] = 'val[' + k++ + ']'
}
out += a + ']'
b[i] = out
}
return Function('val', 'return Snap.path.toString.call([' + b + '])')
}
function path2array(path) {
var out = []
for (var i = 0, ii = path.length; i < ii; i++) {
for (var j = 1, jj = path[i].length; j < jj; j++) {
out.push(path[i][j])
}
}
return out
}
function isNumeric(obj) {
return isFinite(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 (names[name] == 'colour') {
A = Snap.color(a)
B = Snap.color(b)
return {
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 (typeof b == 'string') {
b = Str(b).replace(/\.{3}|\u2026/g, a)
}
a = this.matrix
if (!Snap._.rgTransform.test(b)) {
b = Snap._.transform2matrix(
Snap._.svgTransform2string(b),
this.getBBox()
)
} else {
b = Snap._.transform2matrix(b, this.getBBox())
}
return equaliseTransform(a, b, function () {
return el.getBBox(1)
})
}
if (name == 'd' || name == 'path') {
A = Snap.path.toCubic(a, b)
return {
from: path2array(A[0]),
to: path2array(A[1]),
f: getPath(A[0])
}
}
if (name == 'points') {
A = Str(a).split(Snap._.separator)
B = Str(b).split(Snap._.separator)
return {
from: A,
to: B,
f: function (val) {
return val
}
}
}
if (isNumeric(a) && isNumeric(b)) {
return {
from: parseFloat(a),
to: parseFloat(b),
f: getNumber
}
}
var aUnit = a.match(reUnit),
bUnit = Str(b).match(reUnit)
if (aUnit && arrayEqual(aUnit, bUnit)) {
return {
from: parseFloat(a),
to: parseFloat(b),
f: getUnit(aUnit)
}
} else {
return {
from: this.asPX(name),
to: this.asPX(name, b),
f: getNumber
}
}
})
})

458
src/eve.js Normal file
View File

@ -0,0 +1,458 @@
// Copyright (c) 2017 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.
// ┌────────────────────────────────────────────────────────────┐ \\
// │ Eve 0.5.4 - JavaScript Events Library │ \\
// ├────────────────────────────────────────────────────────────┤ \\
// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
// └────────────────────────────────────────────────────────────┘ \\
var version = '0.5.4',
has = 'hasOwnProperty',
separator = /[\.\/]/,
comaseparator = /\s*,\s*/,
wildcard = '*',
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]
}
}
},
objtos = Object.prototype.toString,
Str = String,
isArray =
Array.isArray ||
function (ar) {
return ar instanceof Array || objtos.call(ar) == '[object Array]'
},
/*\
* eve
[ method ]
* Fires event with given `name`, given scope and other parameters.
- 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. Array has two methods `.firstDefined()` and `.lastDefined()` to get first or last not `undefined` value.
\*/
eve = function (name, scope) {
var oldstop = stop,
args = Array.prototype.slice.call(arguments, 2),
listeners = eve.listeners(name),
z = 0,
l,
indexed = [],
queue = {},
out = [],
ce = current_event
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) {
queue[listeners[i].zIndex] = listeners[i]
}
}
indexed.sort(numsort)
while (indexed[z] < 0) {
l = queue[indexed[z++]]
out.push(l.apply(scope, args))
if (stop) {
stop = oldstop
return out
}
}
for (i = 0; i < ii; i++) {
l = listeners[i]
if ('zIndex' in l) {
if (l.zIndex == indexed[z]) {
out.push(l.apply(scope, args))
if (stop) {
break
}
do {
z++
l = queue[indexed[z]]
l && out.push(l.apply(scope, args))
if (stop) {
break
}
} while (l)
} else {
queue[l.zIndex] = l
}
} else {
out.push(l.apply(scope, args))
if (stop) {
break
}
}
}
stop = oldstop
current_event = ce
return out
}
// 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`.
- name (string) name of the event, dot (`.`) or slash (`/`) separated
= (array) array of event handlers
\*/
eve.listeners = function (name) {
var names = isArray(name) ? name : name.split(separator),
e = events,
item,
items,
k,
i,
ii,
j,
jj,
nes,
es = [e],
out = []
for (i = 0, ii = names.length; i < ii; i++) {
nes = []
for (j = 0, jj = es.length; j < jj; j++) {
e = es[j].n
items = [e[names[i]], e[wildcard]]
k = 2
while (k--) {
item = items[k]
if (item) {
nes.push(item)
out = out.concat(item.f || [])
}
}
}
es = nes
}
return out
}
/*\
* eve.separator
[ method ]
* If for some reasons you dont like default separators (`.` or `/`) you can specify yours
* here. Be aware that if you pass a string longer than one character it will be treated as
* a list of characters.
- separator (string) new separator. Empty string resets to default: `.` or `/`.
\*/
eve.separator = function (sep) {
if (sep) {
sep = Str(sep).replace(/(?=[\.\^\]\[\-])/g, '\\')
sep = '[' + sep + ']'
separator = new RegExp(sep)
} else {
separator = /[\.\/]/
}
}
/*\
* eve.on
[ method ]
**
* Binds given event handler with a given name. You can use wildcards `*` for the names:
| eve.on("*.under.*", f);
| eve("mouse.under.floor"); // triggers f
* Use @eve to trigger the listener.
**
- name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
- f (function) event handler function
**
- name (array) if you dont want to use separators, you can use array of strings
- f (function) event handler function
**
= (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`.
*
* If you want to put your handler before non-indexed handlers, specify a negative value.
* Note: I assume most of the time you dont need to worry about z-index, but its nice to have this feature just in case.
\*/
eve.on = function (name, f) {
if (typeof f != 'function') {
return function () {}
}
var names = isArray(name)
? isArray(name[0])
? name
: [name]
: Str(name).split(comaseparator)
for (var i = 0, ii = names.length; i < ii; i++) {
;(function (name) {
var names = isArray(name) ? name : Str(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])
}
return function (zIndex) {
if (+zIndex == +zIndex) {
f.zIndex = +zIndex
}
}
}
/*\
* 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]
| });
- 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.stop
[ method ]
**
* Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
\*/
eve.stop = function () {
stop = 1
}
/*\
* eve.nt
[ method ]
**
* Could be used inside event handler to figure out actual name of the event.
**
- subname (string) #optional subname of the event
**
= (string) name of the event, if `subname` is not specified
* or
= (boolean) `true`, if current events name contains `subname`
\*/
eve.nt = function (subname) {
var cur = isArray(current_event) ? current_event.join('.') : current_event
if (subname) {
return new RegExp('(?:\\.|\\/|^)' + subname + '(?:\\.|\\/|$)').test(cur)
}
return cur
}
/*\
* eve.nts
[ method ]
**
* Could be used inside event handler to figure out actual name of the event.
**
**
= (array) names of the event
\*/
eve.nts = function () {
return isArray(current_event) ? current_event : current_event.split(separator)
}
/*\
* 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.
**
- name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
- f (function) event handler function
\*/
/*\
* eve.unbind
[ method ]
**
* See @eve.off
\*/
eve.off = eve.unbind = function (name, f) {
if (!name) {
eve._events = events = { n: {} }
return
}
var names = isArray(name)
? isArray(name[0])
? name
: [name]
: Str(name).split(comaseparator)
if (names.length > 1) {
for (var i = 0, ii = names.length; i < ii; i++) {
eve.off(names[i], f)
}
return
}
names = isArray(name) ? name : Str(name).split(separator)
var e,
key,
splice,
i,
ii,
j,
jj,
cur = [events],
inodes = []
for (i = 0, ii = names.length; i < ii; i++) {
for (j = 0; j < cur.length; j += splice.length - 2) {
splice = [j, 1]
e = cur[j].n
if (names[i] != wildcard) {
if (e[names[i]]) {
splice.push(e[names[i]])
inodes.unshift({
n: e,
name: names[i]
})
}
} else {
for (key in e)
if (e[has](key)) {
splice.push(e[key])
inodes.unshift({
n: e,
name: key
})
}
}
cur.splice.apply(cur, splice)
}
}
for (i = 0, ii = cur.length; i < ii; i++) {
e = cur[i]
while (e.n) {
if (f) {
if (e.f) {
for (j = 0, jj = e.f.length; j < jj; j++)
if (e.f[j] == f) {
e.f.splice(j, 1)
break
}
!e.f.length && delete e.f
}
for (key in e.n)
if (e.n[has](key) && e.n[key].f) {
var funcs = e.n[key].f
for (j = 0, jj = funcs.length; j < jj; j++)
if (funcs[j] == f) {
funcs.splice(j, 1)
break
}
!funcs.length && delete e.n[key].f
}
} else {
delete e.f
for (key in e.n)
if (e.n[has](key) && e.n[key].f) {
delete e.n[key].f
}
}
e = e.n
}
}
// prune inner nodes in path
prune: for (i = 0, ii = inodes.length; i < ii; i++) {
e = inodes[i]
for (key in e.n[e.name].f) {
// not empty (has listeners)
continue prune
}
for (key in e.n[e.name].n) {
// not empty (has children)
continue prune
}
// is empty
delete e.n[e.name]
}
}
/*\
* eve.once
[ method ]
**
* Binds given event handler with a given name to only run once then unbind itself.
| eve.once("login", f);
| eve("login"); // triggers f
| eve("login"); // no listeners
* Use @eve to trigger the listener.
**
- name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
- f (function) event handler function
**
= (function) same return function as @eve.on
\*/
eve.once = function (name, f) {
var f2 = function () {
eve.off(name, f2)
return f.apply(this, arguments)
}
return eve.on(name, f2)
}
/*\
* eve.version
[ property (string) ]
**
* Current version of the library.
\*/
eve.version = version
eve.toString = function () {
return 'You are running Eve ' + version
}
export default eve

348
src/filter.js Normal file
View File

@ -0,0 +1,348 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
Snap.plugin(function (Snap, Element, Paper, glob) {
var elproto = Element.prototype,
pproto = Paper.prototype,
rgurl = /^\s*url\((.+)\)/,
Str = String,
$ = Snap._.$
Snap.filter = {}
/*\
* Paper.filter
[ method ]
**
* Creates a `<filter>` element
**
- filstr (string) SVG fragment of filter provided as a string
= (object) @Element
* Note: It is recommended to use filters embedded into the page inside an empty SVG element.
> Usage
| var f = paper.filter('<feGaussianBlur stdDeviation="2"/>'),
| c = paper.circle(10, 10, 10).attr({
| filter: f
| });
\*/
pproto.filter = function (filstr) {
var paper = this
if (paper.type != 'svg') {
paper = paper.paper
}
var f = Snap.parse(Str(filstr)),
id = Snap._.id(),
width = paper.node.offsetWidth,
height = paper.node.offsetHeight,
filter = $('filter')
$(filter, {
id: id,
filterUnits: 'userSpaceOnUse'
})
filter.appendChild(f.node)
paper.defs.appendChild(filter)
return new Element(filter)
}
eve.on('snap.util.getattr.filter', function () {
eve.stop()
var p = $(this.node, 'filter')
if (p) {
var match = Str(p).match(rgurl)
return match && Snap.select(match[1])
}
})
eve.on('snap.util.attr.filter', function (value) {
if (value instanceof Element && value.type == 'filter') {
eve.stop()
var id = value.node.id
if (!id) {
$(value.node, { id: value.id })
id = value.id
}
$(this.node, {
filter: Snap.url(id)
})
}
if (!value || value == 'none') {
eve.stop()
this.node.removeAttribute('filter')
}
})
/*\
* Snap.filter.blur
[ method ]
**
* Returns an SVG markup string for the blur filter
**
- x (number) amount of horizontal blur, in pixels
- y (number) #optional amount of vertical blur, in pixels
= (string) filter representation
> Usage
| var f = paper.filter(Snap.filter.blur(5, 10)),
| c = paper.circle(10, 10, 10).attr({
| filter: f
| });
\*/
Snap.filter.blur = function (x, y) {
if (x == null) {
x = 2
}
var def = y == null ? x : [x, y]
return Snap.format('<feGaussianBlur stdDeviation="{def}"/>', {
def: def
})
}
Snap.filter.blur.toString = function () {
return this()
}
/*\
* Snap.filter.shadow
[ method ]
**
* Returns an SVG markup string for the shadow filter
**
- 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, opacity) {
if (opacity == null) {
if (color == null) {
opacity = blur
blur = 4
color = '#000'
} else {
opacity = color
color = blur
blur = 4
}
}
if (blur == null) {
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"/><feComponentTransfer><feFuncA type="linear" slope="{opacity}"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>',
{
color: color,
dx: dx,
dy: dy,
blur: blur,
opacity: opacity
}
)
}
Snap.filter.shadow.toString = function () {
return this()
}
/*\
* Snap.filter.grayscale
[ method ]
**
* Returns an SVG markup string for the grayscale filter
**
- amount (number) amount of filter (`0..1`)
= (string) filter representation
\*/
Snap.filter.grayscale = function (amount) {
if (amount == null) {
amount = 1
}
return Snap.format(
'<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {b} {h} 0 0 0 0 0 1 0"/>',
{
a: 0.2126 + 0.7874 * (1 - amount),
b: 0.7152 - 0.7152 * (1 - amount),
c: 0.0722 - 0.0722 * (1 - amount),
d: 0.2126 - 0.2126 * (1 - amount),
e: 0.7152 + 0.2848 * (1 - amount),
f: 0.0722 - 0.0722 * (1 - amount),
g: 0.2126 - 0.2126 * (1 - amount),
h: 0.0722 + 0.9278 * (1 - amount)
}
)
}
Snap.filter.grayscale.toString = function () {
return this()
}
/*\
* Snap.filter.sepia
[ method ]
**
* Returns an SVG markup string for the sepia filter
**
- amount (number) amount of filter (`0..1`)
= (string) filter representation
\*/
Snap.filter.sepia = function (amount) {
if (amount == null) {
amount = 1
}
return Snap.format(
'<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {h} {i} 0 0 0 0 0 1 0"/>',
{
a: 0.393 + 0.607 * (1 - amount),
b: 0.769 - 0.769 * (1 - amount),
c: 0.189 - 0.189 * (1 - amount),
d: 0.349 - 0.349 * (1 - amount),
e: 0.686 + 0.314 * (1 - amount),
f: 0.168 - 0.168 * (1 - amount),
g: 0.272 - 0.272 * (1 - amount),
h: 0.534 - 0.534 * (1 - amount),
i: 0.131 + 0.869 * (1 - amount)
}
)
}
Snap.filter.sepia.toString = function () {
return this()
}
/*\
* Snap.filter.saturate
[ method ]
**
* Returns an SVG markup string for the saturate filter
**
- amount (number) amount of filter (`0..1`)
= (string) filter representation
\*/
Snap.filter.saturate = function (amount) {
if (amount == null) {
amount = 1
}
return Snap.format('<feColorMatrix type="saturate" values="{amount}"/>', {
amount: 1 - amount
})
}
Snap.filter.saturate.toString = function () {
return this()
}
/*\
* Snap.filter.hueRotate
[ method ]
**
* Returns an SVG markup string for the hue-rotate filter
**
- angle (number) angle of rotation
= (string) filter representation
\*/
Snap.filter.hueRotate = function (angle) {
angle = angle || 0
return Snap.format('<feColorMatrix type="hueRotate" values="{angle}"/>', {
angle: angle
})
}
Snap.filter.hueRotate.toString = function () {
return this()
}
/*\
* Snap.filter.invert
[ method ]
**
* Returns an SVG markup string for the invert filter
**
- amount (number) amount of filter (`0..1`)
= (string) filter representation
\*/
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
}
)
}
Snap.filter.invert.toString = function () {
return this()
}
/*\
* Snap.filter.brightness
[ method ]
**
* Returns an SVG markup string for the brightness filter
**
- amount (number) amount of filter (`0..1`)
= (string) filter representation
\*/
Snap.filter.brightness = function (amount) {
if (amount == null) {
amount = 1
}
return Snap.format(
'<feComponentTransfer><feFuncR type="linear" slope="{amount}"/><feFuncG type="linear" slope="{amount}"/><feFuncB type="linear" slope="{amount}"/></feComponentTransfer>',
{
amount: amount
}
)
}
Snap.filter.brightness.toString = function () {
return this()
}
/*\
* Snap.filter.contrast
[ method ]
**
* Returns an SVG markup string for the contrast filter
**
- amount (number) amount of filter (`0..1`)
= (string) filter representation
\*/
Snap.filter.contrast = function (amount) {
if (amount == null) {
amount = 1
}
return Snap.format(
'<feComponentTransfer><feFuncR type="linear" slope="{amount}" intercept="{amount2}"/><feFuncG type="linear" slope="{amount}" intercept="{amount2}"/><feFuncB type="linear" slope="{amount}" intercept="{amount2}"/></feComponentTransfer>',
{
amount: amount,
amount2: 0.5 - amount / 2
}
)
}
Snap.filter.contrast.toString = function () {
return this()
}
})

17
src/index.js Normal file
View File

@ -0,0 +1,17 @@
import { Snap } from './svg.js'
import './element.js'
import './animation.js'
import './matrix.js'
import './attr.js'
import './class.js'
import './attradd.js'
import './paper.js'
import './path.js'
import './set.js'
import './equal.js'
import './mouse.js'
import './filter.js'
import './align.js'
import './colors.js'
export default Snap

413
src/matrix.js Normal file
View File

@ -0,0 +1,413 @@
// 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.
import { Snap } from './svg.js'
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 (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) {
if (a && a instanceof Matrix) {
return this.add(a.a, a.b, a.c, a.d, a.e, a.f)
}
var aNew = a * this.a + b * this.c,
bNew = a * this.b + b * this.d
this.e += e * this.a + f * this.c
this.f += e * this.b + f * this.d
this.c = c * this.a + d * this.c
this.d = c * this.b + d * this.d
this.a = aNew
this.b = bNew
return this
}
/*\
* Matrix.multLeft
[ method ]
**
* Multiplies a passed affine transform to the left: M * this.
- a (number)
- b (number)
- c (number)
- d (number)
- e (number)
- f (number)
* or
- matrix (object) @Matrix
\*/
Matrix.prototype.multLeft = function (a, b, c, d, e, f) {
if (a && a instanceof Matrix) {
return this.multLeft(a.a, a.b, a.c, a.d, a.e, a.f)
}
var aNew = a * this.a + c * this.b,
cNew = a * this.c + c * this.d,
eNew = a * this.e + c * this.f + e
this.b = b * this.a + d * this.b
this.d = b * this.c + d * this.d
this.f = b * this.e + d * this.f + f
this.a = aNew
this.c = cNew
this.e = eNew
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) {
this.e += x * this.a + y * this.c
this.f += x * this.b + y * this.d
return this
}
/*\
* 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.translate(cx, cy)
this.a *= x
this.b *= x
this.c *= y
this.d *= y
;(cx || cy) && this.translate(-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.skewX
[ method ]
**
* Skews the matrix along the x-axis
- x (number) Angle to skew along the x-axis (in degrees).
\*/
matrixproto.skewX = function (x) {
return this.skew(x, 0)
}
/*\
* Matrix.skewY
[ method ]
**
* Skews the matrix along the y-axis
- y (number) Angle to skew along the y-axis (in degrees).
\*/
matrixproto.skewY = function (y) {
return this.skew(0, y)
}
/*\
* Matrix.skew
[ method ]
**
* Skews the matrix
- y (number) Angle to skew along the y-axis (in degrees).
- x (number) Angle to skew along the x-axis (in degrees).
\*/
matrixproto.skew = function (x, y) {
x = x || 0
y = y || 0
x = Snap.rad(x)
y = Snap.rad(y)
var c = math.tan(x).toFixed(9)
var b = math.tan(y).toFixed(9)
return this.add(1, b, c, 1, 0, 0)
}
/*\
* 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.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.b],
[this.c, 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.rotate ? 'r' + [+s.rotate.toFixed(4), 0, 0] : E) +
(s.scalex != 1 || s.scaley != 1
? 's' + [s.scalex, s.scaley, 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)
}
})

378
src/mina.js Normal file
View File

@ -0,0 +1,378 @@
// Copyright (c) 2017 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.
import eve from './eve.js'
import { Snap } from './svg.js'
var animations = {},
requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
setTimeout(callback, 16, new Date().getTime())
return true
},
requestID,
isArray =
Array.isArray ||
function (a) {
return (
a instanceof Array ||
Object.prototype.toString.call(a) == '[object Array]'
)
},
idgen = 0,
idprefix = 'M' + (+new Date()).toString(36),
ID = function () {
return idprefix + (idgen++).toString(36)
},
diff = function (a, b, A, B) {
if (isArray(a)) {
res = []
for (var i = 0, ii = a.length; i < ii; i++) {
res[i] = diff(a[i], b, A[i], B)
}
return res
}
var dif = (A - a) / (B - b)
return function (bb) {
return a + dif * (bb - b)
}
},
timer =
Date.now ||
function () {
return +new Date()
},
sta = function (val) {
var a = this
if (val == null) {
return a.s
}
var ds = a.s - val
a.b += a.dur * ds
a.B += a.dur * ds
a.s = val
},
speed = function (val) {
var a = this
if (val == null) {
return a.spd
}
a.spd = val
},
duration = function (val) {
var a = this
if (val == null) {
return a.dur
}
a.s = (a.s * val) / a.dur
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) {
return
}
a.b = a.get() - a.pdif
delete a.pdif
animations[a.id] = a
frame()
},
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 (timeStamp) {
// Manual invokation?
if (!timeStamp) {
// Frame loop stopped?
if (!requestID) {
// Start frame loop...
requestID = requestAnimFrame(frame)
}
return
}
var len = 0
for (var i in animations)
if (animations.hasOwnProperty(i)) {
var a = animations[i],
b = a.get(),
res
len++
a.s = (b - a.b) / (a.dur / a.spd)
if (a.s >= 1) {
delete animations[i]
a.s = 1
len--
;(function (a) {
setTimeout(function () {
eve('mina.finish.' + a.id, a)
})
})(a)
}
a.update()
}
requestID = len ? requestAnimFrame(frame) : false
},
/*\
* mina
[ method ]
**
* Generic animation of numbers
**
- a (number) start _slave_ number
- A (number) end _slave_ number
- b (number) start _master_ number (start time in general case)
- B (number) end _master_ number (end time in general case)
- get (function) getter of _master_ number (see @mina.time)
- set (function) setter of _slave_ number
- easing (function) #optional easing function, default is @mina.linear
= (object) animation descriptor
o {
o id (string) animation id,
o start (number) start _slave_ number,
o end (number) end _slave_ number,
o b (number) start _master_ number,
o s (number) animation status (0..1),
o dur (number) animation duration,
o spd (number) animation speed,
o get (function) getter of _master_ number (see @mina.time),
o set (function) setter of _slave_ number,
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(),
start: a,
end: A,
b: b,
s: 0,
dur: B - b,
spd: 1,
get: get,
set: set,
easing: easing || mina.linear,
status: sta,
speed: speed,
duration: duration,
stop: stopit,
pause: pause,
resume: resume,
update: update
}
animations[anim.id] = anim
var len = 0,
i
for (i in animations)
if (animations.hasOwnProperty(i)) {
len++
if (len == 2) {
break
}
}
len == 1 && frame()
return anim
}
/*\
* mina.time
[ method ]
**
* Returns the current time. Equivalent to:
| function () {
| return (new Date).getTime();
| }
\*/
mina.time = timer
/*\
* mina.getById
[ method ]
**
* Returns an animation by its id
- id (string) animation's id
= (object) See @mina
\*/
mina.getById = function (id) {
return animations[id] || null
}
/*\
* mina.linear
[ method ]
**
* Default linear easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.linear = function (n) {
return n
}
/*\
* mina.easeout
[ method ]
**
* Easeout easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.easeout = function (n) {
return Math.pow(n, 1.7)
}
/*\
* mina.easein
[ method ]
**
* Easein easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.easein = function (n) {
return Math.pow(n, 0.48)
}
/*\
* mina.easeinout
[ method ]
**
* Easeinout easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.easeinout = function (n) {
if (n == 1) {
return 1
}
if (n == 0) {
return 0
}
var q = 0.48 - n / 1.04,
Q = Math.sqrt(0.1734 + q * q),
x = Q - q,
X = Math.pow(Math.abs(x), 1 / 3) * (x < 0 ? -1 : 1),
y = -Q - q,
Y = Math.pow(Math.abs(y), 1 / 3) * (y < 0 ? -1 : 1),
t = X + Y + 0.5
return (1 - t) * 3 * t * t + t * t * t
}
/*\
* mina.backin
[ method ]
**
* Backin easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.backin = function (n) {
if (n == 1) {
return 1
}
var s = 1.70158
return n * n * ((s + 1) * n - s)
}
/*\
* mina.backout
[ method ]
**
* Backout easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.backout = function (n) {
if (n == 0) {
return 0
}
n = n - 1
var s = 1.70158
return n * n * ((s + 1) * n + s) + 1
}
/*\
* mina.elastic
[ method ]
**
* Elastic easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.elastic = function (n) {
if (n == !!n) {
return n
}
return (
Math.pow(2, -10 * n) * Math.sin(((n - 0.075) * (2 * Math.PI)) / 0.3) + 1
)
}
/*\
* mina.bounce
[ method ]
**
* Bounce easing
- n (number) input 0..1
= (number) output 0..1
\*/
mina.bounce = function (n) {
var s = 7.5625,
p = 2.75,
l
if (n < 1 / p) {
l = s * n * n
} else {
if (n < 2 / p) {
n -= 1.5 / p
l = s * n * n + 0.75
} else {
if (n < 2.5 / p) {
n -= 2.25 / p
l = s * n * n + 0.9375
} else {
n -= 2.625 / p
l = s * n * n + 0.984375
}
}
}
return l
}
export default mina

533
src/mouse.js Normal file
View File

@ -0,0 +1,533 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
Snap.plugin(function (Snap, Element, Paper, glob) {
var elproto = Element.prototype,
has = 'hasOwnProperty',
supportsTouch =
'ontouchstart' in window ||
window.TouchEvent ||
(window.DocumentTouch && document instanceof DocumentTouch),
events = [
'click',
'dblclick',
'mousedown',
'mousemove',
'mouseout',
'mouseover',
'mouseup',
'touchstart',
'touchmove',
'touchend',
'touchcancel'
],
touchMap = {
mousedown: 'touchstart',
mousemove: 'touchmove',
mouseup: 'touchend'
},
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 () {
return this.originalEvent.preventDefault()
},
stopPropagation = function () {
this.cancelBubble = true
},
stopTouch = function () {
return this.originalEvent.stopPropagation()
},
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)
}
obj.addEventListener(realName, f, false)
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'),
scrollX = getScroll('x'),
dragi,
j = drag.length
while (j--) {
dragi = drag[j]
if (supportsTouch) {
var i = e.touches && e.touches.length,
touch
while (i--) {
touch = e.touches[i]
if (
touch.identifier == dragi.el._drag.id ||
dragi.el.node.contains(touch.target)
) {
x = touch.clientX
y = touch.clientY
;(e.originalEvent ? e.originalEvent : e).preventDefault()
break
}
}
} else {
e.preventDefault()
}
var node = dragi.el.node,
o,
next = node.nextSibling,
parent = node.parentNode,
display = node.style.display
// glob.win.opera && parent.removeChild(node);
// node.style.display = "none";
// o = dragi.el.paper.getElementByPoint(x, y);
// node.style.display = display;
// glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
// o && eve("snap.drag.over." + dragi.el.id, dragi.el, o);
x += scrollX
y += scrollY
eve(
'snap.drag.move.' + dragi.el.id,
dragi.move_scope || dragi.el,
x - dragi.el._drag.x,
y - dragi.el._drag.y,
x,
y,
e
)
}
},
dragUp = function (e) {
Snap.unmousemove(dragMove).unmouseup(dragUp)
var i = drag.length,
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
[ method ]
**
* Adds a click event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unclick
[ method ]
**
* Removes a click event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.dblclick
[ method ]
**
* Adds a double click event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.undblclick
[ method ]
**
* Removes a double click event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mousedown
[ method ]
**
* Adds a mousedown event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmousedown
[ method ]
**
* Removes a mousedown event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mousemove
[ method ]
**
* Adds a mousemove event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmousemove
[ method ]
**
* Removes a mousemove event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mouseout
[ method ]
**
* Adds a mouseout event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmouseout
[ method ]
**
* Removes a mouseout event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mouseover
[ method ]
**
* Adds a mouseover event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmouseover
[ method ]
**
* Removes a mouseover event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mouseup
[ method ]
**
* Adds a mouseup event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmouseup
[ method ]
**
* Removes a mouseup event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchstart
[ method ]
**
* Adds a touchstart event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchstart
[ method ]
**
* Removes a touchstart event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchmove
[ method ]
**
* Adds a touchmove event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchmove
[ method ]
**
* Removes a touchmove event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchend
[ method ]
**
* Adds a touchend event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchend
[ method ]
**
* Removes a touchend event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchcancel
[ method ]
**
* Adds a touchcancel event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchcancel
[ method ]
**
* Removes a touchcancel event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
for (var i = events.length; i--; ) {
;(function (eventName) {
Snap[eventName] = elproto[eventName] = function (fn, scope) {
if (Snap.is(fn, 'function')) {
this.events = this.events || []
this.events.push({
name: eventName,
f: fn,
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) {
var events = this.events || [],
l = events.length
while (l--)
if (events[l].name == eventName && (events[l].f == fn || !fn)) {
events[l].unbind()
events.splice(l, 1)
!events.length && delete this.events
return this
}
return this
}
})(events[i])
}
/*\
* Element.hover
[ method ]
**
* Adds hover event handlers to the element
- f_in (function) handler for hover in
- f_out (function) handler for hover out
- icontext (object) #optional context for hover in handler
- ocontext (object) #optional context for hover out handler
= (object) @Element
\*/
elproto.hover = function (f_in, f_out, scope_in, scope_out) {
return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in)
}
/*\
* Element.unhover
[ method ]
**
* Removes hover event handlers from the element
- f_in (function) handler for hover in
- f_out (function) handler for hover out
= (object) @Element
\*/
elproto.unhover = function (f_in, f_out) {
return this.unmouseover(f_in).unmouseout(f_out)
}
var draggable = []
// SIERRA unclear what _context_ refers to for starting, ending, moving the drag gesture.
// SIERRA Element.drag(): _x position of the mouse_: Where are the x/y values offset from?
// SIERRA Element.drag(): much of this member's doc appears to be duplicated for some reason.
// SIERRA Unclear about this sentence: _Additionally following drag events will be triggered: drag.start.<id> on start, drag.end.<id> on end and drag.move.<id> on every move._ Is there a global _drag_ object to which you can assign handlers keyed by an element's ID?
/*\
* Element.drag
[ method ]
**
* Adds event handlers for an element's drag gesture
**
- onmove (function) handler for moving
- onstart (function) handler for drag start
- onend (function) handler for drag end
- mcontext (object) #optional context for moving handler
- scontext (object) #optional context for drag start handler
- econtext (object) #optional context for drag end handler
* Additionaly following `drag` events are triggered: `drag.start.<id>` on start,
* `drag.end.<id>` on end and `drag.move.<id>` on every move. When element is dragged over another element
* `drag.over.<id>` fires as well.
*
* Start event and start handler are called in specified context or in context of the element with following parameters:
o x (number) x position of the mouse
o y (number) y position of the mouse
o event (object) DOM event object
* Move event and move handler are called in specified context or in context of the element with following parameters:
o dx (number) shift by x from the start point
o dy (number) shift by y from the start point
o x (number) x position of the mouse
o y (number) y position of the mouse
o event (object) DOM event object
* 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 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()
el._drag.x = x
el._drag.y = y
el._drag.id = e.identifier
!drag.length && Snap.mousemove(dragMove).mouseup(dragUp)
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)
}
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 ]
**
* Shortcut to assign event handler for `drag.over.<id>` event, where `id` is the element's `id` (see @Element.id)
- f (function) handler for event, first argument would be the element you are dragging over
\*/
// elproto.onDragOver = function (f) {
// f ? eve.on("snap.drag.over." + this.id, f) : eve.unbind("snap.drag.over." + this.id);
// };
/*\
* Element.undrag
[ method ]
**
* 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].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
}
})

783
src/paper.js Normal file
View File

@ -0,0 +1,783 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
Snap.plugin(function (Snap, Element, Paper, glob, Fragment) {
var proto = Paper.prototype,
is = Snap.is
/*\
* Paper.rect
[ method ]
*
* Draws a rectangle
**
- x (number) x coordinate of the top left corner
- y (number) y coordinate of the top left corner
- width (number) width
- height (number) height
- rx (number) #optional horizontal radius for rounded corners, default is 0
- ry (number) #optional vertical radius for rounded corners, default is rx or 0
= (object) the `rect` element
**
> Usage
| // regular rectangle
| var c = paper.rect(10, 10, 50, 50);
| // rectangle with rounded corners
| var c = paper.rect(40, 40, 50, 50, 10);
\*/
proto.rect = function (x, y, w, h, rx, ry) {
var attr
if (ry == null) {
ry = rx
}
if (is(x, 'object') && x == '[object Object]') {
attr = x
} else if (x != null) {
attr = {
x: x,
y: y,
width: w,
height: h
}
if (rx != null) {
attr.rx = rx
attr.ry = ry
}
}
return this.el('rect', attr)
}
/*\
* Paper.circle
[ method ]
**
* Draws a circle
**
- x (number) x coordinate of the centre
- y (number) y coordinate of the centre
- r (number) radius
= (object) the `circle` element
**
> Usage
| var c = paper.circle(50, 50, 40);
\*/
proto.circle = function (cx, cy, r) {
var attr
if (is(cx, 'object') && cx == '[object Object]') {
attr = cx
} else if (cx != null) {
attr = {
cx: cx,
cy: cy,
r: r
}
}
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
**
- src (string) URI of the source image
- x (number) x offset position
- y (number) y offset position
- width (number) width of the image
- height (number) height of the image
= (object) the `image` element
* or
= (object) Snap element object with type `image`
**
> Usage
| var c = paper.image("apple.png", 10, 10, 80, 80);
\*/
proto.image = function (src, x, y, width, height) {
var el = this.el('image')
if (is(src, 'object') && 'src' in src) {
el.attr(src)
} else if (src != null) {
var set = {
'xlink:href': src,
preserveAspectRatio: 'none'
}
if (x != null && y != null) {
set.x = x
set.y = y
}
if (width != null && height != null) {
set.width = width
set.height = height
} else {
preload(src, function () {
Snap._.$(el.node, {
width: this.offsetWidth,
height: this.offsetHeight
})
})
}
Snap._.$(el.node, set)
}
return el
}
/*\
* Paper.ellipse
[ method ]
**
* Draws an ellipse
**
- x (number) x coordinate of the centre
- y (number) y coordinate of the centre
- rx (number) horizontal radius
- ry (number) vertical radius
= (object) the `ellipse` element
**
> Usage
| var c = paper.ellipse(50, 50, 40, 20);
\*/
proto.ellipse = function (cx, cy, rx, ry) {
var attr
if (is(cx, 'object') && cx == '[object Object]') {
attr = cx
} else if (cx != null) {
attr = {
cx: cx,
cy: cy,
rx: rx,
ry: ry
}
}
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 ]
**
* Creates a `<path>` element using the given string as the path's definition
- pathString (string) #optional path string in SVG format
* Path string consists of one-letter commands, followed by comma seprarated arguments in numerical form. Example:
| "M10,20L30,40"
* This example features two commands: `M`, with arguments `(10, 20)` and `L` with arguments `(30, 40)`. Uppercase letter commands express coordinates in absolute terms, while lowercase commands express them in relative terms from the most recently declared coordinates.
*
# <p>Here is short list of commands available, for more details see <a href="http://www.w3.org/TR/SVG/paths.html#PathData" title="Details of a path's data attribute's format are described in the SVG specification.">SVG path string format</a> or <a href="https://developer.mozilla.org/en/SVG/Tutorial/Paths">article about path strings at MDN</a>.</p>
# <table><thead><tr><th>Command</th><th>Name</th><th>Parameters</th></tr></thead><tbody>
# <tr><td>M</td><td>moveto</td><td>(x y)+</td></tr>
# <tr><td>Z</td><td>closepath</td><td>(none)</td></tr>
# <tr><td>L</td><td>lineto</td><td>(x y)+</td></tr>
# <tr><td>H</td><td>horizontal lineto</td><td>x+</td></tr>
# <tr><td>V</td><td>vertical lineto</td><td>y+</td></tr>
# <tr><td>C</td><td>curveto</td><td>(x1 y1 x2 y2 x y)+</td></tr>
# <tr><td>S</td><td>smooth curveto</td><td>(x2 y2 x y)+</td></tr>
# <tr><td>Q</td><td>quadratic Bézier curveto</td><td>(x1 y1 x y)+</td></tr>
# <tr><td>T</td><td>smooth quadratic Bézier curveto</td><td>(x y)+</td></tr>
# <tr><td>A</td><td>elliptical arc</td><td>(rx ry x-axis-rotation large-arc-flag sweep-flag x y)+</td></tr>
# <tr><td>R</td><td><a href="http://en.wikipedia.org/wiki/CatmullRom_spline#Catmull.E2.80.93Rom_spline">Catmull-Rom curveto</a>*</td><td>x1 y1 (x y)+</td></tr></tbody></table>
* * _Catmull-Rom curveto_ is a not standard SVG command and added to make life easier.
* Note: there is a special case when a path consists of only three commands: `M10,10R…z`. In this case the path connects back to its starting point.
> Usage
| var c = paper.path("M10 10L90 90");
| // draw a diagonal line:
| // move to 10,10, line to 90,90
\*/
proto.path = function (d) {
var attr
if (is(d, 'object') && !is(d, 'array')) {
attr = d
} else if (d) {
attr = { d: d }
}
return this.el('path', attr)
}
/*\
* Paper.g
[ method ]
**
* Creates a group element
**
- varargs () #optional elements to nest within the group
= (object) the `g` element
**
> Usage
| var c1 = paper.circle(),
| c2 = paper.rect(),
| g = paper.g(c2, c1); // note that the order of elements is different
* or
| var c1 = paper.circle(),
| c2 = paper.rect(),
| g = paper.g();
| g.add(c2, c1);
\*/
/*\
* Paper.group
[ method ]
**
* See @Paper.g
\*/
proto.group = proto.g = function (first) {
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 its 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 its 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
**
- x (number) x coordinate position
- y (number) y coordinate position
- text (string|array) The text string to draw or array of strings to nest within separate `<tspan>` elements
= (object) the `text` element
**
> Usage
| var t1 = paper.text(50, 50, "Snap");
| var t2 = paper.text(50, 50, ["S","n","a","p"]);
| // Text path usage
| t1.attr({textpath: "M10,10L100,100"});
| // or
| var pth = paper.path("M10,10L100,100");
| t1.attr({textpath: pth});
\*/
proto.text = function (x, y, text) {
var attr = {}
if (is(x, 'object')) {
attr = x
} else if (x != null) {
attr = {
x: x,
y: y,
text: text || ''
}
}
return this.el('text', attr)
}
/*\
* Paper.line
[ method ]
**
* Draws a line
**
- x1 (number) x coordinate position of the start
- y1 (number) y coordinate position of the start
- x2 (number) x coordinate position of the end
- y2 (number) y coordinate position of the end
= (object) the `line` element
**
> Usage
| var t1 = paper.line(50, 50, 100, 100);
\*/
proto.line = function (x1, y1, x2, y2) {
var attr = {}
if (is(x1, 'object')) {
attr = x1
} else if (x1 != null) {
attr = {
x1: x1,
x2: x2,
y1: y1,
y2: y2
}
}
return this.el('line', attr)
}
/*\
* Paper.polyline
[ method ]
**
* Draws a polyline
**
- points (array) array of points
* or
- varargs () points
= (object) the `polyline` element
**
> Usage
| var p1 = paper.polyline([10, 10, 100, 100]);
| var p2 = paper.polyline(10, 10, 100, 100);
\*/
proto.polyline = function (points) {
if (arguments.length > 1) {
points = Array.prototype.slice.call(arguments, 0)
}
var attr = {}
if (is(points, 'object') && !is(points, 'array')) {
attr = points
} else if (points != null) {
attr = { points: points }
}
return this.el('polyline', attr)
}
/*\
* Paper.polygon
[ method ]
**
* Draws a polygon. See @Paper.polyline
\*/
proto.polygon = function (points) {
if (arguments.length > 1) {
points = Array.prototype.slice.call(arguments, 0)
}
var attr = {}
if (is(points, 'object') && !is(points, 'array')) {
attr = points
} else if (points != null) {
attr = { points: points }
}
return this.el('polygon', attr)
}
// gradients
;(function () {
var $ = Snap._.$
// gradients' helpers
/*\
* Element.stops
[ method ]
**
* Only for gradients!
* Returns array of gradient stops elements.
= (array) the stops array.
\*/
function Gstops() {
return this.selectAll('stop')
}
/*\
* Element.addStop
[ method ]
**
* Only for gradients!
* Adds another stop to the gradient.
- color (string) stops color
- offset (number) stops offset 0..100
= (object) gradient element
\*/
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)
var stops = this.stops(),
inserted
for (var i = 0; i < stops.length; i++) {
var stopOffset = parseFloat(stops[i].attr('offset'))
if (stopOffset > offset) {
this.node.insertBefore(stop, stops[i].node)
inserted = true
break
}
}
if (!inserted) {
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 || 0.5,
cy = this.node.cy || 0.5,
r = this.node.r || 0
return Snap._.box(cx - r, cy - r, r * 2, r * 2)
}
}
/*\
* Element.setStops
[ method ]
**
* Only for gradients!
* Updates stops of the gradient based on passed gradient descriptor. See @Ppaer.gradient
- str (string) gradient descriptor part after `()`.
= (object) gradient element
| var g = paper.gradient("l(0, 0, 1, 1)#000-#f00-#fff");
| g.setStops("#fff-#000-#f00-#fc0");
\*/
function GsetStops(str) {
var grad = str,
stops = this.stops()
if (typeof str == 'string') {
grad = eve(
'snap.util.grad.parse',
null,
'l(0,0,0,1)' + str
).firstDefined().stops
}
if (!Snap.is(grad, 'array')) {
return
}
for (var i = 0; i < stops.length; i++) {
if (grad[i]) {
var color = Snap.color(grad[i].color),
attr = { offset: grad[i].offset + '%' }
attr['stop-color'] = color.hex
if (color.opacity < 1) {
attr['stop-opacity'] = color.opacity
}
stops[i].attr(attr)
} else {
stops[i].remove()
}
}
for (i = stops.length; i < grad.length; i++) {
this.addStop(grad[i].color, grad[i].offset)
}
return this
}
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
for (var 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
el.setStops = GsetStops
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
**
- gradient (string) gradient descriptor
> Gradient Descriptor
* The gradient descriptor is an expression formatted as
* follows: `<type>(<coords>)<colors>`. The `<type>` can be
* either linear or radial. The uppercase `L` or `R` letters
* indicate absolute coordinates offset from the SVG surface.
* Lowercase `l` or `r` letters indicate coordinates
* calculated relative to the element to which the gradient is
* applied. Coordinates specify a linear gradient vector as
* `x1`, `y1`, `x2`, `y2`, or a radial gradient as `cx`, `cy`,
* `r` and optional `fx`, `fy` specifying a focal point away
* from the center of the circle. Specify `<colors>` as a list
* of dash-separated CSS color values. Each color may be
* followed by a custom offset value, separated with a colon
* character.
> Examples
* 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");
* 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({
| fill: g
| });
= (object) the `gradient` element
\*/
proto.gradient = function (str) {
return gradient(this.defs, str)
}
proto.gradientLinear = function (x1, y1, x2, y2) {
return gradientLinear(this.defs, x1, y1, x2, y2)
}
proto.gradientRadial = function (cx, cy, r, fx, fy) {
return gradientRadial(this.defs, cx, cy, r, fx, fy)
}
/*\
* Paper.toString
[ method ]
**
* Returns SVG code for the @Paper
= (string) SVG code for the @Paper
\*/
proto.toString = function () {
var doc = this.node.ownerDocument,
f = doc.createDocumentFragment(),
d = doc.createElement('div'),
svg = this.node.cloneNode(true),
res
f.appendChild(d)
d.appendChild(svg)
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>.
\*/
proto.clear = function () {
var node = this.node.firstChild,
next
while (node) {
next = node.nextSibling
if (node.tagName != 'defs') {
node.parentNode.removeChild(node)
} else {
proto.clear.call({ node: node })
}
node = next
}
}
})()
})

1633
src/path.js Normal file

File diff suppressed because it is too large Load Diff

379
src/set.js Normal file
View File

@ -0,0 +1,379 @@
// 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.
import eve from './eve.js'
import { Snap } from './svg.js'
import mina from './mina.js'
Snap.plugin(function (Snap, Element, Paper, glob) {
var mmax = Math.max,
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]) {
this[this.items.length] = this.items[this.items.length] = items[i]
this.length++
}
}
}
},
setproto = Set.prototype
/*\
* Set.push
[ method ]
**
* Adds each argument to the current set
= (object) original element
\*/
setproto.push = function () {
var item, len
for (var i = 0, ii = arguments.length; i < ii; i++) {
item = arguments[i]
if (item) {
len = this.items.length
this[len] = this.items[len] = item
this.length++
}
}
return this
}
/*\
* Set.pop
[ method ]
**
* Removes last element and returns it
= (object) element
\*/
setproto.pop = function () {
this.length && delete this[this.length--]
return this.items.pop()
}
/*\
* Set.forEach
[ method ]
**
* Executes given function for each element in the set
*
* If the function returns `false`, the loop stops running.
**
- callback (function) function to run
- thisArg (object) context object for the callback
= (object) Set object
\*/
setproto.forEach = function (callback, thisArg) {
for (var i = 0, ii = this.items.length; i < ii; i++) {
if (callback.call(thisArg, this.items[i], i) === false) {
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)
}
})
}
/*\
* Set.remove
[ method ]
**
* Removes all children of the set.
*
= (object) Set object
\*/
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
}
/*\
* Set.attr
[ method ]
**
* Equivalent of @Element.attr.
= (object) Set object
\*/
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(unbound)
}
return this
}
/*\
* Set.clear
[ method ]
**
* Removes all elements from the set
\*/
setproto.clear = function () {
while (this.length) {
this.pop()
}
}
/*\
* Set.splice
[ method ]
**
* Removes range of elements from the set
**
- index (number) position of the deletion
- count (number) number of element to remove
- insertion (object) #optional elements to insert
= (object) set elements that were deleted
\*/
setproto.splice = function (index, count, insertion) {
index = index < 0 ? mmax(this.length + index, 0) : index
count = mmax(0, mmin(this.length - index, count))
var tail = [],
todel = [],
args = [],
i
for (i = 2; i < arguments.length; i++) {
args.push(arguments[i])
}
for (i = 0; i < count; i++) {
todel.push(this[index + i])
}
for (; i < this.length - index; i++) {
tail.push(this[index + i])
}
var arglen = args.length
for (i = 0; i < arglen + tail.length; i++) {
this.items[index + i] = this[index + i] =
i < arglen ? args[i] : tail[i - arglen]
}
i = this.items.length = this.length -= count - arglen
while (this[i]) {
delete this[i++]
}
return new Set(todel)
}
/*\
* Set.exclude
[ method ]
**
* Removes given element from the set
**
- element (object) element to remove
= (boolean) `true` if object was found and removed from the set
\*/
setproto.exclude = function (el) {
for (var i = 0, ii = this.length; i < ii; i++)
if (this[i] == el) {
this.splice(i, 1)
return true
}
return false
}
/*\
* Set.insertAfter
[ method ]
**
* Inserts set elements after given element.
**
- element (object) set will be inserted after this element
= (object) Set object
\*/
setproto.insertAfter = function (el) {
var i = this.items.length
while (i--) {
this.items[i].insertAfter(el)
}
return this
}
/*\
* Set.getBBox
[ method ]
**
* Union of all bboxes of the set. See @Element.getBBox.
= (object) bounding box descriptor. See @Element.getBBox.
\*/
setproto.getBBox = function () {
var x = [],
y = [],
x2 = [],
y2 = []
for (var i = this.items.length; i--; )
if (!this.items[i].removed) {
var box = this.items[i].getBBox()
x.push(box.x)
y.push(box.y)
x2.push(box.x + box.width)
y2.push(box.y + box.height)
}
x = mmin.apply(0, x)
y = mmin.apply(0, y)
x2 = mmax.apply(0, x2)
y2 = mmax.apply(0, y2)
return {
x: x,
y: y,
x2: x2,
y2: y2,
width: x2 - x,
height: y2 - y,
cx: x + (x2 - x) / 2,
cy: y + (y2 - y) / 2
}
}
/*\
* Set.insertAfter
[ method ]
**
* Creates a clone of the set.
**
= (object) New Set object
\*/
setproto.clone = function (s) {
s = new Set()
for (var i = 0, ii = this.items.length; i < ii; i++) {
s.push(this.items[i].clone())
}
return s
}
setproto.toString = function () {
return 'Snap\u2018s set'
}
setproto.type = 'set'
// export
/*\
* Snap.Set
[ property ]
**
* Set constructor.
\*/
Snap.Set = Set
/*\
* Snap.set
[ method ]
**
* Creates a set and fills it with list of arguments.
**
= (object) New Set object
| var r = paper.rect(0, 0, 10, 10),
| s1 = Snap.set(), // empty set
| s2 = Snap.set(r, paper.circle(100, 100, 20)); // prefilled set
\*/
Snap.set = function () {
var set = new Set()
if (arguments.length) {
set.push.apply(set, Array.prototype.slice.call(arguments, 0))
}
return set
}
})

1984
src/svg.js Normal file

File diff suppressed because it is too large Load Diff