1985 lines
49 KiB
JavaScript
1985 lines
49 KiB
JavaScript
|
// Copyright (c) 2013 - 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'
|
||
|
|
||
|
/*\
|
||
|
* Snap
|
||
|
[ method ]
|
||
|
**
|
||
|
* Creates a drawing surface or wraps existing SVG element.
|
||
|
**
|
||
|
- width (number|string) width of surface
|
||
|
- height (number|string) height of surface
|
||
|
* or
|
||
|
- DOM (SVGElement) element to be wrapped into Snap structure
|
||
|
* or
|
||
|
- array (array) array of elements (will return set of elements)
|
||
|
* or
|
||
|
- query (string) CSS query selector
|
||
|
= (object) @Element
|
||
|
\*/
|
||
|
export function Snap(w, h) {
|
||
|
if (w) {
|
||
|
if (w.nodeType) {
|
||
|
return wrap(w)
|
||
|
}
|
||
|
if (is(w, 'array') && Snap.set) {
|
||
|
return Snap.set.apply(Snap, w)
|
||
|
}
|
||
|
if (w instanceof Element) {
|
||
|
return w
|
||
|
}
|
||
|
if (h == null) {
|
||
|
try {
|
||
|
w = glob.doc.querySelector(String(w))
|
||
|
return wrap(w)
|
||
|
} catch (e) {
|
||
|
return null
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
w = w == null ? '100%' : w
|
||
|
h = h == null ? '100%' : h
|
||
|
return new Paper(w, h)
|
||
|
}
|
||
|
Snap.toString = function () {
|
||
|
return 'Snap v' + this.version
|
||
|
}
|
||
|
Snap._ = {}
|
||
|
var glob = {
|
||
|
win: window,
|
||
|
doc: document
|
||
|
}
|
||
|
Snap._.glob = glob
|
||
|
var has = 'hasOwnProperty',
|
||
|
Str = String,
|
||
|
toFloat = parseFloat,
|
||
|
toInt = parseInt,
|
||
|
math = Math,
|
||
|
mmax = math.max,
|
||
|
mmin = math.min,
|
||
|
abs = math.abs,
|
||
|
pow = math.pow,
|
||
|
PI = math.PI,
|
||
|
round = math.round,
|
||
|
E = '',
|
||
|
S = ' ',
|
||
|
objectToString = Object.prototype.toString,
|
||
|
ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
|
||
|
colourRegExp =
|
||
|
/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,
|
||
|
bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
|
||
|
separator = (Snap._.separator = /[,\s]+/),
|
||
|
whitespace = /[\s]/g,
|
||
|
commaSpaces = /[\s]*,[\s]*/,
|
||
|
hsrg = { hs: 1, rg: 1 },
|
||
|
pathCommand = /([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,
|
||
|
tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,
|
||
|
pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\s]*,?[\s]*/gi,
|
||
|
idgen = 0,
|
||
|
idprefix = 'S' + (+new Date()).toString(36),
|
||
|
ID = function (el) {
|
||
|
return (el && el.type ? el.type : E) + idprefix + (idgen++).toString(36)
|
||
|
},
|
||
|
xlink = 'http://www.w3.org/1999/xlink',
|
||
|
xmlns = 'http://www.w3.org/2000/svg',
|
||
|
hub = {},
|
||
|
/*\
|
||
|
* Snap.url
|
||
|
[ method ]
|
||
|
**
|
||
|
* Wraps path into `"url('<path>')"`.
|
||
|
- value (string) path
|
||
|
= (string) wrapped path
|
||
|
\*/
|
||
|
URL = (Snap.url = function (url) {
|
||
|
return "url('#" + url + "')"
|
||
|
})
|
||
|
|
||
|
function $(el, attr) {
|
||
|
if (attr) {
|
||
|
if (el == '#text') {
|
||
|
el = glob.doc.createTextNode(attr.text || attr['#text'] || '')
|
||
|
}
|
||
|
if (el == '#comment') {
|
||
|
el = glob.doc.createComment(attr.text || attr['#text'] || '')
|
||
|
}
|
||
|
if (typeof el == 'string') {
|
||
|
el = $(el)
|
||
|
}
|
||
|
if (typeof attr == 'string') {
|
||
|
if (el.nodeType == 1) {
|
||
|
if (attr.substring(0, 6) == 'xlink:') {
|
||
|
return el.getAttributeNS(xlink, attr.substring(6))
|
||
|
}
|
||
|
if (attr.substring(0, 4) == 'xml:') {
|
||
|
return el.getAttributeNS(xmlns, attr.substring(4))
|
||
|
}
|
||
|
return el.getAttribute(attr)
|
||
|
} else if (attr == 'text') {
|
||
|
return el.nodeValue
|
||
|
} else {
|
||
|
return null
|
||
|
}
|
||
|
}
|
||
|
if (el.nodeType == 1) {
|
||
|
for (var key in attr)
|
||
|
if (attr[has](key)) {
|
||
|
var val = Str(attr[key])
|
||
|
if (val) {
|
||
|
if (key.substring(0, 6) == 'xlink:') {
|
||
|
el.setAttributeNS(xlink, key.substring(6), val)
|
||
|
} else if (key.substring(0, 4) == 'xml:') {
|
||
|
el.setAttributeNS(xmlns, key.substring(4), val)
|
||
|
} else {
|
||
|
el.setAttribute(key, val)
|
||
|
}
|
||
|
} else {
|
||
|
el.removeAttribute(key)
|
||
|
}
|
||
|
}
|
||
|
} else if ('text' in attr) {
|
||
|
el.nodeValue = attr.text
|
||
|
}
|
||
|
} else {
|
||
|
el = glob.doc.createElementNS(xmlns, el)
|
||
|
}
|
||
|
return el
|
||
|
}
|
||
|
Snap._.$ = $
|
||
|
Snap._.id = ID
|
||
|
function getAttrs(el) {
|
||
|
var attrs = el.attributes,
|
||
|
name,
|
||
|
out = {}
|
||
|
for (var i = 0; i < attrs.length; i++) {
|
||
|
if (attrs[i].namespaceURI == xlink) {
|
||
|
name = 'xlink:'
|
||
|
} else {
|
||
|
name = ''
|
||
|
}
|
||
|
name += attrs[i].name
|
||
|
out[name] = attrs[i].textContent
|
||
|
}
|
||
|
return out
|
||
|
}
|
||
|
function is(o, type) {
|
||
|
type = Str.prototype.toLowerCase.call(type)
|
||
|
if (type == 'finite') {
|
||
|
return isFinite(o)
|
||
|
}
|
||
|
if (
|
||
|
type == 'array' &&
|
||
|
(o instanceof Array || (Array.isArray && Array.isArray(o)))
|
||
|
) {
|
||
|
return true
|
||
|
}
|
||
|
return (
|
||
|
(type == 'null' && o === null) ||
|
||
|
(type == typeof o && o !== null) ||
|
||
|
(type == 'object' && o === Object(o)) ||
|
||
|
objectToString.call(o).slice(8, -1).toLowerCase() == type
|
||
|
)
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.format
|
||
|
[ method ]
|
||
|
**
|
||
|
* Replaces construction of type `{<name>}` to the corresponding argument
|
||
|
**
|
||
|
- token (string) string to format
|
||
|
- json (object) object which properties are used as a replacement
|
||
|
= (string) formatted string
|
||
|
> Usage
|
||
|
| // this draws a rectangular shape equivalent to "M10,20h40v50h-40z"
|
||
|
| paper.path(Snap.format("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
|
||
|
| x: 10,
|
||
|
| y: 20,
|
||
|
| dim: {
|
||
|
| width: 40,
|
||
|
| height: 50,
|
||
|
| "negative width": -40
|
||
|
| }
|
||
|
| }));
|
||
|
\*/
|
||
|
Snap.format = (function () {
|
||
|
var tokenRegex = /\{([^\}]+)\}/g,
|
||
|
objNotationRegex =
|
||
|
/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
|
||
|
replacer = function (all, key, obj) {
|
||
|
var res = obj
|
||
|
key.replace(
|
||
|
objNotationRegex,
|
||
|
function (all, name, quote, quotedName, isFunc) {
|
||
|
name = name || quotedName
|
||
|
if (res) {
|
||
|
if (name in res) {
|
||
|
res = res[name]
|
||
|
}
|
||
|
typeof res == 'function' && isFunc && (res = res())
|
||
|
}
|
||
|
}
|
||
|
)
|
||
|
res = (res == null || res == obj ? all : res) + ''
|
||
|
return res
|
||
|
}
|
||
|
return function (str, obj) {
|
||
|
return Str(str).replace(tokenRegex, function (all, key) {
|
||
|
return replacer(all, key, obj)
|
||
|
})
|
||
|
}
|
||
|
})()
|
||
|
function clone(obj) {
|
||
|
if (typeof obj == 'function' || Object(obj) !== obj) {
|
||
|
return obj
|
||
|
}
|
||
|
var res = new obj.constructor()
|
||
|
for (var key in obj)
|
||
|
if (obj[has](key)) {
|
||
|
res[key] = clone(obj[key])
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
Snap._.clone = clone
|
||
|
function repush(array, item) {
|
||
|
for (var i = 0, ii = array.length; i < ii; i++)
|
||
|
if (array[i] === item) {
|
||
|
return array.push(array.splice(i, 1)[0])
|
||
|
}
|
||
|
}
|
||
|
function cacher(f, scope, postprocessor) {
|
||
|
function newf() {
|
||
|
var arg = Array.prototype.slice.call(arguments, 0),
|
||
|
args = arg.join('\u2400'),
|
||
|
cache = (newf.cache = newf.cache || {}),
|
||
|
count = (newf.count = newf.count || [])
|
||
|
if (cache[has](args)) {
|
||
|
repush(count, args)
|
||
|
return postprocessor ? postprocessor(cache[args]) : cache[args]
|
||
|
}
|
||
|
count.length >= 1e3 && delete cache[count.shift()]
|
||
|
count.push(args)
|
||
|
cache[args] = f.apply(scope, arg)
|
||
|
return postprocessor ? postprocessor(cache[args]) : cache[args]
|
||
|
}
|
||
|
return newf
|
||
|
}
|
||
|
Snap._.cacher = cacher
|
||
|
function angle(x1, y1, x2, y2, x3, y3) {
|
||
|
if (x3 == null) {
|
||
|
var x = x1 - x2,
|
||
|
y = y1 - y2
|
||
|
if (!x && !y) {
|
||
|
return 0
|
||
|
}
|
||
|
return (180 + (math.atan2(-y, -x) * 180) / PI + 360) % 360
|
||
|
} else {
|
||
|
return angle(x1, y1, x3, y3) - angle(x2, y2, x3, y3)
|
||
|
}
|
||
|
}
|
||
|
function rad(deg) {
|
||
|
return ((deg % 360) * PI) / 180
|
||
|
}
|
||
|
function deg(rad) {
|
||
|
return ((rad * 180) / PI) % 360
|
||
|
}
|
||
|
function x_y() {
|
||
|
return this.x + S + this.y
|
||
|
}
|
||
|
function x_y_w_h() {
|
||
|
return this.x + S + this.y + S + this.width + ' \xd7 ' + this.height
|
||
|
}
|
||
|
|
||
|
/*\
|
||
|
* Snap.rad
|
||
|
[ method ]
|
||
|
**
|
||
|
* Transform angle to radians
|
||
|
- deg (number) angle in degrees
|
||
|
= (number) angle in radians
|
||
|
\*/
|
||
|
Snap.rad = rad
|
||
|
/*\
|
||
|
* Snap.deg
|
||
|
[ method ]
|
||
|
**
|
||
|
* Transform angle to degrees
|
||
|
- rad (number) angle in radians
|
||
|
= (number) angle in degrees
|
||
|
\*/
|
||
|
Snap.deg = deg
|
||
|
/*\
|
||
|
* Snap.sin
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.sin()` only works with degrees, not radians.
|
||
|
- angle (number) angle in degrees
|
||
|
= (number) sin
|
||
|
\*/
|
||
|
Snap.sin = function (angle) {
|
||
|
return math.sin(Snap.rad(angle))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.tan
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.tan()` only works with degrees, not radians.
|
||
|
- angle (number) angle in degrees
|
||
|
= (number) tan
|
||
|
\*/
|
||
|
Snap.tan = function (angle) {
|
||
|
return math.tan(Snap.rad(angle))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.cos
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.cos()` only works with degrees, not radians.
|
||
|
- angle (number) angle in degrees
|
||
|
= (number) cos
|
||
|
\*/
|
||
|
Snap.cos = function (angle) {
|
||
|
return math.cos(Snap.rad(angle))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.asin
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.asin()` only works with degrees, not radians.
|
||
|
- num (number) value
|
||
|
= (number) asin in degrees
|
||
|
\*/
|
||
|
Snap.asin = function (num) {
|
||
|
return Snap.deg(math.asin(num))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.acos
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.acos()` only works with degrees, not radians.
|
||
|
- num (number) value
|
||
|
= (number) acos in degrees
|
||
|
\*/
|
||
|
Snap.acos = function (num) {
|
||
|
return Snap.deg(math.acos(num))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.atan
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.atan()` only works with degrees, not radians.
|
||
|
- num (number) value
|
||
|
= (number) atan in degrees
|
||
|
\*/
|
||
|
Snap.atan = function (num) {
|
||
|
return Snap.deg(math.atan(num))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.atan2
|
||
|
[ method ]
|
||
|
**
|
||
|
* Equivalent to `Math.atan2()` only works with degrees, not radians.
|
||
|
- x (number) value
|
||
|
- y (number) value
|
||
|
= (number) atan2 in degrees
|
||
|
\*/
|
||
|
Snap.atan2 = function (x, y) {
|
||
|
return Snap.deg(math.atan2(x, y))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.angle
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns an angle between two or three points
|
||
|
- x1 (number) x coord of first point
|
||
|
- y1 (number) y coord of first point
|
||
|
- x2 (number) x coord of second point
|
||
|
- y2 (number) y coord of second point
|
||
|
- x3 (number) #optional x coord of third point
|
||
|
- y3 (number) #optional y coord of third point
|
||
|
= (number) angle in degrees
|
||
|
\*/
|
||
|
Snap.angle = angle
|
||
|
/*\
|
||
|
* Snap.len
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns distance between two points
|
||
|
- x1 (number) x coord of first point
|
||
|
- y1 (number) y coord of first point
|
||
|
- x2 (number) x coord of second point
|
||
|
- y2 (number) y coord of second point
|
||
|
= (number) distance
|
||
|
\*/
|
||
|
Snap.len = function (x1, y1, x2, y2) {
|
||
|
return Math.sqrt(Snap.len2(x1, y1, x2, y2))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.len2
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns squared distance between two points
|
||
|
- x1 (number) x coord of first point
|
||
|
- y1 (number) y coord of first point
|
||
|
- x2 (number) x coord of second point
|
||
|
- y2 (number) y coord of second point
|
||
|
= (number) distance
|
||
|
\*/
|
||
|
Snap.len2 = function (x1, y1, x2, y2) {
|
||
|
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.closestPoint
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns closest point to a given one on a given path.
|
||
|
- path (Element) path element
|
||
|
- x (number) x coord of a point
|
||
|
- y (number) y coord of a point
|
||
|
= (object) in format
|
||
|
{
|
||
|
x (number) x coord of the point on the path
|
||
|
y (number) y coord of the point on the path
|
||
|
length (number) length of the path to the point
|
||
|
distance (number) distance from the given point to the path
|
||
|
}
|
||
|
\*/
|
||
|
// Copied from http://bl.ocks.org/mbostock/8027637
|
||
|
Snap.closestPoint = function (path, x, y) {
|
||
|
function distance2(p) {
|
||
|
var dx = p.x - x,
|
||
|
dy = p.y - y
|
||
|
return dx * dx + dy * dy
|
||
|
}
|
||
|
var pathNode = path.node,
|
||
|
pathLength = pathNode.getTotalLength(),
|
||
|
precision = (pathLength / pathNode.pathSegList.numberOfItems) * 0.125,
|
||
|
best,
|
||
|
bestLength,
|
||
|
bestDistance = Infinity
|
||
|
|
||
|
// linear scan for coarse approximation
|
||
|
for (
|
||
|
var scan, scanLength = 0, scanDistance;
|
||
|
scanLength <= pathLength;
|
||
|
scanLength += precision
|
||
|
) {
|
||
|
if (
|
||
|
(scanDistance = distance2(
|
||
|
(scan = pathNode.getPointAtLength(scanLength))
|
||
|
)) < bestDistance
|
||
|
) {
|
||
|
best = scan
|
||
|
bestLength = scanLength
|
||
|
bestDistance = scanDistance
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// binary search for precise estimate
|
||
|
precision *= 0.5
|
||
|
while (precision > 0.5) {
|
||
|
var before, after, beforeLength, afterLength, beforeDistance, afterDistance
|
||
|
if (
|
||
|
(beforeLength = bestLength - precision) >= 0 &&
|
||
|
(beforeDistance = distance2(
|
||
|
(before = pathNode.getPointAtLength(beforeLength))
|
||
|
)) < bestDistance
|
||
|
) {
|
||
|
best = before
|
||
|
bestLength = beforeLength
|
||
|
bestDistance = beforeDistance
|
||
|
} else if (
|
||
|
(afterLength = bestLength + precision) <= pathLength &&
|
||
|
(afterDistance = distance2(
|
||
|
(after = pathNode.getPointAtLength(afterLength))
|
||
|
)) < bestDistance
|
||
|
) {
|
||
|
best = after
|
||
|
bestLength = afterLength
|
||
|
bestDistance = afterDistance
|
||
|
} else {
|
||
|
precision *= 0.5
|
||
|
}
|
||
|
}
|
||
|
|
||
|
best = {
|
||
|
x: best.x,
|
||
|
y: best.y,
|
||
|
length: bestLength,
|
||
|
distance: Math.sqrt(bestDistance)
|
||
|
}
|
||
|
return best
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.is
|
||
|
[ method ]
|
||
|
**
|
||
|
* Handy replacement for the `typeof` operator
|
||
|
- o (…) any object or primitive
|
||
|
- type (string) name of the type, e.g., `string`, `function`, `number`, etc.
|
||
|
= (boolean) `true` if given value is of given type
|
||
|
\*/
|
||
|
Snap.is = is
|
||
|
/*\
|
||
|
* Snap.snapTo
|
||
|
[ method ]
|
||
|
**
|
||
|
* Snaps given value to given grid
|
||
|
- values (array|number) given array of values or step of the grid
|
||
|
- value (number) value to adjust
|
||
|
- tolerance (number) #optional maximum distance to the target value that would trigger the snap. Default is `10`.
|
||
|
= (number) adjusted value
|
||
|
\*/
|
||
|
Snap.snapTo = function (values, value, tolerance) {
|
||
|
tolerance = is(tolerance, 'finite') ? tolerance : 10
|
||
|
if (is(values, 'array')) {
|
||
|
var i = values.length
|
||
|
while (i--)
|
||
|
if (abs(values[i] - value) <= tolerance) {
|
||
|
return values[i]
|
||
|
}
|
||
|
} else {
|
||
|
values = +values
|
||
|
var rem = value % values
|
||
|
if (rem < tolerance) {
|
||
|
return value - rem
|
||
|
}
|
||
|
if (rem > values - tolerance) {
|
||
|
return value - rem + values
|
||
|
}
|
||
|
}
|
||
|
return value
|
||
|
}
|
||
|
// Colour
|
||
|
/*\
|
||
|
* Snap.getRGB
|
||
|
[ method ]
|
||
|
**
|
||
|
* Parses color string as RGB object
|
||
|
- color (string) color string in one of the following formats:
|
||
|
# <ul>
|
||
|
# <li>Color name (<code>red</code>, <code>green</code>, <code>cornflowerblue</code>, etc)</li>
|
||
|
# <li>#••• — shortened HTML color: (<code>#000</code>, <code>#fc0</code>, etc.)</li>
|
||
|
# <li>#•••••• — full length HTML color: (<code>#000000</code>, <code>#bd2300</code>)</li>
|
||
|
# <li>rgb(•••, •••, •••) — red, green and blue channels values: (<code>rgb(200, 100, 0)</code>)</li>
|
||
|
# <li>rgba(•••, •••, •••, •••) — also with opacity</li>
|
||
|
# <li>rgb(•••%, •••%, •••%) — same as above, but in %: (<code>rgb(100%, 175%, 0%)</code>)</li>
|
||
|
# <li>rgba(•••%, •••%, •••%, •••%) — also with opacity</li>
|
||
|
# <li>hsb(•••, •••, •••) — hue, saturation and brightness values: (<code>hsb(0.5, 0.25, 1)</code>)</li>
|
||
|
# <li>hsba(•••, •••, •••, •••) — also with opacity</li>
|
||
|
# <li>hsb(•••%, •••%, •••%) — same as above, but in %</li>
|
||
|
# <li>hsba(•••%, •••%, •••%, •••%) — also with opacity</li>
|
||
|
# <li>hsl(•••, •••, •••) — hue, saturation and luminosity values: (<code>hsb(0.5, 0.25, 0.5)</code>)</li>
|
||
|
# <li>hsla(•••, •••, •••, •••) — also with opacity</li>
|
||
|
# <li>hsl(•••%, •••%, •••%) — same as above, but in %</li>
|
||
|
# <li>hsla(•••%, •••%, •••%, •••%) — also with opacity</li>
|
||
|
# </ul>
|
||
|
* Note that `%` can be used any time: `rgb(20%, 255, 50%)`.
|
||
|
= (object) RGB object in the following format:
|
||
|
o {
|
||
|
o r (number) red,
|
||
|
o g (number) green,
|
||
|
o b (number) blue,
|
||
|
o hex (string) color in HTML/CSS format: #••••••,
|
||
|
o error (boolean) true if string can't be parsed
|
||
|
o }
|
||
|
\*/
|
||
|
Snap.getRGB = cacher(function (colour) {
|
||
|
if (!colour || !!((colour = Str(colour)).indexOf('-') + 1)) {
|
||
|
return {
|
||
|
r: -1,
|
||
|
g: -1,
|
||
|
b: -1,
|
||
|
hex: 'none',
|
||
|
error: 1,
|
||
|
toString: rgbtoString
|
||
|
}
|
||
|
}
|
||
|
if (colour == 'none') {
|
||
|
return { r: -1, g: -1, b: -1, hex: 'none', toString: rgbtoString }
|
||
|
}
|
||
|
!(
|
||
|
hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == '#'
|
||
|
) && (colour = toHex(colour))
|
||
|
if (!colour) {
|
||
|
return {
|
||
|
r: -1,
|
||
|
g: -1,
|
||
|
b: -1,
|
||
|
hex: 'none',
|
||
|
error: 1,
|
||
|
toString: rgbtoString
|
||
|
}
|
||
|
}
|
||
|
var res,
|
||
|
red,
|
||
|
green,
|
||
|
blue,
|
||
|
opacity,
|
||
|
t,
|
||
|
values,
|
||
|
rgb = colour.match(colourRegExp)
|
||
|
if (rgb) {
|
||
|
if (rgb[2]) {
|
||
|
blue = toInt(rgb[2].substring(5), 16)
|
||
|
green = toInt(rgb[2].substring(3, 5), 16)
|
||
|
red = toInt(rgb[2].substring(1, 3), 16)
|
||
|
}
|
||
|
if (rgb[3]) {
|
||
|
blue = toInt((t = rgb[3].charAt(3)) + t, 16)
|
||
|
green = toInt((t = rgb[3].charAt(2)) + t, 16)
|
||
|
red = toInt((t = rgb[3].charAt(1)) + t, 16)
|
||
|
}
|
||
|
if (rgb[4]) {
|
||
|
values = rgb[4].split(commaSpaces)
|
||
|
red = toFloat(values[0])
|
||
|
values[0].slice(-1) == '%' && (red *= 2.55)
|
||
|
green = toFloat(values[1])
|
||
|
values[1].slice(-1) == '%' && (green *= 2.55)
|
||
|
blue = toFloat(values[2])
|
||
|
values[2].slice(-1) == '%' && (blue *= 2.55)
|
||
|
rgb[1].toLowerCase().slice(0, 4) == 'rgba' &&
|
||
|
(opacity = toFloat(values[3]))
|
||
|
values[3] && values[3].slice(-1) == '%' && (opacity /= 100)
|
||
|
}
|
||
|
if (rgb[5]) {
|
||
|
values = rgb[5].split(commaSpaces)
|
||
|
red = toFloat(values[0])
|
||
|
values[0].slice(-1) == '%' && (red /= 100)
|
||
|
green = toFloat(values[1])
|
||
|
values[1].slice(-1) == '%' && (green /= 100)
|
||
|
blue = toFloat(values[2])
|
||
|
values[2].slice(-1) == '%' && (blue /= 100)
|
||
|
;(values[0].slice(-3) == 'deg' || values[0].slice(-1) == '\xb0') &&
|
||
|
(red /= 360)
|
||
|
rgb[1].toLowerCase().slice(0, 4) == 'hsba' &&
|
||
|
(opacity = toFloat(values[3]))
|
||
|
values[3] && values[3].slice(-1) == '%' && (opacity /= 100)
|
||
|
return Snap.hsb2rgb(red, green, blue, opacity)
|
||
|
}
|
||
|
if (rgb[6]) {
|
||
|
values = rgb[6].split(commaSpaces)
|
||
|
red = toFloat(values[0])
|
||
|
values[0].slice(-1) == '%' && (red /= 100)
|
||
|
green = toFloat(values[1])
|
||
|
values[1].slice(-1) == '%' && (green /= 100)
|
||
|
blue = toFloat(values[2])
|
||
|
values[2].slice(-1) == '%' && (blue /= 100)
|
||
|
;(values[0].slice(-3) == 'deg' || values[0].slice(-1) == '\xb0') &&
|
||
|
(red /= 360)
|
||
|
rgb[1].toLowerCase().slice(0, 4) == 'hsla' &&
|
||
|
(opacity = toFloat(values[3]))
|
||
|
values[3] && values[3].slice(-1) == '%' && (opacity /= 100)
|
||
|
return Snap.hsl2rgb(red, green, blue, opacity)
|
||
|
}
|
||
|
red = mmin(math.round(red), 255)
|
||
|
green = mmin(math.round(green), 255)
|
||
|
blue = mmin(math.round(blue), 255)
|
||
|
opacity = mmin(mmax(opacity, 0), 1)
|
||
|
rgb = { r: red, g: green, b: blue, toString: rgbtoString }
|
||
|
rgb.hex =
|
||
|
'#' + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1)
|
||
|
rgb.opacity = is(opacity, 'finite') ? opacity : 1
|
||
|
return rgb
|
||
|
}
|
||
|
return { r: -1, g: -1, b: -1, hex: 'none', error: 1, toString: rgbtoString }
|
||
|
}, Snap)
|
||
|
/*\
|
||
|
* Snap.hsb
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts HSB values to a hex representation of the color
|
||
|
- h (number) hue
|
||
|
- s (number) saturation
|
||
|
- b (number) value or brightness
|
||
|
= (string) hex representation of the color
|
||
|
\*/
|
||
|
Snap.hsb = cacher(function (h, s, b) {
|
||
|
return Snap.hsb2rgb(h, s, b).hex
|
||
|
})
|
||
|
/*\
|
||
|
* Snap.hsl
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts HSL values to a hex representation of the color
|
||
|
- h (number) hue
|
||
|
- s (number) saturation
|
||
|
- l (number) luminosity
|
||
|
= (string) hex representation of the color
|
||
|
\*/
|
||
|
Snap.hsl = cacher(function (h, s, l) {
|
||
|
return Snap.hsl2rgb(h, s, l).hex
|
||
|
})
|
||
|
/*\
|
||
|
* Snap.rgb
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts RGB values to a hex representation of the color
|
||
|
- r (number) red
|
||
|
- g (number) green
|
||
|
- b (number) blue
|
||
|
= (string) hex representation of the color
|
||
|
\*/
|
||
|
Snap.rgb = cacher(function (r, g, b, o) {
|
||
|
if (is(o, 'finite')) {
|
||
|
var round = math.round
|
||
|
return 'rgba(' + [round(r), round(g), round(b), +o.toFixed(2)] + ')'
|
||
|
}
|
||
|
return '#' + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1)
|
||
|
})
|
||
|
var toHex = function (color) {
|
||
|
var i =
|
||
|
glob.doc.getElementsByTagName('head')[0] ||
|
||
|
glob.doc.getElementsByTagName('svg')[0],
|
||
|
red = 'rgb(255, 0, 0)'
|
||
|
toHex = cacher(function (color) {
|
||
|
if (color.toLowerCase() == 'red') {
|
||
|
return red
|
||
|
}
|
||
|
i.style.color = red
|
||
|
i.style.color = color
|
||
|
var out = glob.doc.defaultView
|
||
|
.getComputedStyle(i, E)
|
||
|
.getPropertyValue('color')
|
||
|
return out == red ? null : out
|
||
|
})
|
||
|
return toHex(color)
|
||
|
},
|
||
|
hsbtoString = function () {
|
||
|
return 'hsb(' + [this.h, this.s, this.b] + ')'
|
||
|
},
|
||
|
hsltoString = function () {
|
||
|
return 'hsl(' + [this.h, this.s, this.l] + ')'
|
||
|
},
|
||
|
rgbtoString = function () {
|
||
|
return this.opacity == 1 || this.opacity == null
|
||
|
? this.hex
|
||
|
: 'rgba(' + [this.r, this.g, this.b, this.opacity] + ')'
|
||
|
},
|
||
|
prepareRGB = function (r, g, b) {
|
||
|
if (g == null && is(r, 'object') && 'r' in r && 'g' in r && 'b' in r) {
|
||
|
b = r.b
|
||
|
g = r.g
|
||
|
r = r.r
|
||
|
}
|
||
|
if (g == null && is(r, string)) {
|
||
|
var clr = Snap.getRGB(r)
|
||
|
r = clr.r
|
||
|
g = clr.g
|
||
|
b = clr.b
|
||
|
}
|
||
|
if (r > 1 || g > 1 || b > 1) {
|
||
|
r /= 255
|
||
|
g /= 255
|
||
|
b /= 255
|
||
|
}
|
||
|
|
||
|
return [r, g, b]
|
||
|
},
|
||
|
packageRGB = function (r, g, b, o) {
|
||
|
r = math.round(r * 255)
|
||
|
g = math.round(g * 255)
|
||
|
b = math.round(b * 255)
|
||
|
var rgb = {
|
||
|
r: r,
|
||
|
g: g,
|
||
|
b: b,
|
||
|
opacity: is(o, 'finite') ? o : 1,
|
||
|
hex: Snap.rgb(r, g, b),
|
||
|
toString: rgbtoString
|
||
|
}
|
||
|
is(o, 'finite') && (rgb.opacity = o)
|
||
|
return rgb
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.color
|
||
|
[ method ]
|
||
|
**
|
||
|
* Parses the color string and returns an object featuring the color's component values
|
||
|
- clr (string) color string in one of the supported formats (see @Snap.getRGB)
|
||
|
= (object) Combined RGB/HSB object in the following format:
|
||
|
o {
|
||
|
o r (number) red,
|
||
|
o g (number) green,
|
||
|
o b (number) blue,
|
||
|
o hex (string) color in HTML/CSS format: #••••••,
|
||
|
o error (boolean) `true` if string can't be parsed,
|
||
|
o h (number) hue,
|
||
|
o s (number) saturation,
|
||
|
o v (number) value (brightness),
|
||
|
o l (number) lightness
|
||
|
o }
|
||
|
\*/
|
||
|
Snap.color = function (clr) {
|
||
|
var rgb
|
||
|
if (is(clr, 'object') && 'h' in clr && 's' in clr && 'b' in clr) {
|
||
|
rgb = Snap.hsb2rgb(clr)
|
||
|
clr.r = rgb.r
|
||
|
clr.g = rgb.g
|
||
|
clr.b = rgb.b
|
||
|
clr.opacity = 1
|
||
|
clr.hex = rgb.hex
|
||
|
} else if (is(clr, 'object') && 'h' in clr && 's' in clr && 'l' in clr) {
|
||
|
rgb = Snap.hsl2rgb(clr)
|
||
|
clr.r = rgb.r
|
||
|
clr.g = rgb.g
|
||
|
clr.b = rgb.b
|
||
|
clr.opacity = 1
|
||
|
clr.hex = rgb.hex
|
||
|
} else {
|
||
|
if (is(clr, 'string')) {
|
||
|
clr = Snap.getRGB(clr)
|
||
|
}
|
||
|
if (
|
||
|
is(clr, 'object') &&
|
||
|
'r' in clr &&
|
||
|
'g' in clr &&
|
||
|
'b' in clr &&
|
||
|
!('error' in clr)
|
||
|
) {
|
||
|
rgb = Snap.rgb2hsl(clr)
|
||
|
clr.h = rgb.h
|
||
|
clr.s = rgb.s
|
||
|
clr.l = rgb.l
|
||
|
rgb = Snap.rgb2hsb(clr)
|
||
|
clr.v = rgb.b
|
||
|
} else {
|
||
|
clr = { hex: 'none' }
|
||
|
clr.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1
|
||
|
clr.error = 1
|
||
|
}
|
||
|
}
|
||
|
clr.toString = rgbtoString
|
||
|
return clr
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.hsb2rgb
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts HSB values to an RGB object
|
||
|
- h (number) hue
|
||
|
- s (number) saturation
|
||
|
- v (number) value or brightness
|
||
|
= (object) RGB object in the following format:
|
||
|
o {
|
||
|
o r (number) red,
|
||
|
o g (number) green,
|
||
|
o b (number) blue,
|
||
|
o hex (string) color in HTML/CSS format: #••••••
|
||
|
o }
|
||
|
\*/
|
||
|
Snap.hsb2rgb = function (h, s, v, o) {
|
||
|
if (is(h, 'object') && 'h' in h && 's' in h && 'b' in h) {
|
||
|
v = h.b
|
||
|
s = h.s
|
||
|
o = h.o
|
||
|
h = h.h
|
||
|
}
|
||
|
h *= 360
|
||
|
var R, G, B, X, C
|
||
|
h = (h % 360) / 60
|
||
|
C = v * s
|
||
|
X = C * (1 - abs((h % 2) - 1))
|
||
|
R = G = B = v - C
|
||
|
|
||
|
h = ~~h
|
||
|
R += [C, X, 0, 0, X, C][h]
|
||
|
G += [X, C, C, X, 0, 0][h]
|
||
|
B += [0, 0, X, C, C, X][h]
|
||
|
return packageRGB(R, G, B, o)
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.hsl2rgb
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts HSL values to an RGB object
|
||
|
- h (number) hue
|
||
|
- s (number) saturation
|
||
|
- l (number) luminosity
|
||
|
= (object) RGB object in the following format:
|
||
|
o {
|
||
|
o r (number) red,
|
||
|
o g (number) green,
|
||
|
o b (number) blue,
|
||
|
o hex (string) color in HTML/CSS format: #••••••
|
||
|
o }
|
||
|
\*/
|
||
|
Snap.hsl2rgb = function (h, s, l, o) {
|
||
|
if (is(h, 'object') && 'h' in h && 's' in h && 'l' in h) {
|
||
|
l = h.l
|
||
|
s = h.s
|
||
|
h = h.h
|
||
|
}
|
||
|
if (h > 1 || s > 1 || l > 1) {
|
||
|
h /= 360
|
||
|
s /= 100
|
||
|
l /= 100
|
||
|
}
|
||
|
h *= 360
|
||
|
var R, G, B, X, C
|
||
|
h = (h % 360) / 60
|
||
|
C = 2 * s * (l < 0.5 ? l : 1 - l)
|
||
|
X = C * (1 - abs((h % 2) - 1))
|
||
|
R = G = B = l - C / 2
|
||
|
|
||
|
h = ~~h
|
||
|
R += [C, X, 0, 0, X, C][h]
|
||
|
G += [X, C, C, X, 0, 0][h]
|
||
|
B += [0, 0, X, C, C, X][h]
|
||
|
return packageRGB(R, G, B, o)
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.rgb2hsb
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts RGB values to an HSB object
|
||
|
- r (number) red
|
||
|
- g (number) green
|
||
|
- b (number) blue
|
||
|
= (object) HSB object in the following format:
|
||
|
o {
|
||
|
o h (number) hue,
|
||
|
o s (number) saturation,
|
||
|
o b (number) brightness
|
||
|
o }
|
||
|
\*/
|
||
|
Snap.rgb2hsb = function (r, g, b) {
|
||
|
b = prepareRGB(r, g, b)
|
||
|
r = b[0]
|
||
|
g = b[1]
|
||
|
b = b[2]
|
||
|
|
||
|
var H, S, V, C
|
||
|
V = mmax(r, g, b)
|
||
|
C = V - mmin(r, g, b)
|
||
|
H =
|
||
|
C == 0
|
||
|
? null
|
||
|
: V == r
|
||
|
? (g - b) / C
|
||
|
: V == g
|
||
|
? (b - r) / C + 2
|
||
|
: (r - g) / C + 4
|
||
|
H = (((H + 360) % 6) * 60) / 360
|
||
|
S = C == 0 ? 0 : C / V
|
||
|
return { h: H, s: S, b: V, toString: hsbtoString }
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.rgb2hsl
|
||
|
[ method ]
|
||
|
**
|
||
|
* Converts RGB values to an HSL object
|
||
|
- r (number) red
|
||
|
- g (number) green
|
||
|
- b (number) blue
|
||
|
= (object) HSL object in the following format:
|
||
|
o {
|
||
|
o h (number) hue,
|
||
|
o s (number) saturation,
|
||
|
o l (number) luminosity
|
||
|
o }
|
||
|
\*/
|
||
|
Snap.rgb2hsl = function (r, g, b) {
|
||
|
b = prepareRGB(r, g, b)
|
||
|
r = b[0]
|
||
|
g = b[1]
|
||
|
b = b[2]
|
||
|
|
||
|
var H, S, L, M, m, C
|
||
|
M = mmax(r, g, b)
|
||
|
m = mmin(r, g, b)
|
||
|
C = M - m
|
||
|
H =
|
||
|
C == 0
|
||
|
? null
|
||
|
: M == r
|
||
|
? (g - b) / C
|
||
|
: M == g
|
||
|
? (b - r) / C + 2
|
||
|
: (r - g) / C + 4
|
||
|
H = (((H + 360) % 6) * 60) / 360
|
||
|
L = (M + m) / 2
|
||
|
S = C == 0 ? 0 : L < 0.5 ? C / (2 * L) : C / (2 - 2 * L)
|
||
|
return { h: H, s: S, l: L, toString: hsltoString }
|
||
|
}
|
||
|
|
||
|
// Transformations
|
||
|
/*\
|
||
|
* Snap.parsePathString
|
||
|
[ method ]
|
||
|
**
|
||
|
* Utility method
|
||
|
**
|
||
|
* Parses given path string into an array of arrays of path segments
|
||
|
- pathString (string|array) path string or array of segments (in the last case it is returned straight away)
|
||
|
= (array) array of segments
|
||
|
\*/
|
||
|
Snap.parsePathString = function (pathString) {
|
||
|
if (!pathString) {
|
||
|
return null
|
||
|
}
|
||
|
var pth = Snap.path(pathString)
|
||
|
if (pth.arr) {
|
||
|
return Snap.path.clone(pth.arr)
|
||
|
}
|
||
|
|
||
|
var paramCounts = {
|
||
|
a: 7,
|
||
|
c: 6,
|
||
|
o: 2,
|
||
|
h: 1,
|
||
|
l: 2,
|
||
|
m: 2,
|
||
|
r: 4,
|
||
|
q: 4,
|
||
|
s: 4,
|
||
|
t: 2,
|
||
|
v: 1,
|
||
|
u: 3,
|
||
|
z: 0
|
||
|
},
|
||
|
data = []
|
||
|
if (is(pathString, 'array') && is(pathString[0], 'array')) {
|
||
|
// rough assumption
|
||
|
data = Snap.path.clone(pathString)
|
||
|
}
|
||
|
if (!data.length) {
|
||
|
Str(pathString).replace(pathCommand, function (a, b, c) {
|
||
|
var params = [],
|
||
|
name = b.toLowerCase()
|
||
|
c.replace(pathValues, function (a, b) {
|
||
|
b && params.push(+b)
|
||
|
})
|
||
|
if (name == 'm' && params.length > 2) {
|
||
|
data.push([b].concat(params.splice(0, 2)))
|
||
|
name = 'l'
|
||
|
b = b == 'm' ? 'l' : 'L'
|
||
|
}
|
||
|
if (name == 'o' && params.length == 1) {
|
||
|
data.push([b, params[0]])
|
||
|
}
|
||
|
if (name == 'r') {
|
||
|
data.push([b].concat(params))
|
||
|
} else
|
||
|
while (params.length >= paramCounts[name]) {
|
||
|
data.push([b].concat(params.splice(0, paramCounts[name])))
|
||
|
if (!paramCounts[name]) {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
data.toString = Snap.path.toString
|
||
|
pth.arr = Snap.path.clone(data)
|
||
|
return data
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.parseTransformString
|
||
|
[ method ]
|
||
|
**
|
||
|
* Utility method
|
||
|
**
|
||
|
* Parses given transform string into an array of transformations
|
||
|
- TString (string|array) transform string or array of transformations (in the last case it is returned straight away)
|
||
|
= (array) array of transformations
|
||
|
\*/
|
||
|
var parseTransformString = (Snap.parseTransformString = function (TString) {
|
||
|
if (!TString) {
|
||
|
return null
|
||
|
}
|
||
|
var paramCounts = { r: 3, s: 4, t: 2, m: 6 },
|
||
|
data = []
|
||
|
if (is(TString, 'array') && is(TString[0], 'array')) {
|
||
|
// rough assumption
|
||
|
data = Snap.path.clone(TString)
|
||
|
}
|
||
|
if (!data.length) {
|
||
|
Str(TString).replace(tCommand, function (a, b, c) {
|
||
|
var params = [],
|
||
|
name = b.toLowerCase()
|
||
|
c.replace(pathValues, function (a, b) {
|
||
|
b && params.push(+b)
|
||
|
})
|
||
|
data.push([b].concat(params))
|
||
|
})
|
||
|
}
|
||
|
data.toString = Snap.path.toString
|
||
|
return data
|
||
|
})
|
||
|
function svgTransform2string(tstr) {
|
||
|
var res = []
|
||
|
tstr = tstr.replace(
|
||
|
/(?:^|\s)(\w+)\(([^)]+)\)/g,
|
||
|
function (all, name, params) {
|
||
|
params = params.split(/\s*,\s*|\s+/)
|
||
|
if (name == 'rotate' && params.length == 1) {
|
||
|
params.push(0, 0)
|
||
|
}
|
||
|
if (name == 'scale') {
|
||
|
if (params.length > 2) {
|
||
|
params = params.slice(0, 2)
|
||
|
} else if (params.length == 2) {
|
||
|
params.push(0, 0)
|
||
|
}
|
||
|
if (params.length == 1) {
|
||
|
params.push(params[0], 0, 0)
|
||
|
}
|
||
|
}
|
||
|
if (name == 'skewX') {
|
||
|
res.push(['m', 1, 0, math.tan(rad(params[0])), 1, 0, 0])
|
||
|
} else if (name == 'skewY') {
|
||
|
res.push(['m', 1, math.tan(rad(params[0])), 0, 1, 0, 0])
|
||
|
} else {
|
||
|
res.push([name.charAt(0)].concat(params))
|
||
|
}
|
||
|
return all
|
||
|
}
|
||
|
)
|
||
|
return res
|
||
|
}
|
||
|
Snap._.svgTransform2string = svgTransform2string
|
||
|
Snap._.rgTransform = /^[a-z][\s]*-?\.?\d/i
|
||
|
function transform2matrix(tstr, bbox) {
|
||
|
var tdata = parseTransformString(tstr),
|
||
|
m = new Snap.Matrix()
|
||
|
if (tdata) {
|
||
|
for (var i = 0, ii = tdata.length; i < ii; i++) {
|
||
|
var t = tdata[i],
|
||
|
tlen = t.length,
|
||
|
command = Str(t[0]).toLowerCase(),
|
||
|
absolute = t[0] != command,
|
||
|
inver = absolute ? m.invert() : 0,
|
||
|
x1,
|
||
|
y1,
|
||
|
x2,
|
||
|
y2,
|
||
|
bb
|
||
|
if (command == 't' && tlen == 2) {
|
||
|
m.translate(t[1], 0)
|
||
|
} else if (command == 't' && tlen == 3) {
|
||
|
if (absolute) {
|
||
|
x1 = inver.x(0, 0)
|
||
|
y1 = inver.y(0, 0)
|
||
|
x2 = inver.x(t[1], t[2])
|
||
|
y2 = inver.y(t[1], t[2])
|
||
|
m.translate(x2 - x1, y2 - y1)
|
||
|
} else {
|
||
|
m.translate(t[1], t[2])
|
||
|
}
|
||
|
} else if (command == 'r') {
|
||
|
if (tlen == 2) {
|
||
|
bb = bb || bbox
|
||
|
m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2)
|
||
|
} else if (tlen == 4) {
|
||
|
if (absolute) {
|
||
|
x2 = inver.x(t[2], t[3])
|
||
|
y2 = inver.y(t[2], t[3])
|
||
|
m.rotate(t[1], x2, y2)
|
||
|
} else {
|
||
|
m.rotate(t[1], t[2], t[3])
|
||
|
}
|
||
|
}
|
||
|
} else if (command == 's') {
|
||
|
if (tlen == 2 || tlen == 3) {
|
||
|
bb = bb || bbox
|
||
|
m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2)
|
||
|
} else if (tlen == 4) {
|
||
|
if (absolute) {
|
||
|
x2 = inver.x(t[2], t[3])
|
||
|
y2 = inver.y(t[2], t[3])
|
||
|
m.scale(t[1], t[1], x2, y2)
|
||
|
} else {
|
||
|
m.scale(t[1], t[1], t[2], t[3])
|
||
|
}
|
||
|
} else if (tlen == 5) {
|
||
|
if (absolute) {
|
||
|
x2 = inver.x(t[3], t[4])
|
||
|
y2 = inver.y(t[3], t[4])
|
||
|
m.scale(t[1], t[2], x2, y2)
|
||
|
} else {
|
||
|
m.scale(t[1], t[2], t[3], t[4])
|
||
|
}
|
||
|
}
|
||
|
} else if (command == 'm' && tlen == 7) {
|
||
|
m.add(t[1], t[2], t[3], t[4], t[5], t[6])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
Snap._.transform2matrix = transform2matrix
|
||
|
Snap._unit2px = unit2px
|
||
|
var contains =
|
||
|
glob.doc.contains || glob.doc.compareDocumentPosition
|
||
|
? function (a, b) {
|
||
|
var adown = a.nodeType == 9 ? a.documentElement : a,
|
||
|
bup = b && b.parentNode
|
||
|
return (
|
||
|
a == bup ||
|
||
|
!!(
|
||
|
bup &&
|
||
|
bup.nodeType == 1 &&
|
||
|
(adown.contains
|
||
|
? adown.contains(bup)
|
||
|
: a.compareDocumentPosition &&
|
||
|
a.compareDocumentPosition(bup) & 16)
|
||
|
)
|
||
|
)
|
||
|
}
|
||
|
: function (a, b) {
|
||
|
if (b) {
|
||
|
while (b) {
|
||
|
b = b.parentNode
|
||
|
if (b == a) {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
function getSomeDefs(el) {
|
||
|
var p =
|
||
|
(el.node.ownerSVGElement && wrap(el.node.ownerSVGElement)) ||
|
||
|
(el.node.parentNode && wrap(el.node.parentNode)) ||
|
||
|
Snap.select('svg') ||
|
||
|
Snap(0, 0),
|
||
|
pdefs = p.select('defs'),
|
||
|
defs = pdefs == null ? false : pdefs.node
|
||
|
if (!defs) {
|
||
|
defs = make('defs', p.node).node
|
||
|
}
|
||
|
return defs
|
||
|
}
|
||
|
function getSomeSVG(el) {
|
||
|
return (
|
||
|
(el.node.ownerSVGElement && wrap(el.node.ownerSVGElement)) ||
|
||
|
Snap.select('svg')
|
||
|
)
|
||
|
}
|
||
|
Snap._.getSomeDefs = getSomeDefs
|
||
|
Snap._.getSomeSVG = getSomeSVG
|
||
|
function unit2px(el, name, value) {
|
||
|
var svg = getSomeSVG(el).node,
|
||
|
out = {},
|
||
|
mgr = svg.querySelector('.svg---mgr')
|
||
|
if (!mgr) {
|
||
|
mgr = $('rect')
|
||
|
$(mgr, {
|
||
|
x: -9e9,
|
||
|
y: -9e9,
|
||
|
width: 10,
|
||
|
height: 10,
|
||
|
class: 'svg---mgr',
|
||
|
fill: 'none'
|
||
|
})
|
||
|
svg.appendChild(mgr)
|
||
|
}
|
||
|
function getW(val) {
|
||
|
if (val == null) {
|
||
|
return E
|
||
|
}
|
||
|
if (val == +val) {
|
||
|
return val
|
||
|
}
|
||
|
$(mgr, { width: val })
|
||
|
try {
|
||
|
return mgr.getBBox().width
|
||
|
} catch (e) {
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
function getH(val) {
|
||
|
if (val == null) {
|
||
|
return E
|
||
|
}
|
||
|
if (val == +val) {
|
||
|
return val
|
||
|
}
|
||
|
$(mgr, { height: val })
|
||
|
try {
|
||
|
return mgr.getBBox().height
|
||
|
} catch (e) {
|
||
|
return 0
|
||
|
}
|
||
|
}
|
||
|
function set(nam, f) {
|
||
|
if (name == null) {
|
||
|
out[nam] = f(el.attr(nam) || 0)
|
||
|
} else if (nam == name) {
|
||
|
out = f(value == null ? el.attr(nam) || 0 : value)
|
||
|
}
|
||
|
}
|
||
|
switch (el.type) {
|
||
|
case 'rect':
|
||
|
set('rx', getW)
|
||
|
set('ry', getH)
|
||
|
case 'image':
|
||
|
set('width', getW)
|
||
|
set('height', getH)
|
||
|
case 'text':
|
||
|
set('x', getW)
|
||
|
set('y', getH)
|
||
|
break
|
||
|
case 'circle':
|
||
|
set('cx', getW)
|
||
|
set('cy', getH)
|
||
|
set('r', getW)
|
||
|
break
|
||
|
case 'ellipse':
|
||
|
set('cx', getW)
|
||
|
set('cy', getH)
|
||
|
set('rx', getW)
|
||
|
set('ry', getH)
|
||
|
break
|
||
|
case 'line':
|
||
|
set('x1', getW)
|
||
|
set('x2', getW)
|
||
|
set('y1', getH)
|
||
|
set('y2', getH)
|
||
|
break
|
||
|
case 'marker':
|
||
|
set('refX', getW)
|
||
|
set('markerWidth', getW)
|
||
|
set('refY', getH)
|
||
|
set('markerHeight', getH)
|
||
|
break
|
||
|
case 'radialGradient':
|
||
|
set('fx', getW)
|
||
|
set('fy', getH)
|
||
|
break
|
||
|
case 'tspan':
|
||
|
set('dx', getW)
|
||
|
set('dy', getH)
|
||
|
break
|
||
|
default:
|
||
|
set(name, getW)
|
||
|
}
|
||
|
svg.removeChild(mgr)
|
||
|
return out
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.select
|
||
|
[ method ]
|
||
|
**
|
||
|
* Wraps a DOM element specified by CSS selector as @Element
|
||
|
- query (string) CSS selector of the element
|
||
|
= (Element) the current element
|
||
|
\*/
|
||
|
Snap.select = function (query) {
|
||
|
query = Str(query).replace(/([^\\]):/g, '$1\\:')
|
||
|
return wrap(glob.doc.querySelector(query))
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.selectAll
|
||
|
[ method ]
|
||
|
**
|
||
|
* Wraps DOM elements specified by CSS selector as set or array of @Element
|
||
|
- query (string) CSS selector of the element
|
||
|
= (Element) the current element
|
||
|
\*/
|
||
|
Snap.selectAll = function (query) {
|
||
|
var nodelist = glob.doc.querySelectorAll(query),
|
||
|
set = (Snap.set || Array)()
|
||
|
for (var i = 0; i < nodelist.length; i++) {
|
||
|
set.push(wrap(nodelist[i]))
|
||
|
}
|
||
|
return set
|
||
|
}
|
||
|
|
||
|
function add2group(list) {
|
||
|
if (!is(list, 'array')) {
|
||
|
list = Array.prototype.slice.call(arguments, 0)
|
||
|
}
|
||
|
var i = 0,
|
||
|
j = 0,
|
||
|
node = this.node
|
||
|
while (this[i]) delete this[i++]
|
||
|
for (i = 0; i < list.length; i++) {
|
||
|
if (list[i].type == 'set') {
|
||
|
list[i].forEach(function (el) {
|
||
|
node.appendChild(el.node)
|
||
|
})
|
||
|
} else {
|
||
|
node.appendChild(list[i].node)
|
||
|
}
|
||
|
}
|
||
|
var children = node.childNodes
|
||
|
for (i = 0; i < children.length; i++) {
|
||
|
this[j++] = wrap(children[i])
|
||
|
}
|
||
|
return this
|
||
|
}
|
||
|
// Hub garbage collector every 10s
|
||
|
setInterval(function () {
|
||
|
for (var key in hub)
|
||
|
if (hub[has](key)) {
|
||
|
var el = hub[key],
|
||
|
node = el.node
|
||
|
if (
|
||
|
(el.type != 'svg' && !node.ownerSVGElement) ||
|
||
|
(el.type == 'svg' &&
|
||
|
(!node.parentNode ||
|
||
|
('ownerSVGElement' in node.parentNode && !node.ownerSVGElement)))
|
||
|
) {
|
||
|
delete hub[key]
|
||
|
}
|
||
|
}
|
||
|
}, 1e4)
|
||
|
function Element(el) {
|
||
|
if (el.snap in hub) {
|
||
|
return hub[el.snap]
|
||
|
}
|
||
|
var svg
|
||
|
try {
|
||
|
svg = el.ownerSVGElement
|
||
|
} catch (e) {}
|
||
|
/*\
|
||
|
* Element.node
|
||
|
[ property (object) ]
|
||
|
**
|
||
|
* Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
|
||
|
> Usage
|
||
|
| // draw a circle at coordinate 10,10 with radius of 10
|
||
|
| var c = paper.circle(10, 10, 10);
|
||
|
| c.node.onclick = function () {
|
||
|
| c.attr("fill", "red");
|
||
|
| };
|
||
|
\*/
|
||
|
this.node = el
|
||
|
if (svg) {
|
||
|
this.paper = new Paper(svg)
|
||
|
}
|
||
|
/*\
|
||
|
* Element.type
|
||
|
[ property (string) ]
|
||
|
**
|
||
|
* SVG tag name of the given element.
|
||
|
\*/
|
||
|
this.type = el.tagName || el.nodeName
|
||
|
var id = (this.id = ID(this))
|
||
|
this.anims = {}
|
||
|
this._ = {
|
||
|
transform: []
|
||
|
}
|
||
|
el.snap = id
|
||
|
hub[id] = this
|
||
|
if (this.type == 'g') {
|
||
|
this.add = add2group
|
||
|
}
|
||
|
if (this.type in { g: 1, mask: 1, pattern: 1, symbol: 1 }) {
|
||
|
for (var method in Paper.prototype)
|
||
|
if (Paper.prototype[has](method)) {
|
||
|
this[method] = Paper.prototype[method]
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*\
|
||
|
* Element.attr
|
||
|
[ method ]
|
||
|
**
|
||
|
* Gets or sets given attributes of the element.
|
||
|
**
|
||
|
- params (object) contains key-value pairs of attributes you want to set
|
||
|
* or
|
||
|
- param (string) name of the attribute
|
||
|
= (Element) the current element
|
||
|
* or
|
||
|
= (string) value of attribute
|
||
|
> Usage
|
||
|
| el.attr({
|
||
|
| fill: "#fc0",
|
||
|
| stroke: "#000",
|
||
|
| strokeWidth: 2, // CamelCase...
|
||
|
| "fill-opacity": 0.5, // or dash-separated names
|
||
|
| width: "*=2" // prefixed values
|
||
|
| });
|
||
|
| console.log(el.attr("fill")); // #fc0
|
||
|
* Prefixed values in format `"+=10"` supported. All four operations
|
||
|
* (`+`, `-`, `*` and `/`) could be used. Optionally you can use units for `+`
|
||
|
* and `-`: `"+=2em"`.
|
||
|
\*/
|
||
|
Element.prototype.attr = function (params, value) {
|
||
|
var el = this,
|
||
|
node = el.node
|
||
|
if (!params) {
|
||
|
if (node.nodeType != 1) {
|
||
|
return {
|
||
|
text: node.nodeValue
|
||
|
}
|
||
|
}
|
||
|
var attr = node.attributes,
|
||
|
out = {}
|
||
|
for (var i = 0, ii = attr.length; i < ii; i++) {
|
||
|
out[attr[i].nodeName] = attr[i].nodeValue
|
||
|
}
|
||
|
return out
|
||
|
}
|
||
|
if (is(params, 'string')) {
|
||
|
if (arguments.length > 1) {
|
||
|
var json = {}
|
||
|
json[params] = value
|
||
|
params = json
|
||
|
} else {
|
||
|
return eve('snap.util.getattr.' + params, el).firstDefined()
|
||
|
}
|
||
|
}
|
||
|
for (var att in params) {
|
||
|
if (params[has](att)) {
|
||
|
eve('snap.util.attr.' + att, el, params[att])
|
||
|
}
|
||
|
}
|
||
|
return el
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.parse
|
||
|
[ method ]
|
||
|
**
|
||
|
* Parses SVG fragment and converts it into a @Fragment
|
||
|
**
|
||
|
- svg (string) SVG string
|
||
|
= (Fragment) the @Fragment
|
||
|
\*/
|
||
|
Snap.parse = function (svg) {
|
||
|
var f = glob.doc.createDocumentFragment(),
|
||
|
full = true,
|
||
|
div = glob.doc.createElement('div')
|
||
|
svg = Str(svg)
|
||
|
if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) {
|
||
|
svg = '<svg>' + svg + '</svg>'
|
||
|
full = false
|
||
|
}
|
||
|
div.innerHTML = svg
|
||
|
svg = div.getElementsByTagName('svg')[0]
|
||
|
if (svg) {
|
||
|
if (full) {
|
||
|
f = svg
|
||
|
} else {
|
||
|
while (svg.firstChild) {
|
||
|
f.appendChild(svg.firstChild)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return new Fragment(f)
|
||
|
}
|
||
|
function Fragment(frag) {
|
||
|
this.node = frag
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.fragment
|
||
|
[ method ]
|
||
|
**
|
||
|
* Creates a DOM fragment from a given list of elements or strings
|
||
|
**
|
||
|
- varargs (…) SVG string
|
||
|
= (Fragment) the @Fragment
|
||
|
\*/
|
||
|
Snap.fragment = function () {
|
||
|
var args = Array.prototype.slice.call(arguments, 0),
|
||
|
f = glob.doc.createDocumentFragment()
|
||
|
for (var i = 0, ii = args.length; i < ii; i++) {
|
||
|
var item = args[i]
|
||
|
if (item.node && item.node.nodeType) {
|
||
|
f.appendChild(item.node)
|
||
|
}
|
||
|
if (item.nodeType) {
|
||
|
f.appendChild(item)
|
||
|
}
|
||
|
if (typeof item == 'string') {
|
||
|
f.appendChild(Snap.parse(item).node)
|
||
|
}
|
||
|
}
|
||
|
return new Fragment(f)
|
||
|
}
|
||
|
|
||
|
function make(name, parent) {
|
||
|
var res = $(name)
|
||
|
parent.appendChild(res)
|
||
|
var el = wrap(res)
|
||
|
return el
|
||
|
}
|
||
|
function Paper(w, h) {
|
||
|
var res,
|
||
|
desc,
|
||
|
defs,
|
||
|
proto = Paper.prototype
|
||
|
if (w && w.tagName && w.tagName.toLowerCase() == 'svg') {
|
||
|
if (w.snap in hub) {
|
||
|
return hub[w.snap]
|
||
|
}
|
||
|
var doc = w.ownerDocument
|
||
|
res = new Element(w)
|
||
|
desc = w.getElementsByTagName('desc')[0]
|
||
|
defs = w.getElementsByTagName('defs')[0]
|
||
|
if (!desc) {
|
||
|
desc = $('desc')
|
||
|
desc.appendChild(doc.createTextNode('Created with Snap'))
|
||
|
res.node.appendChild(desc)
|
||
|
}
|
||
|
if (!defs) {
|
||
|
defs = $('defs')
|
||
|
res.node.appendChild(defs)
|
||
|
}
|
||
|
res.defs = defs
|
||
|
for (var key in proto)
|
||
|
if (proto[has](key)) {
|
||
|
res[key] = proto[key]
|
||
|
}
|
||
|
res.paper = res.root = res
|
||
|
} else {
|
||
|
res = make('svg', glob.doc.body)
|
||
|
$(res.node, {
|
||
|
height: h,
|
||
|
version: 1.1,
|
||
|
width: w,
|
||
|
xmlns: xmlns
|
||
|
})
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
function wrap(dom) {
|
||
|
if (!dom) {
|
||
|
return dom
|
||
|
}
|
||
|
if (dom instanceof Element || dom instanceof Fragment) {
|
||
|
return dom
|
||
|
}
|
||
|
if (dom.tagName && dom.tagName.toLowerCase() == 'svg') {
|
||
|
return new Paper(dom)
|
||
|
}
|
||
|
if (
|
||
|
dom.tagName &&
|
||
|
dom.tagName.toLowerCase() == 'object' &&
|
||
|
dom.type == 'image/svg+xml'
|
||
|
) {
|
||
|
return new Paper(dom.contentDocument.getElementsByTagName('svg')[0])
|
||
|
}
|
||
|
return new Element(dom)
|
||
|
}
|
||
|
|
||
|
Snap._.make = make
|
||
|
Snap._.wrap = wrap
|
||
|
/*\
|
||
|
* Paper.el
|
||
|
[ method ]
|
||
|
**
|
||
|
* Creates an element on paper with a given name and no attributes
|
||
|
**
|
||
|
- name (string) tag name
|
||
|
- attr (object) attributes
|
||
|
= (Element) the current element
|
||
|
> Usage
|
||
|
| var c = paper.circle(10, 10, 10); // is the same as...
|
||
|
| var c = paper.el("circle").attr({
|
||
|
| cx: 10,
|
||
|
| cy: 10,
|
||
|
| r: 10
|
||
|
| });
|
||
|
| // and the same as
|
||
|
| var c = paper.el("circle", {
|
||
|
| cx: 10,
|
||
|
| cy: 10,
|
||
|
| r: 10
|
||
|
| });
|
||
|
\*/
|
||
|
Paper.prototype.el = function (name, attr) {
|
||
|
var el = make(name, this.node)
|
||
|
attr && el.attr(attr)
|
||
|
return el
|
||
|
}
|
||
|
/*\
|
||
|
* Element.children
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns array of all the children of the element.
|
||
|
= (array) array of Elements
|
||
|
\*/
|
||
|
Element.prototype.children = function () {
|
||
|
var out = [],
|
||
|
ch = this.node.childNodes
|
||
|
for (var i = 0, ii = ch.length; i < ii; i++) {
|
||
|
out[i] = Snap(ch[i])
|
||
|
}
|
||
|
return out
|
||
|
}
|
||
|
function jsonFiller(root, o) {
|
||
|
for (var i = 0, ii = root.length; i < ii; i++) {
|
||
|
var item = {
|
||
|
type: root[i].type,
|
||
|
attr: root[i].attr()
|
||
|
},
|
||
|
children = root[i].children()
|
||
|
o.push(item)
|
||
|
if (children.length) {
|
||
|
jsonFiller(children, (item.childNodes = []))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*\
|
||
|
* Element.toJSON
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns object representation of the given element and all its children.
|
||
|
= (object) in format
|
||
|
o {
|
||
|
o type (string) this.type,
|
||
|
o attr (object) attributes map,
|
||
|
o childNodes (array) optional array of children in the same format
|
||
|
o }
|
||
|
\*/
|
||
|
Element.prototype.toJSON = function () {
|
||
|
var out = []
|
||
|
jsonFiller([this], out)
|
||
|
return out[0]
|
||
|
}
|
||
|
// default
|
||
|
eve.on('snap.util.getattr', function () {
|
||
|
var att = eve.nt()
|
||
|
att = att.substring(att.lastIndexOf('.') + 1)
|
||
|
var css = att.replace(/[A-Z]/g, function (letter) {
|
||
|
return '-' + letter.toLowerCase()
|
||
|
})
|
||
|
if (cssAttr[has](css)) {
|
||
|
return this.node.ownerDocument.defaultView
|
||
|
.getComputedStyle(this.node, null)
|
||
|
.getPropertyValue(css)
|
||
|
} else {
|
||
|
return $(this.node, att)
|
||
|
}
|
||
|
})
|
||
|
var cssAttr = {
|
||
|
'alignment-baseline': 0,
|
||
|
'baseline-shift': 0,
|
||
|
clip: 0,
|
||
|
'clip-path': 0,
|
||
|
'clip-rule': 0,
|
||
|
color: 0,
|
||
|
'color-interpolation': 0,
|
||
|
'color-interpolation-filters': 0,
|
||
|
'color-profile': 0,
|
||
|
'color-rendering': 0,
|
||
|
cursor: 0,
|
||
|
direction: 0,
|
||
|
display: 0,
|
||
|
'dominant-baseline': 0,
|
||
|
'enable-background': 0,
|
||
|
fill: 0,
|
||
|
'fill-opacity': 0,
|
||
|
'fill-rule': 0,
|
||
|
filter: 0,
|
||
|
'flood-color': 0,
|
||
|
'flood-opacity': 0,
|
||
|
font: 0,
|
||
|
'font-family': 0,
|
||
|
'font-size': 0,
|
||
|
'font-size-adjust': 0,
|
||
|
'font-stretch': 0,
|
||
|
'font-style': 0,
|
||
|
'font-variant': 0,
|
||
|
'font-weight': 0,
|
||
|
'glyph-orientation-horizontal': 0,
|
||
|
'glyph-orientation-vertical': 0,
|
||
|
'image-rendering': 0,
|
||
|
kerning: 0,
|
||
|
'letter-spacing': 0,
|
||
|
'lighting-color': 0,
|
||
|
marker: 0,
|
||
|
'marker-end': 0,
|
||
|
'marker-mid': 0,
|
||
|
'marker-start': 0,
|
||
|
mask: 0,
|
||
|
opacity: 0,
|
||
|
overflow: 0,
|
||
|
'pointer-events': 0,
|
||
|
'shape-rendering': 0,
|
||
|
'stop-color': 0,
|
||
|
'stop-opacity': 0,
|
||
|
stroke: 0,
|
||
|
'stroke-dasharray': 0,
|
||
|
'stroke-dashoffset': 0,
|
||
|
'stroke-linecap': 0,
|
||
|
'stroke-linejoin': 0,
|
||
|
'stroke-miterlimit': 0,
|
||
|
'stroke-opacity': 0,
|
||
|
'stroke-width': 0,
|
||
|
'text-anchor': 0,
|
||
|
'text-decoration': 0,
|
||
|
'text-rendering': 0,
|
||
|
'unicode-bidi': 0,
|
||
|
visibility: 0,
|
||
|
'word-spacing': 0,
|
||
|
'writing-mode': 0
|
||
|
}
|
||
|
|
||
|
eve.on('snap.util.attr', function (value) {
|
||
|
var att = eve.nt(),
|
||
|
attr = {}
|
||
|
att = att.substring(att.lastIndexOf('.') + 1)
|
||
|
attr[att] = value
|
||
|
var style = att.replace(/-(\w)/gi, function (all, letter) {
|
||
|
return letter.toUpperCase()
|
||
|
}),
|
||
|
css = att.replace(/[A-Z]/g, function (letter) {
|
||
|
return '-' + letter.toLowerCase()
|
||
|
})
|
||
|
if (cssAttr[has](css)) {
|
||
|
this.node.style[style] = value == null ? E : value
|
||
|
} else {
|
||
|
$(this.node, attr)
|
||
|
}
|
||
|
})
|
||
|
;(function (proto) {})(Paper.prototype)
|
||
|
|
||
|
// simple ajax
|
||
|
/*\
|
||
|
* Snap.ajax
|
||
|
[ method ]
|
||
|
**
|
||
|
* Simple implementation of Ajax
|
||
|
**
|
||
|
- url (string) URL
|
||
|
- postData (object|string) data for post request
|
||
|
- callback (function) callback
|
||
|
- scope (object) #optional scope of callback
|
||
|
* or
|
||
|
- url (string) URL
|
||
|
- callback (function) callback
|
||
|
- scope (object) #optional scope of callback
|
||
|
= (XMLHttpRequest) the XMLHttpRequest object, just in case
|
||
|
\*/
|
||
|
Snap.ajax = function (url, postData, callback, scope) {
|
||
|
var req = new XMLHttpRequest(),
|
||
|
id = ID()
|
||
|
if (req) {
|
||
|
if (is(postData, 'function')) {
|
||
|
scope = callback
|
||
|
callback = postData
|
||
|
postData = null
|
||
|
} else if (is(postData, 'object')) {
|
||
|
var pd = []
|
||
|
for (var key in postData)
|
||
|
if (postData.hasOwnProperty(key)) {
|
||
|
pd.push(
|
||
|
encodeURIComponent(key) + '=' + encodeURIComponent(postData[key])
|
||
|
)
|
||
|
}
|
||
|
postData = pd.join('&')
|
||
|
}
|
||
|
req.open(postData ? 'POST' : 'GET', url, true)
|
||
|
if (postData) {
|
||
|
req.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
|
||
|
req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
|
||
|
}
|
||
|
if (callback) {
|
||
|
eve.once('snap.ajax.' + id + '.0', callback)
|
||
|
eve.once('snap.ajax.' + id + '.200', callback)
|
||
|
eve.once('snap.ajax.' + id + '.304', callback)
|
||
|
}
|
||
|
req.onreadystatechange = function () {
|
||
|
if (req.readyState != 4) return
|
||
|
eve('snap.ajax.' + id + '.' + req.status, scope, req)
|
||
|
}
|
||
|
if (req.readyState == 4) {
|
||
|
return req
|
||
|
}
|
||
|
req.send(postData)
|
||
|
return req
|
||
|
}
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.load
|
||
|
[ method ]
|
||
|
**
|
||
|
* Loads external SVG file as a @Fragment (see @Snap.ajax for more advanced AJAX)
|
||
|
**
|
||
|
- url (string) URL
|
||
|
- callback (function) callback
|
||
|
- scope (object) #optional scope of callback
|
||
|
\*/
|
||
|
Snap.load = function (url, callback, scope) {
|
||
|
Snap.ajax(url, function (req) {
|
||
|
var f = Snap.parse(req.responseText)
|
||
|
scope ? callback.call(scope, f) : callback(f)
|
||
|
})
|
||
|
}
|
||
|
var getOffset = function (elem) {
|
||
|
var box = elem.getBoundingClientRect(),
|
||
|
doc = elem.ownerDocument,
|
||
|
body = doc.body,
|
||
|
docElem = doc.documentElement,
|
||
|
clientTop = docElem.clientTop || body.clientTop || 0,
|
||
|
clientLeft = docElem.clientLeft || body.clientLeft || 0,
|
||
|
top =
|
||
|
box.top +
|
||
|
(g.win.pageYOffset || docElem.scrollTop || body.scrollTop) -
|
||
|
clientTop,
|
||
|
left =
|
||
|
box.left +
|
||
|
(g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) -
|
||
|
clientLeft
|
||
|
return {
|
||
|
y: top,
|
||
|
x: left
|
||
|
}
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.getElementByPoint
|
||
|
[ method ]
|
||
|
**
|
||
|
* Returns you topmost element under given point.
|
||
|
**
|
||
|
= (object) Snap element object
|
||
|
- x (number) x coordinate from the top left corner of the window
|
||
|
- y (number) y coordinate from the top left corner of the window
|
||
|
> Usage
|
||
|
| Snap.getElementByPoint(mouseX, mouseY).attr({stroke: "#f00"});
|
||
|
\*/
|
||
|
Snap.getElementByPoint = function (x, y) {
|
||
|
var paper = this,
|
||
|
svg = paper.canvas,
|
||
|
target = glob.doc.elementFromPoint(x, y)
|
||
|
if (glob.win.opera && target.tagName == 'svg') {
|
||
|
var so = getOffset(target),
|
||
|
sr = target.createSVGRect()
|
||
|
sr.x = x - so.x
|
||
|
sr.y = y - so.y
|
||
|
sr.width = sr.height = 1
|
||
|
var hits = target.getIntersectionList(sr, null)
|
||
|
if (hits.length) {
|
||
|
target = hits[hits.length - 1]
|
||
|
}
|
||
|
}
|
||
|
if (!target) {
|
||
|
return null
|
||
|
}
|
||
|
return wrap(target)
|
||
|
}
|
||
|
/*\
|
||
|
* Snap.plugin
|
||
|
[ method ]
|
||
|
**
|
||
|
* Let you write plugins. You pass in a function with five arguments, like this:
|
||
|
| Snap.plugin(function (Snap, Element, Paper, global, Fragment) {
|
||
|
| Snap.newmethod = function () {};
|
||
|
| Element.prototype.newmethod = function () {};
|
||
|
| Paper.prototype.newmethod = function () {};
|
||
|
| });
|
||
|
* Inside the function you have access to all main objects (and their
|
||
|
* prototypes). This allow you to extend anything you want.
|
||
|
**
|
||
|
- f (function) your plugin body
|
||
|
\*/
|
||
|
Snap.plugin = function (f) {
|
||
|
f(Snap, Element, Paper, glob, Fragment)
|
||
|
}
|