2013-08-05 08:04:30 +00:00
// .--.--.
// / / '.
// | : /`. /
// ; | |--` .---. ,----._,.
// | : ;_ ,--.--. /. ./| ,--.--. / / ' / ,---.
// \ \ `. / \ .-' . ' | / \ | : | / \
// `----. \.--. .-. /___/ \: | .--. .-. || | .\ . / / |
// __ \ \ | \__\/: . . \ ' . \__\/: . .. ; '; |. ' / |
// / /`--' / ," .--.; |\ \ ' ," .--.; |' . . |' ; /|
// '--'. / / / ,. | \ \ / / ,. | `---`-'| |' | / |
// `--'---' ; : .' \ \ \ |; : .' \.'__/\_: || : |
// | , .-./ '---" | , .-./| : : \ \ /
// `--`---' `--`---' \ \ / `----'
// `--`-'
// Savage 0.0.1
//
// 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.
//
2013-09-17 06:33:21 +00:00
// build: 2013-09-17
2013-08-05 08:04:30 +00:00
// 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.
2013-08-23 23:31:00 +00:00
// ┌────────────────────────────────────────────────────────────┐ \\
// │ Eve 0.4.2 - JavaScript Events Library │ \\
// ├────────────────────────────────────────────────────────────┤ \\
// │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
// └────────────────────────────────────────────────────────────┘ \\
( function ( glob ) {
var version = "0.4.2" ,
has = "hasOwnProperty" ,
separator = /[\.\/]/ ,
wildcard = "*" ,
fun = function ( ) { } ,
numsort = function ( a , b ) {
return a - b ;
} ,
current _event ,
stop ,
events = { n : { } } ,
/ * \
* eve
[ method ]
* Fires event with given ` name ` , given scope and other parameters .
> Arguments
- 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
\ * /
eve = function ( name , scope ) {
name = String ( name ) ;
var e = events ,
oldstop = stop ,
args = Array . prototype . slice . call ( arguments , 2 ) ,
listeners = eve . listeners ( name ) ,
z = 0 ,
f = false ,
l ,
indexed = [ ] ,
queue = { } ,
out = [ ] ,
ce = current _event ,
errors = [ ] ;
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 . length ? out : null ;
} ;
// 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 ` .
> Arguments
- name ( string ) name of the event , dot ( ` . ` ) or slash ( ` / ` ) separated
= ( array ) array of event handlers
\ * /
eve . listeners = function ( name ) {
var names = 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 . 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 .
* *
> Arguments
* *
- name ( string ) name of the event , dot ( ` . ` ) or slash ( ` / ` ) separated , with optional wildcards
- 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 don ’ t need to worry about z - index , but it ’ s nice to have this feature “ just in case ” .
\ * /
eve . on = function ( name , f ) {
name = String ( name ) ;
if ( typeof f != "function" ) {
return function ( ) { } ;
}
var names = name . split ( separator ) ,
e = events ;
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 ) {
return fun ;
}
e . f . push ( f ) ;
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]
| } ) ;
> Arguments
- 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 .
* *
> Arguments
* *
- subname ( string ) # optional subname of the event
* *
= ( string ) name of the event , if ` subname ` is not specified
* or
= ( boolean ) ` true ` , if current event ’ s name contains ` subname `
\ * /
eve . nt = function ( subname ) {
if ( subname ) {
return new RegExp ( "(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)" ) . test ( current _event ) ;
}
return current _event ;
} ;
/ * \
* 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 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 .
* *
> Arguments
* *
- 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 = name . split ( separator ) ,
e ,
key ,
splice ,
i , ii , j , jj ,
cur = [ events ] ;
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 ] ] ) ;
}
} else {
for ( key in e ) if ( e [ has ] ( key ) ) {
splice . push ( e [ 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 ;
}
}
} ;
/ * \
* 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 .
* *
> Arguments
* *
- 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 . unbind ( 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 ;
} ;
( typeof module != "undefined" && module . exports ) ? ( module . exports = eve ) : ( typeof define != "undefined" ? ( define ( "eve" , [ ] , function ( ) { return eve ; } ) ) : ( glob . eve = eve ) ) ;
} ) ( this ) ;
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var mina = ( function ( eve ) {
2013-08-20 09:42:07 +00:00
var animations = { } ,
2013-08-05 08:04:30 +00:00
requestAnimFrame = window . requestAnimationFrame ||
window . webkitRequestAnimationFrame ||
window . mozRequestAnimationFrame ||
window . oRequestAnimationFrame ||
window . msRequestAnimationFrame ||
function ( callback ) {
setTimeout ( callback , 16 ) ;
} ,
isArray = Array . isArray || function ( a ) {
return a instanceof Array ||
Object . prototype . toString . call ( a ) == "[object Array]" ;
} ,
2013-08-16 02:56:19 +00:00
idgen = 0 ,
idprefix = "M" + ( + new Date ) . toString ( 36 ) ,
ID = function ( ) {
return idprefix + ( idgen ++ ) . toString ( 36 ) ;
} ,
2013-08-05 08:04:30 +00:00
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 = function ( ) {
return + new Date ;
} ,
2013-08-15 08:29:47 +00:00
sta = function ( val ) {
var a = this ;
if ( val == null ) {
return a . s ;
2013-08-05 08:04:30 +00:00
}
2013-08-15 08:29:47 +00:00
var ds = a . s - val ;
a . b += a . dur * ds ;
a . B += a . dur * ds ;
a . s = val ;
2013-08-05 08:04:30 +00:00
} ,
2013-08-15 08:29:47 +00:00
speed = function ( val ) {
var a = this ;
if ( val == null ) {
return a . spd ;
2013-08-05 08:04:30 +00:00
}
2013-08-15 08:29:47 +00:00
a . spd = val ;
2013-08-05 08:04:30 +00:00
} ,
2013-08-15 08:29:47 +00:00
duration = function ( val ) {
var a = this ;
if ( val == null ) {
return a . dur ;
}
a . s = a . s * val / a . dur ;
a . dur = val ;
} ,
2013-08-20 09:42:07 +00:00
stopit = function ( ) {
var a = this ;
delete animations [ a . id ] ;
eve ( "mina.stop." + a . id , a ) ;
} ,
2013-08-15 08:29:47 +00:00
frame = function ( ) {
2013-08-20 09:42:07 +00:00
var len = 0 ;
for ( var i in animations ) if ( animations . hasOwnProperty ( i ) ) {
2013-08-15 08:29:47 +00:00
var a = animations [ i ] ,
b = a . get ( ) ,
res ;
2013-08-20 09:42:07 +00:00
len ++ ;
2013-08-15 08:29:47 +00:00
a . s = ( b - a . b ) / ( a . dur / a . spd ) ;
if ( a . s >= 1 ) {
2013-08-20 09:42:07 +00:00
delete animations [ i ] ;
2013-08-15 08:29:47 +00:00
a . s = 1 ;
2013-08-20 09:42:07 +00:00
len -- ;
2013-08-05 08:04:30 +00:00
}
2013-08-15 08:29:47 +00:00
if ( isArray ( a . start ) ) {
res = [ ] ;
for ( var j = 0 , jj = a . start . length ; j < jj ; j ++ ) {
2013-08-16 02:56:19 +00:00
res [ j ] = a . start [ j ] +
( a . end [ j ] - a . start [ j ] ) * a . easing ( a . s ) ;
2013-08-15 08:29:47 +00:00
}
} else {
res = a . start + ( a . end - a . start ) * a . easing ( a . s ) ;
}
a . set ( res ) ;
2013-08-20 09:42:07 +00:00
if ( a . s == 1 ) {
2013-08-16 02:56:19 +00:00
eve ( "mina.finish." + a . id , a ) ;
}
2013-08-05 08:04:30 +00:00
}
2013-08-20 09:42:07 +00:00
len && requestAnimFrame ( frame ) ;
2013-08-05 08:04:30 +00:00
} ,
2013-08-23 23:31:00 +00:00
/ * \
* mina
[ method ]
* *
* Generic animation of numbers .
* *
- a ( number ) start “ slave ” number
- A ( number ) end “ slave ” number
- b ( number ) start “ master ” number ( start time in gereal case )
- B ( number ) end “ master ” number ( end time in gereal 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 }
\ * /
2013-08-15 08:29:47 +00:00
mina = function ( a , A , b , B , get , set , easing ) {
2013-08-05 08:04:30 +00:00
var anim = {
2013-08-16 02:56:19 +00:00
id : ID ( ) ,
2013-08-15 08:29:47 +00:00
start : a ,
end : A ,
2013-08-05 08:04:30 +00:00
b : b ,
2013-08-15 08:29:47 +00:00
s : 0 ,
dur : B - b ,
spd : 1 ,
get : get ,
set : set ,
easing : easing || mina . linear ,
status : sta ,
speed : speed ,
2013-08-20 09:42:07 +00:00
duration : duration ,
stop : stopit
2013-08-05 08:04:30 +00:00
} ;
2013-08-20 09:42:07 +00:00
animations [ anim . id ] = anim ;
var len = 0 , i ;
for ( i in animations ) if ( animations . hasOwnProperty ( i ) ) {
len ++ ;
if ( len == 2 ) {
break ;
}
}
len == 1 && requestAnimFrame ( frame ) ;
2013-08-05 08:04:30 +00:00
return anim ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . time
[ method ]
* *
* Returns current time . Equal to
| function ( ) {
| return ( new Date ) . getTime ( ) ;
| }
\ * /
2013-08-15 08:29:47 +00:00
mina . time = timer ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . getById
[ method ]
* *
* Returns animation by it ’ s id .
- id ( string ) animation ’ s id
= ( object ) See @ mina
\ * /
2013-08-20 09:42:07 +00:00
mina . getById = function ( id ) {
return animations [ anim . id ] || null ;
} ;
2013-08-15 08:29:47 +00:00
2013-08-23 23:31:00 +00:00
/ * \
* mina . linear
[ method ]
* *
* Default linear easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
mina . linear = function ( n ) {
return n ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . easeout
[ method ]
* *
* Easeout easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
mina . easeout = function ( n ) {
return Math . pow ( n , 1.7 ) ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . easein
[ method ]
* *
* Easein easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-15 08:29:47 +00:00
mina . easein = function ( n ) {
2013-08-05 08:04:30 +00:00
return Math . pow ( n , . 48 ) ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . easeinout
[ method ]
* *
* Easeinout easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
mina . easeinout = function ( n ) {
2013-09-13 12:51:01 +00:00
if ( n == 1 ) {
return 1 ;
}
if ( n == 0 ) {
return 0 ;
}
2013-08-05 08:04:30 +00:00
var q = . 48 - n / 1.04 ,
Q = Math . sqrt ( . 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 + . 5 ;
return ( 1 - t ) * 3 * t * t + t * t * t ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . backin
[ method ]
* *
* Backin easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
mina . backin = function ( n ) {
2013-09-13 12:51:01 +00:00
if ( n == 1 ) {
return 1 ;
}
2013-08-05 08:04:30 +00:00
var s = 1.70158 ;
return n * n * ( ( s + 1 ) * n - s ) ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . backout
[ method ]
* *
* Backout easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
mina . backout = function ( n ) {
2013-09-13 12:51:01 +00:00
if ( n == 0 ) {
return 0 ;
}
2013-08-05 08:04:30 +00:00
n = n - 1 ;
var s = 1.70158 ;
return n * n * ( ( s + 1 ) * n + s ) + 1 ;
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . elastic
[ method ]
* *
* Elastic easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
mina . elastic = function ( n ) {
if ( n == ! ! n ) {
return n ;
}
2013-08-15 08:29:47 +00:00
return Math . pow ( 2 , - 10 * n ) * Math . sin ( ( n - . 075 ) *
( 2 * Math . PI ) / . 3 ) + 1 ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-23 23:31:00 +00:00
/ * \
* mina . bounce
[ method ]
* *
* Bounce easing .
- n ( number ) input 0. . 1
= ( number ) output 0. . 1
\ * /
2013-08-05 08:04:30 +00:00
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 + . 75 ;
} else {
if ( n < ( 2.5 / p ) ) {
n -= ( 2.25 / p ) ;
l = s * n * n + . 9375 ;
} else {
n -= ( 2.625 / p ) ;
l = s * n * n + . 984375 ;
}
}
}
return l ;
} ;
2013-08-23 23:31:00 +00:00
2013-08-05 08:04:30 +00:00
return mina ;
2013-08-20 09:42:07 +00:00
} ) ( typeof eve == "undefined" ? function ( ) { } : eve ) ;
2013-08-05 08:04:30 +00:00
/ *
2013-09-11 03:17:32 +00:00
* Elemental 0.2 . 4 - Simple JavaScript Tag Parser
2013-08-05 08:04:30 +00:00
*
2013-09-09 00:33:59 +00:00
* Copyright ( c ) 2010 - 2013 Dmitry Baranovskiy ( http : //dmitry.baranovskiy.com/)
2013-08-05 08:04:30 +00:00
* Licensed under the MIT ( http : //www.opensource.org/licenses/mit-license.php) license.
* /
( function ( ) {
function parse ( s ) {
s = s || Object ( s ) ;
var pos = 1 ,
len = s . length + 1 ,
p , c , n = at ( s , 0 ) ;
for ( ; pos < len ; pos ++ ) {
p = c ;
c = n ;
n = at ( s , pos ) ;
this . raw += c ;
step . call ( this , c , n , p ) ;
}
this . _beforeEnd = function ( ) {
step . call ( this , "" , "" , c ) ;
} ;
return this ;
}
function at ( s , i ) {
return s && ( s . charAt ? s . charAt ( i ) : s [ i ] ) ;
}
function on ( name , f ) {
this . events = this . events || { } ;
this . events [ name ] = this . events [ name ] || [ ] ;
this . events [ name ] . push ( f ) ;
}
function event ( name , data , extra ) {
if ( typeof eve == "function" ) {
2013-09-11 03:17:32 +00:00
eve ( "elemental." + name + ( data ? "." + data : "" ) , null , data , extra || "" , this . raw ) ;
2013-08-05 08:04:30 +00:00
}
var a = this . events && this . events [ name ] ,
i = a && a . length ;
while ( i -- ) try {
this . events [ name ] [ i ] ( data , extra || "" , this . raw ) ;
} catch ( e ) { }
this . raw = "" ;
}
function end ( ) {
step . call ( this , "eof" ) ;
this . event ( "eof" ) ;
}
2013-09-11 03:17:32 +00:00
var entities = {
"lt" : 60 ,
"lt;" : 60 ,
"AMP;" : 38 ,
"AMP" : 38 ,
"GT;" : 62 ,
"GT" : 62 ,
"QUOT;" : 34 ,
"QUOT" : 34 ,
"apos;" : 39 ,
"bull;" : 8226 ,
"bullet;" : 8226 ,
"copy;" : 169 ,
"copy" : 169 ,
"deg;" : 176 ,
"deg" : 176
} ,
whitespace = /[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]/ ,
notEntity = /[#\da-z]/i ,
entity2text = function ( entity ) {
var code ;
if ( entity . charAt ( ) == "#" ) {
if ( entity . charAt ( 1 ) . toLowerCase ( ) == "x" ) {
code = parseInt ( entity . substring ( 2 ) , 16 ) ;
} else {
code = parseInt ( entity . substring ( 1 ) , 10 ) ;
}
}
code = entities [ entity ] ;
return code ? String . fromCharCode ( code ) : ( "&" + entity ) ;
} ,
2013-08-05 08:04:30 +00:00
fireAttrEvent = function ( ) {
for ( var key in this . attr ) if ( this . attr . hasOwnProperty ( key ) ) {
this . event ( "attr" , key , {
value : this . attr [ key ] ,
tagname : this . tagname ,
attr : this . attr
} ) ;
}
} ,
act = {
text : function ( c , n , p ) {
switch ( c ) {
case "<" :
case "eof" :
this . nodename = "" ;
this . attr = { } ;
this . mode = "tag name start" ;
this . raw = this . raw . slice ( 0 , - 1 ) ;
this . textchunk && this . event ( "text" , this . textchunk ) ;
this . raw += c ;
this . textchunk = "" ;
break ;
2013-09-11 03:17:32 +00:00
case "&" :
this . mode = "entity" ;
this . entity = "" ;
break ;
2013-08-05 08:04:30 +00:00
default :
this . textchunk += c ;
break ;
}
} ,
2013-09-11 03:17:32 +00:00
entity : function ( c , n , p ) {
if ( whitespace . test ( c ) ) {
this . textchunk += entity2text ( this . entity ) ;
this . mode = "text" ;
} else if ( c == ";" ) {
this . textchunk += entity2text ( this . entity + c ) ;
this . mode = "text" ;
} else {
this . entity += c ;
}
} ,
2013-08-05 08:04:30 +00:00
special : function ( c , n , p ) {
if ( p == "!" && c == "-" && n == "-" ) {
this . mode = "comment start" ;
return ;
}
if ( this . textchunk == "[CDATA" && c == "[" ) {
this . mode = "cdata" ;
this . textchunk = "" ;
return ;
}
if ( c == ">" || c == "eof" ) {
this . event ( "special" , this . textchunk ) ;
this . mode = "text" ;
this . textchunk = "" ;
return ;
}
this . textchunk += c ;
} ,
cdata : function ( c , n , p ) {
if ( p == "]" && c == "]" && n == ">" ) {
this . mode = "cdata end" ;
this . textchunk = this . textchunk . slice ( 0 , - 1 ) ;
return ;
}
if ( c == "eof" ) {
act [ "cdata end" ] . call ( this ) ;
}
this . textchunk += c ;
} ,
"cdata end" : function ( c , n , p ) {
this . event ( "cdata" , this . textchunk ) ;
this . textchunk = "" ;
this . mode = "text" ;
} ,
"comment start" : function ( c , n , p ) {
if ( n == ">" || c == "eof" ) {
this . event ( "comment" , "" ) ;
2013-09-11 03:17:32 +00:00
this . mode = "skip" ;
2013-08-05 08:04:30 +00:00
} else {
this . mode = "comment" ;
}
} ,
2013-09-11 03:17:32 +00:00
"skip" : function ( c , n , p ) {
2013-08-05 08:04:30 +00:00
this . mode = "text" ;
} ,
comment : function ( c , n , p ) {
if ( c == "-" && p == "-" && n == ">" ) {
this . mode = "comment end" ;
this . textchunk = this . textchunk . slice ( 0 , - 1 ) ;
} else if ( c == "eof" ) {
this . event ( "comment" , this . textchunk ) ;
} else {
this . textchunk += c ;
}
} ,
"comment end" : function ( c , n , p ) {
this . event ( "comment" , this . textchunk ) ;
this . textchunk = "" ;
this . mode = "text" ;
} ,
declaration : function ( c , n , p ) {
if ( c == "?" && n == ">" ) {
this . mode = "declaration end" ;
return ;
}
if ( c == "eof" ) {
this . event ( "comment" , this . textchunk ) ;
}
this . textchunk += c ;
} ,
"declaration end" : function ( c , n , p ) {
this . event ( "comment" , this . textchunk ) ;
this . textchunk = "" ;
this . mode = "text" ;
} ,
"tag name start" : function ( c , n , p ) {
if ( c == "eof" ) {
this . event ( "text" , "<" ) ;
return ;
}
if ( ! whitespace . test ( c ) ) {
this . mode = "tag name" ;
if ( c == "/" ) {
this . mode = "close tag name start" ;
return ;
} else if ( c == "!" ) {
this . mode = "special" ;
this . textchunk = "" ;
return ;
} else if ( c == "?" ) {
this . mode = "declaration" ;
return ;
}
act [ this . mode ] . call ( this , c , n , p ) ;
}
} ,
"close tag name start" : function ( c , n , p ) {
if ( ! whitespace . test ( c ) ) {
this . mode = "close tag name" ;
this . tagname = "" ;
this . nodename = "" ;
act [ this . mode ] . call ( this , c , n , p ) ;
}
} ,
"close tag name" : function ( c , n , p ) {
if ( whitespace . test ( c ) ) {
this . tagname = this . nodename ;
} else switch ( c ) {
case ">" :
this . event ( "/tag" , ( this . tagname || this . nodename ) ) ;
this . mode = "text" ;
break ;
default :
! this . tagname && ( this . nodename += c ) ;
break ;
}
} ,
"tag name" : function ( c , n , p ) {
if ( whitespace . test ( c ) ) {
this . tagname = this . nodename ;
this . nodename = "" ;
this . mode = "attr start" ;
} else switch ( c ) {
case ">" :
this . event ( "tag" , this . nodename ) ;
this . mode = "text" ;
break ;
2013-09-11 03:17:32 +00:00
case "/" :
this . raw += n ;
this . event ( "tag" , this . nodename ) ;
this . event ( "/tag" , this . nodename ) ;
this . mode = "skip" ;
break ;
2013-08-05 08:04:30 +00:00
default :
this . nodename += c ;
break ;
}
} ,
"attr start" : function ( c , n , p ) {
if ( ! whitespace . test ( c ) ) {
this . mode = "attr" ;
this . nodename = "" ;
act [ this . mode ] . call ( this , c , n , p ) ;
}
} ,
attr : function ( c , n , p ) {
if ( whitespace . test ( c ) || c == "=" ) {
this . attr [ this . nodename ] = "" ;
this . mode = "attr value start" ;
} else switch ( c ) {
case ">" :
if ( this . nodename == "/" ) {
delete this . attr [ "/" ] ;
this . event ( "tag" , this . tagname , this . attr ) ;
fireAttrEvent . call ( this ) ;
this . event ( "/tag" , this . tagname , true ) ;
} else {
this . nodename && ( this . attr [ this . nodename ] = "" ) ;
this . event ( "tag" , this . tagname , this . attr ) ;
fireAttrEvent . call ( this ) ;
}
this . mode = "text" ;
break ;
default :
this . nodename += c ;
break ;
}
} ,
"attr value start" : function ( c , n , p ) {
if ( ! whitespace . test ( c ) ) {
this . mode = "attr value" ;
this . quote = false ;
if ( c == "'" || c == '"' ) {
this . quote = c ;
return ;
}
act [ this . mode ] . call ( this , c , n , p ) ;
}
} ,
"attr value" : function ( c , n , p ) {
if ( whitespace . test ( c ) && ! this . quote ) {
this . mode = "attr start" ;
} else if ( c == ">" && ! this . quote ) {
this . event ( "tag" , this . tagname , this . attr ) ;
this . mode = "text" ;
} else switch ( c ) {
case '"' :
case "'" :
if ( this . quote == c && p != "\\" ) {
this . mode = "attr start" ;
}
break ;
default :
this . attr [ this . nodename ] += c ;
break ;
}
}
} ;
function step ( c , n , p ) {
c == "\n" && this . event ( "newline" ) ;
act [ this . mode ] . call ( this , c , n , p ) ;
}
2013-09-11 03:17:32 +00:00
function elemental ( type , ent ) {
2013-08-05 08:04:30 +00:00
var out = function ( s ) {
out . parse ( s ) ;
} ;
out . mode = "text" ;
out . type = String ( type || "html" ) . toLowerCase ( ) ;
out . textchunk = "" ;
out . raw = "" ;
out . parse = parse ;
out . on = on ;
out . event = event ;
out . end = end ;
2013-09-11 03:17:32 +00:00
if ( ent ) {
entities = ent ;
}
2013-08-05 08:04:30 +00:00
return out ;
}
2013-09-11 03:17:32 +00:00
elemental . version = "0.2.4" ;
2013-08-05 08:04:30 +00:00
( typeof exports == "undefined" ? this : exports ) . elemental = elemental ;
} ) ( ) ;
2013-08-20 09:42:07 +00:00
// 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.
2013-08-22 10:00:40 +00:00
var Savage = ( function ( ) {
2013-08-05 08:04:30 +00:00
Savage . version = "0.0.1" ;
2013-08-22 10:00:40 +00:00
/ * \
* Savage
[ method ]
* *
* Creates 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 Savage structure
* or
- query ( string ) CSS query selector
= ( object ) @ Element
\ * /
2013-08-05 08:04:30 +00:00
function Savage ( w , h ) {
if ( w ) {
if ( w . tagName ) {
2013-08-09 12:13:22 +00:00
return wrap ( w ) ;
2013-08-05 08:04:30 +00:00
}
if ( w instanceof Element ) {
return w ;
}
if ( h == null ) {
2013-08-09 12:13:22 +00:00
w = glob . doc . querySelector ( w ) ;
return wrap ( w ) ;
2013-08-05 08:04:30 +00:00
}
}
w = w == null ? "100%" : w ;
h = h == null ? "100%" : h ;
return new Paper ( w , h ) ;
}
Savage . toString = function ( ) {
return "Savage v" + this . version ;
} ;
Savage . _ = { } ;
var glob = {
win : window ,
doc : window . document
} ;
2013-09-12 02:56:46 +00:00
Savage . _ . glob = glob ;
2013-08-05 08:04:30 +00:00
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 ,
2013-09-16 06:55:33 +00:00
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 ,
2013-08-05 08:04:30 +00:00
isnan = { "NaN" : 1 , "Infinity" : 1 , "-Infinity" : 1 } ,
bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/ ,
reURLValue = /^url\(#?([^)]+)\)$/ ,
spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029" ,
separator = new RegExp ( "[," + spaces + "]+" ) ,
whitespace = new RegExp ( "[" + spaces + "]" , "g" ) ,
commaSpaces = new RegExp ( "[" + spaces + "]*,[" + spaces + "]*" ) ,
hsrg = { hs : 1 , rg : 1 } ,
pathCommand = new RegExp ( "([a-z])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)" , "ig" ) ,
tCommand = new RegExp ( "([rstm])[" + spaces + ",]*((-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?[" + spaces + "]*,?[" + spaces + "]*)+)" , "ig" ) ,
pathValues = new RegExp ( "(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[" + spaces + "]*,?[" + spaces + "]*" , "ig" ) ,
idgen = 0 ,
idprefix = "S" + ( + new Date ) . toString ( 36 ) ,
ID = function ( ) {
return idprefix + ( idgen ++ ) . toString ( 36 ) ;
} ,
xlink = "http://www.w3.org/1999/xlink" ,
hub = { } ;
function $ ( el , attr ) {
if ( attr ) {
if ( typeof el == "string" ) {
el = $ ( el ) ;
}
if ( typeof attr == "string" ) {
2013-08-15 08:29:47 +00:00
if ( attr . substring ( 0 , 6 ) == "xlink:" ) {
return el . getAttributeNS ( xlink , attr . substring ( 6 ) ) ;
}
2013-08-05 08:04:30 +00:00
return el . getAttribute ( attr ) ;
}
for ( var key in attr ) if ( attr [ has ] ( key ) ) {
var val = Str ( attr [ key ] ) ;
if ( val ) {
if ( key . substring ( 0 , 6 ) == "xlink:" ) {
el . setAttributeNS ( xlink , key . substring ( 6 ) , val ) ;
} else {
el . setAttribute ( key , val ) ;
}
} else {
el . removeAttribute ( key ) ;
}
}
} else {
el = glob . doc . createElementNS ( "http://www.w3.org/2000/svg" , el ) ;
// el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
}
return el ;
}
Savage . _ . $ = $ ;
2013-08-15 08:29:47 +00:00
Savage . _ . id = ID ;
2013-08-05 08:04:30 +00:00
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 ! isnan [ has ] ( + 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 ;
}
2013-08-15 08:29:47 +00:00
/ * \
* Savage . format
[ method ]
* *
* Replaces construction of type “ ` {<name>} ` ” to the corresponding argument .
* *
- token ( string ) string to format
- json ( object ) object which properties will be used as a replacement
= ( string ) formated string
> Usage
| // this will draw a rectangular shape equivalent to "M10,20h40v50h-40z"
| paper . path ( Savage . 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
| }
| } ) ) ;
\ * /
Savage . 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 ) ;
} ) ;
} ;
} ) ( ) ;
2013-08-05 08:04:30 +00:00
var preload = ( function ( ) {
function onerror ( ) {
this . parentNode . removeChild ( this ) ;
}
return function ( src , f ) {
var img = glob . doc . createElement ( "img" ) ,
body = glob . doc . body ;
img . style . cssText = "position:absolute;left:-9999em;top:-9999em" ;
img . onload = function ( ) {
f . call ( img ) ;
img . onload = img . onerror = null ;
body . removeChild ( img ) ;
} ;
img . onerror = onerror ;
body . appendChild ( img ) ;
img . src = src ;
} ;
} ( ) ) ;
function clone ( obj ) {
if ( typeof obj == "function" || Object ( obj ) !== obj ) {
return obj ;
}
var res = new obj . constructor ;
for ( var key in obj ) if ( obj [ has ] ( key ) ) {
res [ key ] = clone ( obj [ key ] ) ;
}
return res ;
}
Savage . _ . 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 ;
}
Savage . _ . cacher = cacher ;
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 ;
}
/ * \
2013-08-20 09:42:07 +00:00
* Savage . rad
2013-08-05 08:04:30 +00:00
[ method ]
* *
* Transform angle to radians
- deg ( number ) angle in degrees
= ( number ) angle in radians .
\ * /
Savage . rad = rad ;
/ * \
2013-08-20 09:42:07 +00:00
* Savage . deg
2013-08-05 08:04:30 +00:00
[ method ]
* *
* Transform angle to degrees
- deg ( number ) angle in radians
= ( number ) angle in degrees .
\ * /
Savage . deg = deg ;
/ * \
* Savage . is
[ method ]
* *
* Handfull replacement for ` typeof ` operator .
- o ( … ) any object or primitive
- type ( string ) name of the type , i . e . “ string ” , “ function ” , “ number ” , etc .
= ( boolean ) is given value is of given type
\ * /
Savage . is = is ;
/ * \
* Savage . 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 tolerance for snapping . Default is ` 10 ` .
= ( number ) adjusted value .
\ * /
Savage . 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 ;
} ;
// MATRIX
function Matrix ( a , b , c , d , e , f ) {
if ( b == null && objectToString . call ( a ) == "[object SVGMatrix]" ) {
this . a = a . a ;
this . b = a . b ;
this . c = a . c ;
this . d = a . d ;
this . e = a . e ;
this . f = a . f ;
return ;
}
if ( a != null ) {
this . a = + a ;
this . b = + b ;
this . c = + c ;
this . d = + d ;
this . e = + e ;
this . f = + f ;
} else {
this . a = 1 ;
this . b = 0 ;
this . c = 0 ;
this . d = 1 ;
this . e = 0 ;
this . f = 0 ;
}
}
( function ( matrixproto ) {
/ * \
* Matrix . add
[ method ]
* *
* Adds given matrix to existing one .
- a ( number )
- b ( number )
- c ( number )
- d ( number )
- e ( number )
- f ( number )
or
- matrix ( object ) @ Matrix
\ * /
matrixproto . add = function ( a , b , c , d , e , f ) {
var out = [ [ ] , [ ] , [ ] ] ,
m = [ [ this . a , this . c , this . e ] , [ this . b , this . d , this . f ] , [ 0 , 0 , 1 ] ] ,
matrix = [ [ a , c , e ] , [ b , d , f ] , [ 0 , 0 , 1 ] ] ,
x , y , z , res ;
if ( a && a instanceof Matrix ) {
matrix = [ [ a . a , a . c , a . e ] , [ a . b , a . d , a . f ] , [ 0 , 0 , 1 ] ] ;
}
for ( x = 0 ; x < 3 ; x ++ ) {
for ( y = 0 ; y < 3 ; y ++ ) {
res = 0 ;
for ( z = 0 ; z < 3 ; z ++ ) {
res += m [ x ] [ z ] * matrix [ z ] [ y ] ;
}
out [ x ] [ y ] = res ;
}
}
this . a = out [ 0 ] [ 0 ] ;
this . b = out [ 1 ] [ 0 ] ;
this . c = out [ 0 ] [ 1 ] ;
this . d = out [ 1 ] [ 1 ] ;
this . e = out [ 0 ] [ 2 ] ;
this . f = out [ 1 ] [ 2 ] ;
return this ;
} ;
/ * \
* Matrix . invert
[ method ]
* *
* Returns 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 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 )
- y ( number )
\ * /
matrixproto . translate = function ( x , y ) {
return this . add ( 1 , 0 , 0 , 1 , x , y ) ;
} ;
/ * \
* Matrix . scale
[ method ]
* *
* Scales the matrix
- x ( number )
- y ( number ) # optional
- cx ( number ) # optional
- cy ( number ) # optional
\ * /
matrixproto . scale = function ( x , y , cx , cy ) {
y == null && ( y = x ) ;
( cx || cy ) && this . add ( 1 , 0 , 0 , 1 , cx , cy ) ;
this . add ( x , 0 , 0 , y , 0 , 0 ) ;
( cx || cy ) && this . add ( 1 , 0 , 0 , 1 , - cx , - cy ) ;
return this ;
} ;
/ * \
* Matrix . rotate
[ method ]
* *
* Rotates the matrix
- a ( number )
- x ( number )
- y ( number )
\ * /
matrixproto . rotate = function ( a , x , y ) {
a = rad ( a ) ;
x = x || 0 ;
y = y || 0 ;
var cos = + math . cos ( a ) . toFixed ( 9 ) ,
sin = + math . sin ( a ) . toFixed ( 9 ) ;
this . add ( cos , sin , - sin , cos , x , y ) ;
return this . add ( 1 , 0 , 0 , 1 , - x , - y ) ;
} ;
/ * \
* Matrix . x
[ method ]
* *
* Return 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 ]
* *
* Return y coordinate for given point after transformation described by the matrix . See also @ Matrix . x
- x ( number )
- y ( number )
= ( number ) y
\ * /
matrixproto . y = function ( x , y ) {
return x * this . b + y * this . d + this . f ;
} ;
matrixproto . get = function ( i ) {
return + this [ Str . fromCharCode ( 97 + i ) ] . toFixed ( 4 ) ;
} ;
matrixproto . toString = function ( ) {
return "matrix(" + [ this . get ( 0 ) , this . get ( 1 ) , this . get ( 2 ) , this . get ( 3 ) , this . get ( 4 ) , this . get ( 5 ) ] . join ( ) + ")" ;
} ;
matrixproto . offset = function ( ) {
return [ this . e . toFixed ( 4 ) , this . f . toFixed ( 4 ) ] ;
} ;
function norm ( a ) {
return a [ 0 ] * a [ 0 ] + a [ 1 ] * a [ 1 ] ;
}
function normalize ( a ) {
var mag = math . sqrt ( norm ( a ) ) ;
a [ 0 ] && ( a [ 0 ] /= mag ) ;
a [ 1 ] && ( a [ 1 ] /= mag ) ;
}
/ * \
* Matrix . split
[ method ]
* *
* Splits matrix into primitive transformations
= ( object ) in format :
o dx ( number ) translation by x
o dy ( number ) translation by y
o scalex ( number ) scale by x
o scaley ( number ) scale by y
o shear ( number ) shear
o rotate ( number ) rotation in deg
o isSimple ( boolean ) could it be represented via simple transformations
\ * /
matrixproto . split = function ( ) {
var out = { } ;
// translation
out . dx = this . e ;
out . dy = this . f ;
// scale and shear
var row = [ [ this . a , this . c ] , [ this . b , this . d ] ] ;
out . scalex = math . sqrt ( norm ( row [ 0 ] ) ) ;
normalize ( row [ 0 ] ) ;
out . shear = row [ 0 ] [ 0 ] * row [ 1 ] [ 0 ] + row [ 0 ] [ 1 ] * row [ 1 ] [ 1 ] ;
row [ 1 ] = [ row [ 1 ] [ 0 ] - row [ 0 ] [ 0 ] * out . shear , row [ 1 ] [ 1 ] - row [ 0 ] [ 1 ] * out . shear ] ;
out . scaley = math . sqrt ( norm ( row [ 1 ] ) ) ;
normalize ( row [ 1 ] ) ;
out . shear /= out . scaley ;
// rotation
var sin = - row [ 0 ] [ 1 ] ,
cos = row [ 1 ] [ 1 ] ;
if ( cos < 0 ) {
out . rotate = deg ( math . acos ( cos ) ) ;
if ( sin < 0 ) {
out . rotate = 360 - out . rotate ;
}
} else {
out . rotate = deg ( math . asin ( sin ) ) ;
}
out . isSimple = ! + out . shear . toFixed ( 9 ) && ( out . scalex . toFixed ( 9 ) == out . scaley . toFixed ( 9 ) || ! out . rotate ) ;
out . isSuperSimple = ! + out . shear . toFixed ( 9 ) && out . scalex . toFixed ( 9 ) == out . scaley . toFixed ( 9 ) && ! out . rotate ;
out . noRotation = ! + out . shear . toFixed ( 9 ) && ! out . rotate ;
return out ;
} ;
/ * \
* Matrix . toTransformString
[ method ]
* *
* Return transform string that represents given matrix
= ( string ) transform string
\ * /
matrixproto . toTransformString = function ( shorter ) {
var s = shorter || this . split ( ) ;
if ( s . isSimple ) {
s . scalex = + s . scalex . toFixed ( 4 ) ;
s . scaley = + s . scaley . toFixed ( 4 ) ;
s . rotate = + s . rotate . toFixed ( 4 ) ;
return ( s . dx || s . dy ? "t" + [ + s . dx . toFixed ( 4 ) , + s . dy . toFixed ( 4 ) ] : E ) +
( s . scalex != 1 || s . scaley != 1 ? "s" + [ s . scalex , s . scaley , 0 , 0 ] : E ) +
( s . rotate ? "r" + [ + s . rotate . toFixed ( 4 ) , 0 , 0 ] : E ) ;
} else {
return "m" + [ this . get ( 0 ) , this . get ( 1 ) , this . get ( 2 ) , this . get ( 3 ) , this . get ( 4 ) , this . get ( 5 ) ] ;
}
} ;
} ) ( Matrix . prototype ) ;
/ * \
2013-08-20 09:42:07 +00:00
* Savage . Matrix
2013-08-05 08:04:30 +00:00
[ method ]
* *
* Utility method
* *
* Returns matrix based on given parameters .
- a ( number )
- b ( number )
- c ( number )
- d ( number )
- e ( number )
- f ( number )
* or
- svgMatrix ( SVGMatrix )
= ( object ) @ Matrix
\ * /
Savage . Matrix = Matrix ;
// Colour
/ * \
* Savage . getRGB
[ method ]
* *
* Parses colour string as RGB object
- colour ( string ) colour string in one of formats :
# < ul >
# < li > Colour name ( “ < code > red < / c o d e > ” , “ < c o d e > g r e e n < / c o d e > ” , “ < c o d e > c o r n f l o w e r b l u e < / c o d e > ” , e t c ) < / l i >
# < li > # • • • — shortened HTML colour : ( “ < code > # 000 < / c o d e > ” , “ < c o d e > # f c 0 < / c o d e > ” , e t c ) < / l i >
# < li > # • • • • • • — full length HTML colour : ( “ < code > # 000000 < / c o d e > ” , “ < c o d e > # b d 2 3 0 0 < / c o d e > ” ) < / l i >
# < li > rgb ( • • • , • • • , • • • ) — red , green and blue channels values : ( “ < code > rgb ( 200 , & nbsp ; 100 , & nbsp ; 0 ) < / c o d e > ” ) < / l i >
2013-09-17 06:33:21 +00:00
# < li > rgba ( • • • , • • • , • • • , • • • ) — also with opacity < / l i >
2013-08-05 08:04:30 +00:00
# < li > rgb ( • • • % , • • • % , • • • % ) — same as above , but in % : ( “ < code > rgb ( 100 % , & nbsp ; 175 % , & nbsp ; 0 % ) < / c o d e > ” ) < / l i >
2013-09-17 06:33:21 +00:00
# < li > rgba ( • • • % , • • • % , • • • % , • • • % ) — also with opacity < / l i >
2013-08-05 08:04:30 +00:00
# < li > hsb ( • • • , • • • , • • • ) — hue , saturation and brightness values : ( “ < code > hsb ( 0.5 , & nbsp ; 0.25 , & nbsp ; 1 ) < / c o d e > ” ) < / l i >
2013-09-17 06:33:21 +00:00
# < li > hsba ( • • • , • • • , • • • , • • • ) — also with opacity < / l i >
2013-08-05 08:04:30 +00:00
# < li > hsb ( • • • % , • • • % , • • • % ) — same as above , but in % < / l i >
2013-09-17 06:33:21 +00:00
# < li > hsba ( • • • % , • • • % , • • • % , • • • % ) — also with opacity < / l i >
# < li > hsl ( • • • , • • • , • • • ) — hue , saturation and luminosity values : ( “ < code > hsb ( 0.5 , & nbsp ; 0.25 , & nbsp ; 0.5 ) < / c o d e > ” ) < / l i >
# < li > hsla ( • • • , • • • , • • • , • • • ) — also with opacity < / l i >
# < li > hsl ( • • • % , • • • % , • • • % ) — same as above , but in % < / l i >
# < li > hsla ( • • • % , • • • % , • • • % , • • • % ) — also with opacity < / l i >
2013-08-05 08:04:30 +00:00
# < / u l >
2013-09-17 06:33:21 +00:00
* Note that ` % ` can be used any time : ` rgb(20%, 255, 50%) ` .
2013-08-05 08:04:30 +00:00
= ( object ) RGB object in 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 cant be parsed
o }
\ * /
Savage . 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 ) ) ;
2013-09-11 12:25:30 +00:00
if ( ! colour ) {
return { r : - 1 , g : - 1 , b : - 1 , hex : "none" , error : 1 , toString : rgbtoString } ;
}
2013-08-05 08:04:30 +00:00
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 ] ) ;
2013-09-16 06:55:33 +00:00
values [ 0 ] . slice ( - 1 ) == "%" && ( red /= 100 ) ;
2013-08-05 08:04:30 +00:00
green = toFloat ( values [ 1 ] ) ;
2013-09-16 06:55:33 +00:00
values [ 1 ] . slice ( - 1 ) == "%" && ( green /= 100 ) ;
2013-08-05 08:04:30 +00:00
blue = toFloat ( values [ 2 ] ) ;
2013-09-16 06:55:33 +00:00
values [ 2 ] . slice ( - 1 ) == "%" && ( blue /= 100 ) ;
2013-08-05 08:04:30 +00:00
( 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 Savage . hsb2rgb ( red , green , blue , opacity ) ;
}
if ( rgb [ 6 ] ) {
values = rgb [ 6 ] . split ( commaSpaces ) ;
red = toFloat ( values [ 0 ] ) ;
2013-09-16 06:55:33 +00:00
values [ 0 ] . slice ( - 1 ) == "%" && ( red /= 100 ) ;
2013-08-05 08:04:30 +00:00
green = toFloat ( values [ 1 ] ) ;
2013-09-16 06:55:33 +00:00
values [ 1 ] . slice ( - 1 ) == "%" && ( green /= 100 ) ;
2013-08-05 08:04:30 +00:00
blue = toFloat ( values [ 2 ] ) ;
2013-09-16 06:55:33 +00:00
values [ 2 ] . slice ( - 1 ) == "%" && ( blue /= 100 ) ;
2013-08-05 08:04:30 +00:00
( 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 Savage . hsl2rgb ( red , green , blue , opacity ) ;
}
2013-09-16 06:55:33 +00:00
red = mmin ( math . round ( red ) , 255 ) ;
green = mmin ( math . round ( green ) , 255 ) ;
blue = mmin ( math . round ( blue ) , 255 ) ;
opacity = mmin ( mmax ( opacity , 0 ) , 1 ) ;
2013-08-05 08:04:30 +00:00
rgb = { r : red , g : green , b : blue , toString : rgbtoString } ;
rgb . hex = "#" + ( 16777216 | blue | ( green << 8 ) | ( red << 16 ) ) . toString ( 16 ) . slice ( 1 ) ;
2013-08-15 08:29:47 +00:00
rgb . opacity = is ( opacity , "finite" ) ? opacity : 1 ;
2013-08-05 08:04:30 +00:00
return rgb ;
}
return { r : - 1 , g : - 1 , b : - 1 , hex : "none" , error : 1 , toString : rgbtoString } ;
} , Savage ) ;
/ * \
* Savage . hsb
[ method ]
* *
* Converts HSB values to hex representation of the colour .
- h ( number ) hue
- s ( number ) saturation
- b ( number ) value or brightness
= ( string ) hex representation of the colour .
\ * /
Savage . hsb = cacher ( function ( h , s , b ) {
return Savage . hsb2rgb ( h , s , b ) . hex ;
} ) ;
/ * \
* Savage . hsl
[ method ]
* *
* Converts HSL values to hex representation of the colour .
- h ( number ) hue
- s ( number ) saturation
- l ( number ) luminosity
= ( string ) hex representation of the colour .
\ * /
Savage . hsl = cacher ( function ( h , s , l ) {
return Savage . hsl2rgb ( h , s , l ) . hex ;
} ) ;
/ * \
* Savage . rgb
[ method ]
* *
* Converts RGB values to hex representation of the colour .
- r ( number ) red
- g ( number ) green
- b ( number ) blue
= ( string ) hex representation of the colour .
\ * /
Savage . 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 ) {
2013-09-11 12:25:30 +00:00
var i = glob . doc . getElementsByTagName ( "head" ) [ 0 ] ,
red = "rgb(255, 0, 0)" ;
2013-08-05 08:04:30 +00:00
toHex = cacher ( function ( color ) {
2013-09-11 12:25:30 +00:00
if ( color . toLowerCase ( ) == "red" ) {
return red ;
}
i . style . color = red ;
2013-08-05 08:04:30 +00:00
i . style . color = color ;
var out = glob . doc . defaultView . getComputedStyle ( i , E ) . getPropertyValue ( "color" ) ;
2013-09-11 12:25:30 +00:00
return out == red ? null : out ;
2013-08-05 08:04:30 +00:00
} ) ;
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 = Savage . 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 ,
2013-08-15 08:29:47 +00:00
opacity : is ( o , "finite" ) ? o : 1 ,
2013-08-05 08:04:30 +00:00
hex : Savage . rgb ( r , g , b ) ,
toString : rgbtoString
} ;
is ( o , "finite" ) && ( rgb . opacity = o ) ;
return rgb ;
} ;
/ * \
* Savage . color
[ method ]
* *
* Parses the color string and returns object with all values for the given color .
- clr ( string ) color string in one of the supported formats ( see @ Savage . getRGB )
= ( object ) Combined RGB & HSB object in 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 cant be parsed ,
o h ( number ) hue ,
o s ( number ) saturation ,
o v ( number ) value ( brightness ) ,
o l ( number ) lightness
o }
\ * /
Savage . color = function ( clr ) {
var rgb ;
if ( is ( clr , "object" ) && "h" in clr && "s" in clr && "b" in clr ) {
rgb = Savage . hsb2rgb ( clr ) ;
clr . r = rgb . r ;
clr . g = rgb . g ;
clr . b = rgb . b ;
2013-08-15 08:29:47 +00:00
clr . opacity = 1 ;
2013-08-05 08:04:30 +00:00
clr . hex = rgb . hex ;
} else if ( is ( clr , "object" ) && "h" in clr && "s" in clr && "l" in clr ) {
rgb = Savage . hsl2rgb ( clr ) ;
clr . r = rgb . r ;
clr . g = rgb . g ;
clr . b = rgb . b ;
2013-08-15 08:29:47 +00:00
clr . opacity = 1 ;
2013-08-05 08:04:30 +00:00
clr . hex = rgb . hex ;
} else {
if ( is ( clr , "string" ) ) {
clr = Savage . getRGB ( clr ) ;
}
2013-09-11 12:25:30 +00:00
if ( is ( clr , "object" ) && "r" in clr && "g" in clr && "b" in clr && ! ( "error" in clr ) ) {
2013-08-05 08:04:30 +00:00
rgb = Savage . rgb2hsl ( clr ) ;
clr . h = rgb . h ;
clr . s = rgb . s ;
clr . l = rgb . l ;
rgb = Savage . 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 ;
2013-09-11 12:25:30 +00:00
clr . error = 1 ;
2013-08-05 08:04:30 +00:00
}
}
clr . toString = rgbtoString ;
return clr ;
} ;
/ * \
* Savage . hsb2rgb
[ method ]
* *
* Converts HSB values to RGB object .
- h ( number ) hue
- s ( number ) saturation
- v ( number ) value or brightness
= ( object ) RGB object in format :
o {
o r ( number ) red ,
o g ( number ) green ,
o b ( number ) blue ,
o hex ( string ) color in HTML / CSS format : # • • • • • •
o }
\ * /
Savage . hsb2rgb = function ( h , s , v , o ) {
if ( is ( h , "object" ) && "h" in h && "s" in h && "b" in h ) {
v = h . b ;
s = h . s ;
h = h . h ;
o = h . o ;
}
h *= 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 ) ;
} ;
/ * \
* Savage . hsl2rgb
[ method ]
* *
* Converts HSL values to RGB object .
- h ( number ) hue
- s ( number ) saturation
- l ( number ) luminosity
= ( object ) RGB object in format :
o {
o r ( number ) red ,
o g ( number ) green ,
o b ( number ) blue ,
o hex ( string ) color in HTML / CSS format : # • • • • • •
o }
\ * /
Savage . 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 < . 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 ) ;
} ;
/ * \
* Savage . rgb2hsb
[ method ]
* *
* Converts RGB values to HSB object .
- r ( number ) red
- g ( number ) green
- b ( number ) blue
= ( object ) HSB object in format :
o {
o h ( number ) hue
o s ( number ) saturation
o b ( number ) brightness
o }
\ * /
Savage . 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 } ;
} ;
/ * \
* Savage . rgb2hsl
[ method ]
* *
* Converts RGB values to HSL object .
- r ( number ) red
- g ( number ) green
- b ( number ) blue
= ( object ) HSL object in format :
o {
o h ( number ) hue
o s ( number ) saturation
o l ( number ) luminosity
o }
\ * /
Savage . 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 < . 5 ? C / ( 2 * L ) :
C / ( 2 - 2 * L ) ) ;
return { h : H , s : S , l : L , toString : hsltoString } ;
} ;
// Transformations
2013-08-20 09:42:07 +00:00
/ * \
* Savage . 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 will be returned straight away )
= ( array ) array of segments .
\ * /
Savage . parsePathString = function ( pathString ) {
if ( ! pathString ) {
return null ;
}
var pth = Savage . path ( pathString ) ;
if ( pth . arr ) {
return Savage . 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 = Savage . 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 ) ;
2013-08-05 08:04:30 +00:00
} ) ;
2013-08-20 09:42:07 +00:00
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 = Savage . path . toString ;
pth . arr = Savage . path . clone ( data ) ;
return data ;
} ;
/ * \
* Savage . parseTransformString
[ method ]
* *
* Utility method
* *
* Parses given path string into an array of transformations .
- TString ( string | array ) transform string or array of transformations ( in the last case it will be returned straight away )
= ( array ) array of transformations .
\ * /
2013-08-05 08:04:30 +00:00
var parseTransformString = Savage . 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 = Savage . 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 = Savage . path . toString ;
return data ;
} ;
function svgTransform2string ( tstr ) {
var res = [ ] ;
tstr = tstr . replace ( /(?:^|\s)(\w+)\(([^)]+)\)/g , function ( all , name , params ) {
params = params . split ( /\s*,\s*/ ) ;
if ( name == "rotate" && params . length == 1 ) {
params . push ( 0 , 0 ) ;
}
if ( name == "scale" ) {
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 ;
}
var rgTransform = new RegExp ( "^[a-z][" + spaces + "]*-?\\.?\\d" ) ;
function extractTransform ( el , tstr ) {
if ( tstr == null ) {
var doReturn = true ;
if ( el . type == "linearGradient" || el . type == "radialGradient" ) {
tstr = el . node . getAttribute ( "gradientTransform" ) ;
} else if ( el . type == "pattern" ) {
tstr = el . node . getAttribute ( "patternTransform" ) ;
} else {
tstr = el . node . getAttribute ( "transform" ) ;
}
if ( ! tstr ) {
return new Matrix ;
}
tstr = svgTransform2string ( tstr ) ;
} else if ( ! rgTransform . test ( tstr ) ) {
tstr = svgTransform2string ( tstr ) ;
} else {
tstr = Str ( tstr ) . replace ( /\.{3}|\u2026/g , el . _ . transform || E ) ;
}
var tdata = parseTransformString ( tstr ) ,
deg = 0 ,
dx = 0 ,
dy = 0 ,
sx = 1 ,
sy = 1 ,
_ = el . _ ,
m = new Matrix ;
_ . transform = tdata || [ ] ;
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 == 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 || el . getBBox ( 1 ) ;
m . rotate ( t [ 1 ] , bb . x + bb . width / 2 , bb . y + bb . height / 2 ) ;
deg += t [ 1 ] ;
} 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 ] ) ;
}
deg += t [ 1 ] ;
}
} else if ( command == "s" ) {
if ( tlen == 2 || tlen == 3 ) {
bb = bb || el . getBBox ( 1 ) ;
m . scale ( t [ 1 ] , t [ tlen - 1 ] , bb . x + bb . width / 2 , bb . y + bb . height / 2 ) ;
sx *= t [ 1 ] ;
sy *= t [ tlen - 1 ] ;
} 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 ] ) ;
}
sx *= t [ 1 ] ;
sy *= t [ 2 ] ;
}
} else if ( command == "m" && tlen == 7 ) {
m . add ( t [ 1 ] , t [ 2 ] , t [ 3 ] , t [ 4 ] , t [ 5 ] , t [ 6 ] ) ;
}
}
if ( doReturn ) {
return m ;
} else {
_ . dirtyT = 1 ;
el . matrix = m ;
}
}
el . matrix = m ;
_ . sx = sx ;
_ . sy = sy ;
_ . deg = deg ;
_ . dx = dx = m . e ;
_ . dy = dy = m . f ;
if ( sx == 1 && sy == 1 && ! deg && _ . bbox ) {
_ . bbox . x += + dx ;
_ . bbox . y += + dy ;
} else {
_ . dirtyT = 1 ;
}
}
2013-09-10 00:22:05 +00:00
Savage . _unit2px = unit2px ;
2013-08-05 08:04:30 +00:00
function unit2px ( el , name , value ) {
var defs = el . paper . defs ,
out = { } ,
mgr = el . paper . measurer ;
if ( ! mgr ) {
el . paper . measurer = mgr = $ ( "rect" ) ;
$ ( mgr , { width : 10 , height : 10 } ) ;
defs . appendChild ( mgr ) ;
}
function getW ( val ) {
if ( val == null ) {
return E ;
}
if ( val == + val ) {
return val ;
}
$ ( mgr , { width : val } ) ;
return mgr . getBBox ( ) . width ;
}
function getH ( val ) {
if ( val == null ) {
return E ;
}
if ( val == + val ) {
return val ;
}
$ ( mgr , { height : val } ) ;
return mgr . getBBox ( ) . height ;
}
function set ( nam , f ) {
if ( name == null ) {
out [ nam ] = f ( el . attr ( nam ) ) ;
} else if ( nam == name ) {
out = f ( value == null ? el . attr ( nam ) : 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 :
out = null ;
}
return out ;
}
/ * \
* Savage . select
[ method ]
* *
* Wraps DOM element specified by CSS selector as @ Element
- query ( string ) CSS selector of the element
= ( Element )
\ * /
Savage . select = function ( query ) {
2013-08-09 12:13:22 +00:00
return wrap ( glob . doc . querySelector ( query ) ) ;
2013-08-05 08:04:30 +00:00
} ;
/ * \
* Savage . selectAll
[ method ]
* *
* Wraps DOM elements specified by CSS selector as set or array of @ Element
- query ( string ) CSS selector of the element
= ( Element )
\ * /
Savage . selectAll = function ( query ) {
var nodelist = glob . doc . querySelectorAll ( query ) ,
set = ( Savage . set || Array ) ( ) ;
for ( var i = 0 ; i < nodelist . length ; i ++ ) {
2013-08-09 12:13:22 +00:00
set . push ( wrap ( nodelist [ i ] ) ) ;
2013-08-05 08:04:30 +00:00
}
return set ;
} ;
2013-09-05 13:24:16 +00:00
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 ++ ) if ( children [ i ] . savage ) {
this [ j ++ ] = hub [ children [ i ] . savage ] ;
}
}
2013-08-05 08:04:30 +00:00
function Element ( el ) {
if ( el . savage in hub ) {
return hub [ el . savage ] ;
}
var id = this . id = ID ( ) ,
svg ;
try {
svg = el . ownerSVGElement ;
} catch ( e ) { }
this . node = el ;
if ( svg ) {
this . paper = new Paper ( svg ) ;
}
this . type = el . tagName ;
2013-08-20 09:42:07 +00:00
this . anims = { } ;
2013-08-05 08:04:30 +00:00
this . _ = {
transform : [ ] ,
sx : 1 ,
sy : 1 ,
deg : 0 ,
dx : 0 ,
dy : 0 ,
dirty : 1
} ;
el . savage = id ;
hub [ id ] = this ;
2013-09-05 13:24:16 +00:00
if ( this . type == "g" ) {
this . add = add2group ;
for ( var method in Paper . prototype ) if ( Paper . prototype [ has ] ( method ) ) {
this [ method ] = Paper . prototype [ method ] ;
}
}
2013-08-05 08:04:30 +00:00
}
function arrayFirstValue ( arr ) {
var res ;
for ( var i = 0 , ii = arr . length ; i < ii ; i ++ ) {
res = res || arr [ i ] ;
if ( res ) {
return res ;
}
}
}
( function ( elproto ) {
2013-08-20 09:42:07 +00:00
/ * \
* Element . attr
[ method ]
* *
* Gets or sets given attributes of the element
* *
- params ( object ) key - value pairs of attributes you want to set
* or
- param ( string ) name of the attribute
= ( Element )
* or
= ( string ) value of attribute
> Usage
| el . attr ( {
| fill : "#fc0" ,
| stroke : "#000" ,
| strokeWidth : 2 , // CamelCase...
| "fill-opacity" : 0.5 // or dash-separated names
| } ) ;
| console . log ( el . attr ( "fill" ) ) ; // “#fc0”
\ * /
2013-09-10 00:38:04 +00:00
elproto . attr = function ( params , value ) {
var el = this ,
node = el . node ;
2013-09-09 07:36:48 +00:00
if ( ! params ) {
2013-09-10 00:38:04 +00:00
return el ;
2013-09-09 07:36:48 +00:00
}
2013-08-05 08:04:30 +00:00
if ( is ( params , "string" ) ) {
2013-09-10 00:38:04 +00:00
if ( arguments . length > 1 ) {
var json = { } ;
json [ params ] = value ;
params = json ;
} else {
return arrayFirstValue ( eve ( "savage.util.getattr." + params , el ) ) ;
}
2013-08-05 08:04:30 +00:00
}
for ( var att in params ) {
if ( params [ has ] ( att ) ) {
2013-09-10 00:38:04 +00:00
eve ( "savage.util.attr." + att , el , params [ att ] ) ;
2013-08-05 08:04:30 +00:00
}
}
2013-09-10 00:38:04 +00:00
return el ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . getBBox
[ method ]
* *
* Returns 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 the circle that will enclose the box ,
o r1 : ( number ) radius of the smallest circle that can be enclosed ,
o r2 : ( number ) radius of the biggest 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 right side ,
o y : ( number ) y of the left side
o }
\ * /
2013-08-05 08:04:30 +00:00
elproto . getBBox = function ( isWithoutTransform ) {
if ( this . removed ) {
return { } ;
}
var _ = this . _ ;
if ( isWithoutTransform ) {
if ( _ . dirty || ! _ . bboxwt ) {
this . realPath = Savage . path . get [ this . type ] ( this ) ;
_ . bboxwt = Savage . path . getBBox ( this . realPath ) ;
_ . bboxwt . toString = x _y _w _h ;
_ . dirty = 0 ;
}
return Savage . _ . box ( _ . bboxwt ) ;
}
if ( _ . dirty || _ . dirtyT || ! _ . bbox ) {
if ( _ . dirty || ! this . realPath ) {
_ . bboxwt = 0 ;
this . realPath = Savage . path . get [ this . type ] ( this ) ;
}
_ . bbox = Savage . path . getBBox ( Savage . path . map ( this . realPath , this . matrix ) ) ;
_ . bbox . toString = x _y _w _h ;
_ . dirty = _ . dirtyT = 0 ;
}
return Savage . _ . box ( _ . bbox ) ;
} ;
var propString = function ( ) {
return this . local ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . transform
[ method ]
* *
* Gets or sets transformation of the element
* *
- tstr ( string ) transform string in Savage or SVG format
= ( 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 }
\ * /
2013-08-05 08:04:30 +00:00
elproto . transform = function ( tstr ) {
var _ = this . _ ;
if ( tstr == null ) {
var global = new Matrix ( this . node . getCTM ( ) ) ,
local = extractTransform ( this ) ;
return {
2013-09-12 01:23:53 +00:00
string : Str ( _ . transform ) || "" ,
2013-08-05 08:04:30 +00:00
globalMatrix : global ,
localMatrix : local ,
diffMatrix : global . clone ( ) . add ( local . invert ( ) ) ,
global : global . toTransformString ( ) ,
local : local . toTransformString ( ) ,
toString : propString
} ;
}
if ( tstr instanceof Matrix ) {
// may be need to apply it directly
// TODO: investigate
tstr = tstr . toTransformString ( ) ;
}
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 ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . parent
[ method ]
* *
* Returns parent of the element
* *
= ( Element ) parent
\ * /
2013-08-05 08:04:30 +00:00
elproto . parent = function ( ) {
2013-08-20 09:42:07 +00:00
return wrap ( this . node . parentNode ) ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . append
[ method ]
* *
* Appends given element to current one .
* *
- el ( Element | Set ) element to append
= ( Element ) parent
\ * /
2013-09-02 04:26:51 +00:00
/ * \
* Element . add
[ method ]
* *
* See @ Element . append .
\ * /
elproto . append = elproto . add = function ( el ) {
2013-08-05 08:04:30 +00:00
if ( el . type == "set" ) {
var it = this ;
el . forEach ( function ( el ) {
it . append ( el ) ;
} ) ;
return this ;
}
el = wrap ( el ) ;
this . node . appendChild ( el . node ) ;
el . paper = this . paper ;
return this ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . prepend
[ method ]
* *
* Prepends given element to current one .
* *
- el ( Element ) element to prepend
= ( Element ) parent
\ * /
2013-08-05 08:04:30 +00:00
elproto . prepend = function ( el ) {
el = wrap ( el ) ;
2013-09-10 00:06:35 +00:00
this . node . insertBefore ( el . node , this . node . firstChild ) ;
2013-08-05 08:04:30 +00:00
el . paper = this . paper ;
return this ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . before
[ method ]
* *
* Inserts given element before the current one .
* *
- el ( Element ) element to insert
= ( Element ) parent
\ * /
// TODO make it work for sets too
2013-08-05 08:04:30 +00:00
elproto . before = function ( el ) {
el = wrap ( el ) ;
this . node . parentNode . insertBefore ( el . node , this . node ) ;
el . paper = this . paper ;
return this ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . after
[ method ]
* *
* Inserts given element after the current one .
* *
- el ( Element ) element to insert
= ( Element ) parent
\ * /
2013-08-05 08:04:30 +00:00
elproto . after = function ( el ) {
el = wrap ( el ) ;
this . node . parentNode . insertBefore ( el . node , this . node . nextSibling ) ;
el . paper = this . paper ;
return this ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . insertBefore
[ method ]
* *
* Inserts the element after the given one .
* *
- el ( Element ) element next to whom insert to
= ( Element ) parent
\ * /
2013-08-05 08:04:30 +00:00
elproto . insertBefore = function ( el ) {
el = wrap ( el ) ;
el . node . parentNode . insertBefore ( this . node , el . node ) ;
this . paper = el . paper ;
return this ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . insertAfter
[ method ]
* *
* Inserts the element after the given one .
* *
- el ( Element ) element next to whom insert to
= ( Element ) parent
\ * /
2013-08-05 08:04:30 +00:00
elproto . insertAfter = function ( el ) {
el = wrap ( el ) ;
el . node . parentNode . insertBefore ( this . node , el . node . nextSibling ) ;
this . paper = el . paper ;
return this ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . remove
[ method ]
* *
* Removes element from the DOM
2013-09-10 02:22:49 +00:00
= ( Element ) removed element
2013-08-20 09:42:07 +00:00
\ * /
2013-08-05 08:04:30 +00:00
elproto . remove = function ( ) {
this . node . parentNode . removeChild ( this . node ) ;
delete this . paper ;
this . removed = true ;
2013-09-10 02:22:49 +00:00
return this ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . select
[ method ]
* *
* Applies CSS selector with the element as a parent and returns the result as an @ Element .
* *
- query ( string ) CSS selector
= ( Element ) result of query selection
\ * /
2013-08-05 08:04:30 +00:00
elproto . select = function ( query ) {
2013-08-09 12:13:22 +00:00
return wrap ( this . node . querySelector ( query ) ) ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . selectAll
[ method ]
* *
* Applies CSS selector with the element as a parent and returns the result as a set or array of elements .
* *
- query ( string ) CSS selector
= ( Set | array ) result of query selection
\ * /
2013-08-05 08:04:30 +00:00
elproto . selectAll = function ( query ) {
var nodelist = this . node . querySelectorAll ( query ) ,
set = ( Savage . set || Array ) ( ) ;
for ( var i = 0 ; i < nodelist . length ; i ++ ) {
2013-08-09 12:13:22 +00:00
set . push ( wrap ( nodelist [ i ] ) ) ;
2013-08-05 08:04:30 +00:00
}
return set ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . asPX
[ method ]
* *
* Return 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
\ * /
2013-08-05 08:04:30 +00:00
elproto . asPX = function ( attr , value ) {
2013-08-20 09:42:07 +00:00
if ( value == null ) {
value = this . attr ( attr ) ;
}
2013-08-05 08:04:30 +00:00
return unit2px ( this , attr , value ) ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . use
[ method ]
* *
* Creates ` <use> ` element linked to the current element .
* *
= ( Element ) ` <use> ` element
\ * /
2013-08-05 08:04:30 +00:00
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
} ) ;
return use ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . clone
[ method ]
* *
2013-09-10 00:38:04 +00:00
* Creates clone of the element and inserts it after the element .
2013-08-20 09:42:07 +00:00
* *
2013-09-10 00:38:04 +00:00
= ( Element ) the clone
2013-08-20 09:42:07 +00:00
\ * /
2013-09-11 03:17:32 +00:00
function fixids ( el ) {
var els = el . selectAll ( "*" ) ,
it ,
url = /^\s*url\(("|'|)(.*)\1\)\s*$/ ,
ids = [ ] ,
uses = { } ;
function urltest ( it , name ) {
var val = $ ( it . node , name ) ;
val = val && val . match ( url ) ;
val = val && val [ 2 ] ;
if ( val && val . charAt ( ) == "#" ) {
val = val . substring ( 1 ) ;
} else {
return ;
}
if ( val ) {
uses [ val ] = ( uses [ val ] || [ ] ) . concat ( function ( id ) {
var attr = { } ;
attr [ name ] = "url(#" + id + ")" ;
$ ( it . node , attr ) ;
} ) ;
}
}
function linktest ( it ) {
var val = $ ( it . node , "xlink:href" ) ;
if ( val && val . charAt ( ) == "#" ) {
val = val . substring ( 1 ) ;
} else {
return ;
}
if ( val ) {
uses [ val ] = ( uses [ val ] || [ ] ) . concat ( function ( id ) {
it . attr ( "xlink:href" , "#" + id ) ;
} ) ;
}
}
for ( var i = 0 , ii = els . length ; i < ii ; i ++ ) {
it = els [ i ] ;
urltest ( it , "fill" ) ;
urltest ( it , "stroke" ) ;
urltest ( it , "filter" ) ;
urltest ( it , "mask" ) ;
urltest ( it , "clip-path" ) ;
linktest ( it ) ;
var oldid = $ ( it . node , "id" ) ;
if ( oldid ) {
$ ( it . node , { id : it . id } ) ;
ids . push ( {
old : oldid ,
id : it . id
} ) ;
}
}
for ( i = 0 , ii = ids . length ; i < ii ; i ++ ) {
var fs = uses [ ids [ i ] . old ] ;
if ( fs ) {
for ( var j = 0 , jj = fs . length ; j < jj ; j ++ ) {
fs [ j ] ( ids [ i ] . id ) ;
}
}
}
}
2013-08-05 08:04:30 +00:00
elproto . clone = function ( ) {
2013-08-20 09:42:07 +00:00
var clone = wrap ( this . node . cloneNode ( true ) ) ;
2013-09-11 03:17:32 +00:00
if ( $ ( clone . node , "id" ) ) {
$ ( clone . node , { id : clone . id } ) ;
}
fixids ( clone ) ;
2013-08-20 09:42:07 +00:00
clone . insertAfter ( this ) ;
return clone ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . pattern
[ method ]
* *
* Creates ` <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 ) ` <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
| } ) ;
\ * /
2013-08-05 08:04:30 +00:00
elproto . pattern = function ( x , y , width , height ) {
var p = make ( "pattern" , this . paper . defs ) ;
if ( x == null ) {
x = this . getBBox ( ) ;
}
if ( x && "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 ;
} ;
2013-08-20 09:42:07 +00:00
/ * \
* Element . marker
[ method ]
* *
* Creates ` <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 ) ` <marker> ` element
* You can use pattern later on as an argument for ` marker-start ` or ` marker-end ` attributes .
\ * /
// TODO add usage for markers
2013-08-05 08:04:30 +00:00
elproto . marker = function ( x , y , width , height , refX , refY ) {
var p = make ( "marker" , this . paper . defs ) ;
if ( x == null ) {
x = this . getBBox ( ) ;
}
if ( x && "x" in x ) {
y = x . y ;
width = x . width ;
height = x . height ;
2013-08-20 09:42:07 +00:00
refX = x . refX || x . cx ;
refY = x . refY || x . cy ;
2013-08-05 08:04:30 +00:00
x = x . x ;
}
$ ( p . node , {
viewBox : [ x , y , width , height ] . join ( S ) ,
markerWidth : width ,
markerHeight : height ,
orient : "auto" ,
refX : refX || 0 ,
refY : refY || 0 ,
id : p . id
} ) ;
p . node . appendChild ( this . node ) ;
return p ;
} ;
// animation
2013-08-15 08:29:47 +00:00
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 ;
2013-08-05 08:04:30 +00:00
} ;
}
2013-08-20 09:42:07 +00:00
var Animation = function ( attr , ms , easing , callback ) {
2013-09-13 12:51:01 +00:00
if ( typeof easing == "function" && ! easing . length ) {
2013-08-20 09:42:07 +00:00
callback = easing ;
easing = mina . linear ;
}
this . attr = attr ;
this . dur = ms ;
easing && ( this . easing = easing ) ;
callback && ( this . callback = callback ) ;
} ;
/ * \
* Savage . animation
[ method ]
* *
* Creates animation object .
* *
- attr ( object ) attributes of final destination
- ms ( number ) animation duration
- easing ( function ) # optional one of easing functions of @ mina or custom one
- callback ( function ) # optional callback
= ( object ) animation object
\ * /
Savage . animation = function ( attr , ms , easing , callback ) {
return new Animation ( attr , ms , easing , callback ) ;
} ;
/ * \
2013-08-22 10:00:40 +00:00
* Element . inAnim
2013-08-20 09:42:07 +00:00
[ method ]
* *
2013-08-22 10:00:40 +00:00
* Returns an array of animations element currently in
2013-08-20 09:42:07 +00:00
* *
2013-08-22 10:00:40 +00:00
= ( object ) in format
o {
o anim ( object ) animation 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 }
2013-08-20 09:42:07 +00:00
\ * /
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 ) ,
curStatus : a . status ( ) ,
status : function ( val ) {
return a . status ( val ) ;
2013-08-22 10:00:40 +00:00
} ,
stop : function ( ) {
a . stop ( ) ;
2013-08-20 09:42:07 +00:00
}
} ) ;
} ( el . anims [ id ] ) ) ;
}
return res ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Savage . 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 will take one number argument
- ms ( number ) duration
- easing ( function ) # optional easing function from @ mina or custom
- callback ( function ) # optional
= ( 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 }
\ * /
2013-08-20 09:42:07 +00:00
Savage . animate = function ( from , to , setter , ms , easing , callback ) {
2013-09-13 12:51:01 +00:00
if ( typeof easing == "function" && ! easing . length ) {
2013-08-20 09:42:07 +00:00
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 ) ;
2013-08-22 10:00:40 +00:00
return anim ;
2013-08-20 09:42:07 +00:00
} ;
2013-09-02 04:26:51 +00:00
/ * \
* Element . stop
[ method ]
* *
* Stops all the animations of the current element .
* *
= ( Element ) the element
\ * /
elproto . stop = function ( ) {
var anims = this . inAnim ( ) ;
for ( var i = 0 , ii = anims . length ; i < ii ; i ++ ) {
anims [ i ] . stop ( ) ;
}
return this ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Element . animate
[ method ]
* *
* Animate given attributes of the element .
* *
- attrs ( object ) key - value pairs of destination attributes
- ms ( number ) duration
- easing ( function ) # optional easing function from @ mina or custom
- callback ( function ) # optional
= ( Element ) the element
\ * /
2013-08-20 09:42:07 +00:00
elproto . animate = function ( attrs , ms , easing , callback ) {
2013-09-11 04:38:52 +00:00
if ( typeof easing == "function" && ! easing . length ) {
2013-08-20 09:42:07 +00:00
callback = easing ;
easing = mina . linear ;
}
if ( attrs instanceof Animation ) {
callback = attrs . callback ;
easing = attrs . easing ;
ms = easing . dur ;
attrs = attrs . attr ;
}
2013-08-22 10:00:40 +00:00
var fkeys = [ ] , tkeys = [ ] , keys = { } , from , to , f , eq ,
el = this ;
2013-08-05 08:04:30 +00:00
for ( var key in attrs ) if ( attrs [ has ] ( key ) ) {
2013-08-22 10:00:40 +00:00
if ( el . equal ) {
eq = el . equal ( key , Str ( attrs [ key ] ) ) ;
2013-08-15 08:29:47 +00:00
from = eq . from ;
to = eq . to ;
f = eq . f ;
2013-08-05 08:04:30 +00:00
} else {
2013-08-22 10:00:40 +00:00
from = + el . attr ( key ) ;
2013-08-15 08:29:47 +00:00
to = + attrs [ key ] ;
2013-08-05 08:04:30 +00:00
}
2013-08-15 08:29:47 +00:00
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 ( ) ,
2013-08-16 02:56:19 +00:00
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 ) ;
2013-08-20 09:42:07 +00:00
} , easing ) ;
el . anims [ anim . id ] = anim ;
anim . _attrs = attrs ;
anim . _callback = callback ;
eve . once ( "mina.finish." + anim . id , function ( ) {
delete el . anims [ anim . id ] ;
callback && callback . call ( el ) ;
} ) ;
eve . once ( "mina.stop." + anim . id , function ( ) {
delete el . anims [ anim . id ] ;
2013-08-15 08:29:47 +00:00
} ) ;
2013-08-22 10:00:40 +00:00
return el ;
2013-08-05 08:04:30 +00:00
} ;
2013-09-10 00:06:35 +00:00
var eldata = { } ;
/ * \
* Element . data
[ method ]
* *
* Adds or retrieves given value asociated with given key .
* *
* 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 == 1 ) {
if ( Savage . is ( key , "object" ) ) {
for ( var i in key ) if ( key [ has ] ( i ) ) {
this . data ( i , key [ i ] ) ;
}
return this ;
}
eve ( "savage.data.get." + this . id , this , data [ key ] , key ) ;
return data [ key ] ;
}
data [ key ] = value ;
eve ( "savage.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 ;
} ;
2013-09-11 04:38:52 +00:00
/ * \
* Element . toString
[ method ]
* *
* Returns SVG code of the element . Equivalent to ` outerHTML ` in HTML context .
= ( string ) SVG code of the element .
\ * /
elproto . toString = toString ( 1 ) ;
/ * \
* Element . innerSVG
[ method ]
* *
* Returns SVG code of the element . Equivalent to ` innerHTML ` in HTML context .
= ( string ) SVG code of 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 , '\\"' ) + '"' ;
2013-09-11 03:17:32 +00:00
}
}
2013-09-11 04:38:52 +00:00
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 ;
} ;
}
2013-08-05 08:04:30 +00:00
} ( Element . prototype ) ) ;
2013-08-22 10:00:40 +00:00
/ * \
* Savage . parse
[ method ]
* *
* Parses SVG fragment and converts it into @ Fragment .
* *
- svg ( string ) SVG string
= ( Fragment ) the fragment
\ * /
2013-08-05 08:04:30 +00:00
Savage . parse = function ( svg ) {
2013-09-09 07:22:14 +00:00
var f = glob . doc . createDocumentFragment ( ) ,
2013-08-05 08:04:30 +00:00
pointer = f ;
eve . on ( "elemental.tag" , function ( data , extra , raw ) {
var tag = $ ( data ) ;
2013-08-15 08:29:47 +00:00
extra && $ ( tag , extra ) ;
2013-08-05 08:04:30 +00:00
pointer . appendChild ( tag ) ;
pointer = tag ;
} ) ;
2013-08-15 08:29:47 +00:00
eve . on ( "elemental.text" , function ( text ) {
2013-09-09 07:22:14 +00:00
pointer . appendChild ( glob . doc . createTextNode ( text ) ) ;
2013-08-15 08:29:47 +00:00
} ) ;
2013-08-05 08:04:30 +00:00
eve . on ( "elemental./tag" , function ( ) {
pointer = pointer . parentNode ;
} ) ;
eve . on ( "elemental.eof" , function ( ) {
eve . off ( "elemental.*" ) ;
eve ( "savage.parsed" , f ) ;
} ) ;
elemental ( ) . parse ( svg ) . end ( ) ;
2013-08-15 08:29:47 +00:00
return new Fragment ( f ) ;
2013-08-05 08:04:30 +00:00
} ;
function Fragment ( frag ) {
this . node = frag ;
}
2013-08-22 10:00:40 +00:00
/ * \
* Fragment . select
[ method ]
* *
* See @ Element . select
\ * /
2013-08-05 08:04:30 +00:00
Fragment . prototype . select = Element . prototype . select ;
2013-08-22 10:00:40 +00:00
/ * \
* Fragment . selectAll
[ method ]
* *
* See @ Element . selectAll
\ * /
2013-08-05 08:04:30 +00:00
Fragment . prototype . selectAll = Element . prototype . selectAll ;
2013-08-22 10:00:40 +00:00
/ * \
* Savage . fragment
[ method ]
* *
* Creates DOM fragment from given list of elements or strings
* *
- varargs ( … ) SVG string
= ( Fragment ) the @ Fragment
\ * /
2013-08-05 08:04:30 +00:00
Savage . fragment = function ( ) {
var args = Array . prototype . slice . call ( arguments , 0 ) ,
2013-09-09 07:22:14 +00:00
f = glob . doc . createDocumentFragment ( ) ;
2013-08-05 08:04:30 +00:00
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" ) {
2013-08-15 08:29:47 +00:00
f . appendChild ( Savage . parse ( item ) . node ) ;
2013-08-05 08:04:30 +00:00
}
}
return new Fragment ( f ) ;
} ;
function make ( name , parent ) {
var res = $ ( name ) ;
parent . appendChild ( res ) ;
2013-08-09 12:13:22 +00:00
var el = wrap ( res ) ;
2013-08-05 08:04:30 +00:00
el . type = name ;
return el ;
}
function Paper ( w , h ) {
var res ,
desc ,
defs ,
proto = Paper . prototype ;
if ( w && w . tagName == "svg" ) {
if ( w . savage in hub ) {
return hub [ w . savage ] ;
}
res = new Element ( w ) ;
desc = w . getElementsByTagName ( "desc" ) [ 0 ] ;
defs = w . getElementsByTagName ( "defs" ) [ 0 ] ;
} else {
res = make ( "svg" , glob . doc . body ) ;
$ ( res . node , {
height : h ,
version : 1.1 ,
width : w ,
xmlns : "http://www.w3.org/2000/svg"
} ) ;
}
if ( ! desc ) {
desc = $ ( "desc" ) ;
desc . appendChild ( glob . doc . createTextNode ( "Created with Savage" ) ) ;
res . node . appendChild ( desc ) ;
}
if ( ! defs ) {
defs = $ ( "defs" ) ;
res . node . appendChild ( defs ) ;
}
for ( var key in proto ) if ( proto [ has ] ( key ) ) {
res [ key ] = proto [ key ] ;
}
2013-09-02 04:26:51 +00:00
res . paper = res . root = res ;
2013-08-05 08:04:30 +00:00
res . defs = defs ;
return res ;
}
function wrap ( dom ) {
2013-08-09 12:13:22 +00:00
if ( ! dom ) {
return dom ;
}
2013-08-05 08:04:30 +00:00
if ( dom instanceof Element || dom instanceof Fragment ) {
return dom ;
}
2013-08-09 12:13:22 +00:00
if ( dom . tagName == "svg" ) {
return new Paper ( dom ) ;
}
2013-08-05 08:04:30 +00:00
return new Element ( dom ) ;
}
( function ( proto ) {
2013-08-22 10:00:40 +00:00
/ * \
* Paper . el
[ method ]
* *
* Creates element on paper with a given name and no attributes .
* *
2013-09-09 07:36:48 +00:00
- name ( string ) tag name
- attr ( object ) attributes
2013-08-22 10:00:40 +00:00
= ( Element ) the 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
| } ) ;
\ * /
2013-09-09 07:36:48 +00:00
proto . el = function ( name , attr ) {
return make ( name , this . node ) . attr ( attr ) ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-22 10:00:40 +00:00
/ * \
* 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 horisontal radius for rounded corners , default is 0
- ry ( number ) # optional vertical radius for rounded corners , default is rx or 0
= ( object ) Element object with type “ rect ”
* *
> Usage
| // regular rectangle
| var c = paper . rect ( 10 , 10 , 50 , 50 ) ;
| // rectangle with rounded corners
| var c = paper . rect ( 40 , 40 , 50 , 50 , 10 ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . rect = function ( x , y , w , h , rx , ry ) {
var el = make ( "rect" , this . node ) ;
if ( ry == null ) {
ry = rx ;
}
if ( is ( x , "object" ) && "x" in x ) {
el . attr ( x ) ;
} else if ( x != null ) {
el . attr ( {
x : x ,
y : y ,
width : w ,
height : h
} ) ;
if ( rx != null ) {
el . attr ( {
rx : rx ,
ry : ry
} ) ;
}
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . circle
[ method ]
* *
* Draws a circle .
* *
- x ( number ) x coordinate of the centre
- y ( number ) y coordinate of the centre
- r ( number ) radius
= ( object ) Element object with type “ circle ”
* *
> Usage
| var c = paper . circle ( 50 , 50 , 40 ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . circle = function ( cx , cy , r ) {
var el = make ( "circle" , this . node ) ;
if ( is ( cx , "object" ) && "cx" in cx ) {
el . attr ( cx ) ;
} else if ( cx != null ) {
el . attr ( {
cx : cx ,
cy : cy ,
r : r
} ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . image
[ method ]
* *
* Embeds an image into the surface .
* *
- src ( string ) URI of the source image
- x ( number ) x coordinate position
- y ( number ) y coordinate position
- width ( number ) width of the image
- height ( number ) height of the image
= ( object ) Raphaël element object with type “ image ”
* *
> Usage
| var c = paper . image ( "apple.png" , 10 , 10 , 80 , 80 ) ;
\ * /
/ * \
* Paper . image
[ method ]
* *
* Embeds an image into the surface .
* *
- src ( string ) URI of the source image
- x ( number ) x coordinate position
- y ( number ) y coordinate position
- width ( number ) width of the image
- height ( number ) height of the image
= ( object ) Element object with type “ image ”
* *
> Usage
| var c = paper . image ( "apple.png" , 10 , 10 , 80 , 80 ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . image = function ( src , x , y , width , height ) {
var el = make ( "image" , this . node ) ;
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 ( ) {
$ ( el . node , {
width : this . offsetWidth ,
height : this . offsetHeight
} ) ;
} ) ;
}
$ ( el . node , set ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* 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 ) Element object with type “ ellipse ”
* *
> Usage
| var c = paper . ellipse ( 50 , 50 , 40 , 20 ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . ellipse = function ( cx , cy , rx , ry ) {
var el = make ( "ellipse" , this . node ) ;
if ( is ( cx , "object" ) && "cx" in cx ) {
el . attr ( cx ) ;
} else if ( cx != null ) {
el . attr ( {
cx : cx ,
cy : cy ,
rx : rx ,
ry : ry
} ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . path
[ method ]
* *
* Creates a path element by given path data string .
- pathString ( string ) # optional path string in SVG format .
* Path string consists of one - letter commands , followed by comma seprarated arguments in numercal form . Example :
| "M10,20L30,40"
* Here we can see two commands : “ M ” , with arguments ` (10, 20) ` and “ L ” with arguments ` (30, 40) ` . Upper case letter mean command is absolute , lower case — relative .
*
# < 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 < / t h > < t h > N a m e < / t h > < t h > P a r a m e t e r s < / t h > < / t r > < / t h e a d > < t b o d y >
# < tr > < td > M < / t d > < t d > m o v e t o < / t d > < t d > ( x y ) + < / t d > < / t r >
# < tr > < td > Z < / t d > < t d > c l o s e p a t h < / t d > < t d > ( n o n e ) < / t d > < / t r >
# < tr > < td > L < / t d > < t d > l i n e t o < / t d > < t d > ( x y ) + < / t d > < / t r >
# < tr > < td > H < / t d > < t d > h o r i z o n t a l l i n e t o < / t d > < t d > x + < / t d > < / t r >
# < tr > < td > V < / t d > < t d > v e r t i c a l l i n e t o < / t d > < t d > y + < / t d > < / t r >
# < tr > < td > C < / t d > < t d > c u r v e t o < / t d > < t d > ( x 1 y 1 x 2 y 2 x y ) + < / t d > < / t r >
# < tr > < td > S < / t d > < t d > s m o o t h c u r v e t o < / t d > < t d > ( x 2 y 2 x y ) + < / t d > < / t r >
# < tr > < td > Q < / t d > < t d > q u a d r a t i c B é z i e r c u r v e t o < / t d > < t d > ( x 1 y 1 x y ) + < / t d > < / t r >
# < tr > < td > T < / t d > < t d > s m o o t h q u a d r a t i c B é z i e r c u r v e t o < / t d > < t d > ( x y ) + < / t d > < / t r >
# < tr > < td > A < / t d > < t d > e l l i p t i c a l a r c < / t d > < t d > ( r x r y x - a x i s - r o t a t i o n l a r g e - a r c - f l a g s w e e p - f l a g x y ) + < / t d > < / t r >
# < tr > < td > R < /td><td><a href="http:/ / en . wikipedia . org / wiki / Catmull – Rom _spline # Catmull . E2 . 80.93 Rom _spline " > Catmull - Rom curveto < / a > * < / t d > < t d > x 1 y 1 ( x y ) + < / t d > < / t r > < / t b o d y > < / t a b l e >
* * “ Catmull - Rom curveto ” is a not standard SVG command and added to make life easier .
* Note : there is a special case when path consist of just three commands : “ M10 , 10 R … z ” . In this case path will smoothly connects to its beginning .
> Usage
| var c = paper . path ( "M10 10L90 90" ) ;
| // draw a diagonal line:
| // move to 10,10, line to 90,90
\ * /
2013-08-05 08:04:30 +00:00
proto . path = function ( d ) {
var el = make ( "path" , this . node ) ;
2013-09-17 06:33:21 +00:00
if ( is ( d , "object" ) && ! is ( d , "array" ) ) {
2013-08-05 08:04:30 +00:00
el . attr ( d ) ;
} else if ( d ) {
el . attr ( {
d : d
} ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . g
[ method ]
* *
* Makes a group element .
* *
- varargs ( … ) # optional elements
= ( object ) Element object with type “ g ”
* *
> Usage
| var c1 = paper . circle ( ) ,
| c2 = paper . rect ( ) ,
| g = paper . g ( c2 , c1 ) ; // note that the order of elements will be different
* or
| var c1 = paper . circle ( ) ,
| c2 = paper . rect ( ) ,
| g = paper . g ( ) ;
| g . add ( c2 , c1 ) ;
\ * /
/ * \
* Paper . group
[ method ]
* *
* See @ Paper . g
\ * /
2013-09-09 07:36:48 +00:00
proto . group = proto . g = function ( first ) {
2013-08-05 08:04:30 +00:00
var el = make ( "g" , this . node ) ;
el . add = add2group ;
2013-09-02 04:26:51 +00:00
for ( var method in proto ) if ( proto [ has ] ( method ) ) {
el [ method ] = proto [ method ] ;
}
2013-09-09 07:36:48 +00:00
if ( arguments . length == 1 && first && ! first . type ) {
el . attr ( first ) ;
} else if ( arguments . length ) {
el . add ( Array . prototype . slice . call ( arguments , 0 ) ) ;
2013-08-05 08:04:30 +00:00
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* 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 < tspan > s
= ( object ) Element object with type “ text ”
* *
> Usage
| var t1 = paper . text ( 50 , 50 , "Savage" ) ;
| var t2 = paper . text ( 50 , 50 , [ "S" , "a" , "v" , "a" , "g" , "e" ] ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . text = function ( x , y , text ) {
var el = make ( "text" , this . node ) ;
if ( is ( x , "object" ) ) {
2013-09-05 00:10:45 +00:00
el . attr ( x ) ;
2013-08-05 08:04:30 +00:00
} else if ( x != null ) {
el . attr ( {
x : x ,
y : y ,
text : text || ""
} ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* 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 ) Element object with type “ line ”
* *
> Usage
| var t1 = paper . line ( 50 , 50 , 100 , 100 ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . line = function ( x1 , y1 , x2 , y2 ) {
var el = make ( "line" , this . node ) ;
if ( is ( x1 , "object" ) ) {
el . attr ( x1 ) ;
} else if ( x1 != null ) {
el . attr ( {
x1 : x1 ,
x2 : x2 ,
y1 : y1 ,
y2 : y2
} ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . polyline
[ method ]
* *
* Draws a polyline .
* *
- points ( array ) array of points
* or
- varargs ( … ) points
= ( object ) Element object with type “ text ”
* *
> Usage
| var p1 = paper . polyline ( [ 10 , 10 , 100 , 100 ] ) ;
| var p2 = paper . polyline ( 10 , 10 , 100 , 100 ) ;
\ * /
2013-08-05 08:04:30 +00:00
proto . polyline = function ( points ) {
if ( arguments . length > 1 ) {
points = Array . prototype . slice . call ( arguments , 0 ) ;
}
var el = make ( "polyline" , this . node ) ;
if ( is ( points , "object" ) && ! is ( points , "array" ) ) {
el . attr ( points ) ;
} else if ( points != null ) {
el . attr ( {
points : points
} ) ;
}
return el ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . polygon
[ method ]
* *
* Draws a polygon . See @ Paper . polyline
\ * /
2013-08-05 08:04:30 +00:00
proto . polygon = function ( points ) {
if ( arguments . length > 1 ) {
points = Array . prototype . slice . call ( arguments , 0 ) ;
}
var el = make ( "polygon" , this . node ) ;
if ( is ( points , "object" ) && ! is ( points , "array" ) ) {
el . attr ( points ) ;
} else if ( points != null ) {
el . attr ( {
points : points
} ) ;
}
return el ;
} ;
// gradients
( function ( ) {
2013-08-22 10:00:40 +00:00
/ * \
* Paper . gradient
[ method ]
* *
* Creates a gradient element .
* *
- gradient ( string ) gradient descriptor
> Gradient Descriptor
* Gradient descriptor consists of ` <type>(<coords>)<colors> ` . Type
* could be linear or radial , which presented as letter “ L ” or “ R ” . Any
* type could be absolute or relative , absolute gradient take it coords
* relative to the SVG surface , while relative takes them relative to
* the bounding box of the element it applied to . For absolute
* coordinates you specify type as an upper case letter ( “ L ” or “ R ” ) .
* For relative use low case letter ( “ l ” or “ r ” ) . Coordinates specify
* vector of gradient for linear as x1 , y1 , x2 , y2 . For radial as cx ,
* cy , r and optional fx , fy . Colors are list of dash separated colors .
* Optionally color could have offset after colon .
> Example
| var g = paper . gradient ( "l(0, 0, 1, 1)#000-#f00-#fff" ) ;
* Linear gradient , relative from top - left corner to bottom - right
* corner , from black through red to white .
| var g = paper . gradient ( "L(0, 0, 100, 100)#000-#f00:25%-#fff" ) ;
* Linear gradient , absolute from ( 0 , 0 ) to ( 100 , 100 ) , from black
* through red at 25 % to white .
| var g = paper . gradient ( "r(0.5, 0.5, 0.5)#000-#fff" ) ;
* Radial gradient , relative from the center of the element with radius
* 0.5 of the width , from black to white .
| paper . circle ( 50 , 50 , 40 ) . attr ( {
| fill : g
| } ) ;
= ( object ) Element object with type “ gradient ”
\ * /
2013-08-05 08:04:30 +00:00
proto . gradient = function ( str ) {
var grad = arrayFirstValue ( eve ( "savage.util.grad.parse" , null , str ) ) ,
el ;
2013-09-11 12:25:30 +00:00
if ( ! grad ) {
return null ;
}
2013-08-05 08:04:30 +00:00
if ( grad . type . toLowerCase ( ) == "l" ) {
el = this . gradientLinear . apply ( this , grad . params ) ;
} else {
el = this . gradientRadial . apply ( this , grad . params ) ;
}
if ( grad . type != grad . type . toLowerCase ( ) ) {
$ ( el . node , {
gradientUnits : "userSpaceOnUse"
} ) ;
}
var stops = grad . stops ,
len = stops . length ,
start = 0 ,
j = 0 ;
function seed ( i , end ) {
var step = ( end - start ) / ( i - j ) ;
for ( var k = j ; k < i ; k ++ ) {
stops [ k ] . offset = + ( + start + step * ( k - j ) ) . toFixed ( 2 ) ;
}
j = i ;
start = end ;
}
len -- ;
for ( var i = 0 ; i < len ; i ++ ) if ( "offset" in stops [ i ] ) {
seed ( i , stops [ i ] . offset ) ;
}
stops [ len ] . offset = stops [ len ] . offset || 100 ;
seed ( len , stops [ len ] . offset ) ;
for ( i = 0 ; i <= len ; i ++ ) {
var stop = stops [ i ] ;
el . addStop ( stop . color , stop . offset ) ;
}
return el ;
} ;
function stops ( ) {
return this . selectAll ( "stop" ) ;
}
function addStop ( color , offset ) {
var stop = $ ( "stop" ) ;
$ ( stop , {
"stop-color" : color ,
offset : + offset + "%"
} ) ;
this . node . appendChild ( stop ) ;
return this ;
}
function getBBox ( ) {
if ( this . type == "linearGradient" ) {
var x1 = $ ( this . node , "x1" ) || 0 ,
x2 = $ ( this . node , "x2" ) || 1 ,
y1 = $ ( this . node , "y1" ) || 0 ,
y2 = $ ( this . node , "y2" ) || 0 ;
return Savage . _ . box ( x1 , y1 , math . abs ( x2 - x1 ) , math . abs ( y2 - y1 ) ) ;
} else {
var cx = this . node . cx || . 5 ,
cy = this . node . cy || . 5 ,
r = this . node . r || 0 ;
return Savage . _ . box ( cx - r , cy - r , r * 2 , r * 2 ) ;
}
}
proto . gradientLinear = function ( x1 , y1 , x2 , y2 ) {
var el = make ( "linearGradient" , this . node ) ;
el . stops = stops ;
el . addStop = addStop ;
el . getBBox = getBBox ;
if ( x1 != null ) {
$ ( el . node , {
x1 : x1 ,
y1 : y1 ,
x2 : x2 ,
y2 : y2
} ) ;
}
return el ;
} ;
proto . gradientRadial = function ( cx , cy , r , fx , fy ) {
var el = make ( "radialGradient" , this . node ) ;
el . stops = stops ;
el . addStop = addStop ;
el . getBBox = getBBox ;
if ( cx != null ) {
$ ( el . node , {
cx : cx ,
cy : cy ,
r : r
} ) ;
}
if ( fx != null && fy != null ) {
$ ( el . node , {
fx : fx ,
fy : fy
} ) ;
}
return el ;
} ;
2013-09-05 00:10:45 +00:00
/ * \
* Paper . toString
[ method ]
* *
* Returns SVG code of the @ Paper .
= ( string ) SVG code of the @ Paper .
\ * /
proto . toString = function ( ) {
var f = glob . doc . createDocumentFragment ( ) ,
d = glob . doc . createElement ( "div" ) ,
svg = this . node . cloneNode ( true ) ,
res ;
f . appendChild ( d ) ;
d . appendChild ( svg ) ;
$ ( svg , { xmlns : "http://www.w3.org/2000/svg" } ) ;
res = d . innerHTML ;
f . removeChild ( f . firstChild ) ;
return res ;
} ;
2013-08-05 08:04:30 +00:00
} ( ) ) ;
} ( Paper . prototype ) ) ;
2013-09-02 04:26:51 +00:00
// simple ajax
/ * \
* Savage . 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 ) XMLHttpRequest ( just in case )
\ * /
Savage . ajax = function ( url , postData , callback , scope ) {
var req = new XMLHttpRequest ,
id = ID ( ) ;
if ( req ) {
if ( is ( postData , "function" ) ) {
scope = callback ;
callback = postData ;
postData = null ;
} else if ( is ( postData , "object" ) ) {
var pd = [ ] ;
for ( var key in postData ) if ( postData . hasOwnProperty ( key ) ) {
pd . push ( encodeURIComponent ( key ) + "=" + encodeURIComponent ( postData [ key ] ) ) ;
}
postData = pd . join ( "&" ) ;
}
req . open ( ( postData ? "POST" : "GET" ) , url , true ) ;
req . setRequestHeader ( "X-Requested-With" , "XMLHttpRequest" ) ;
if ( postData ) {
req . setRequestHeader ( "Content-type" , "application/x-www-form-urlencoded" ) ;
}
if ( callback ) {
eve . once ( "savage.ajax." + id + ".0" , callback ) ;
eve . once ( "savage.ajax." + id + ".200" , callback ) ;
eve . once ( "savage.ajax." + id + ".304" , callback ) ;
}
req . onreadystatechange = function ( ) {
if ( req . readyState != 4 ) return ;
eve ( "savage.ajax." + id + "." + req . status , scope , req ) ;
} ;
if ( req . readyState == 4 ) {
return req ;
}
req . send ( postData ) ;
return req ;
}
} ;
2013-09-05 00:10:45 +00:00
/ * \
* Savage . load
[ method ]
* *
* Loads external SVG file as a @ Fragment . For more advanced AJAX see @ Savage . ajax .
* *
- url ( string ) URL
- callback ( function ) callback
- scope ( object ) # optional scope of callback
\ * /
Savage . load = function ( url , callback , scope ) {
Savage . ajax ( url , function ( req ) {
var f = Savage . parse ( req . responseText ) ;
scope ? callback . call ( scope , f ) : callback ( f ) ;
} ) ;
} ;
2013-09-02 04:26:51 +00:00
2013-08-05 08:04:30 +00:00
// Attributes event handlers
eve . on ( "savage.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 ;
this . paper . defs . appendChild ( value ) ;
value = wrap ( value ) ;
}
if ( value . type == "mask" ) {
var mask = value ;
} else {
mask = make ( "mask" , this . paper . defs ) ;
mask . node . appendChild ( value . node ) ;
! mask . node . id && $ ( mask . node , {
id : mask . id
} ) ;
}
$ ( this . node , {
mask : "url(#" + mask . id + ")"
} ) ;
}
} ) ;
( function ( clipIt ) {
eve . on ( "savage.util.attr.clip" , clipIt ) ;
eve . on ( "savage.util.attr.clip-path" , clipIt ) ;
eve . on ( "savage.util.attr.clipPath" , clipIt ) ;
} ( function ( value ) {
if ( value instanceof Element || value instanceof Fragment ) {
eve . stop ( ) ;
if ( value . type == "clipPath" ) {
var clip = value ;
} else {
clip = make ( "clipPath" , this . paper . defs ) ;
clip . node . appendChild ( value . node ) ;
! clip . node . id && $ ( clip . node , {
id : clip . id
} ) ;
}
$ ( this . node , {
"clip-path" : "url(#" + clip . id + ")"
} ) ;
}
} ) ) ;
2013-09-13 12:51:01 +00:00
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 ;
this . paper . defs . appendChild ( value ) ;
value = wrap ( value ) ;
2013-09-11 12:25:30 +00:00
}
2013-09-13 12:51:01 +00:00
if ( value instanceof Element ) {
if ( value . type == "radialGradient" || value . type == "linearGradient"
|| value . type == "pattern" ) {
if ( ! value . node . id ) {
$ ( value . node , {
id : value . id
2013-09-11 12:25:30 +00:00
} ) ;
}
2013-09-13 12:51:01 +00:00
var fill = "url(#" + value . node . id + ")" ;
2013-09-11 12:25:30 +00:00
} else {
2013-09-13 12:51:01 +00:00
fill = value . attr ( name ) ;
2013-08-05 08:04:30 +00:00
}
} else {
2013-09-13 12:51:01 +00:00
fill = Savage . color ( value ) ;
if ( fill . error ) {
var grad = this . paper . 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 ) ;
}
2013-08-05 08:04:30 +00:00
}
2013-09-13 12:51:01 +00:00
var attrs = { } ;
attrs [ name ] = fill ;
$ ( this . node , attrs ) ;
this . node . style [ name ] = E ;
} ;
}
eve . on ( "savage.util.attr.fill" , fillStroke ( "fill" ) ) ;
eve . on ( "savage.util.attr.stroke" , fillStroke ( "stroke" ) ) ;
2013-08-05 08:04:30 +00:00
var gradrg = /^([lr])(?:\(([^)]*)\))?(.*)$/i ;
eve . on ( "savage.util.grad.parse" , function parseGrad ( string ) {
string = Str ( string ) ;
2013-09-11 12:25:30 +00:00
var tokens = string . match ( gradrg ) ;
if ( ! tokens ) {
return null ;
}
var type = tokens [ 1 ] ,
2013-08-05 08:04:30 +00:00
params = tokens [ 2 ] ,
stops = tokens [ 3 ] ;
params = params . split ( /\s*,\s*/ ) . map ( function ( el ) {
return + el == el ? + el : el ;
} ) ;
if ( params . length == 1 && params [ 0 ] == 0 ) {
params = [ ] ;
}
stops = stops . split ( "-" ) ;
stops = stops . map ( function ( el ) {
el = el . split ( ":" ) ;
var out = {
color : el [ 0 ]
} ;
if ( el [ 1 ] ) {
out . offset = el [ 1 ] ;
}
return out ;
} ) ;
return {
type : type ,
params : params ,
stops : stops
} ;
} ) ;
eve . on ( "savage.util.attr.d" , function ( value ) {
eve . stop ( ) ;
if ( is ( value , "array" ) && is ( value [ 0 ] , "array" ) ) {
value = Savage . path . toString . call ( value ) ;
}
value = Str ( value ) ;
if ( value . match ( /[ruo]/i ) ) {
value = Savage . path . toAbsolute ( value ) ;
}
$ ( this . node , { d : value } ) ;
} ) ( - 1 ) ;
2013-09-05 00:10:45 +00:00
eve . on ( "savage.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 ) ;
2013-08-05 08:04:30 +00:00
eve . on ( "savage.util.attr.path" , function ( value ) {
eve . stop ( ) ;
this . attr ( { d : value } ) ;
} ) ( - 1 ) ;
eve . on ( "savage.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 ( "savage.util.attr.transform" , function ( value ) {
this . transform ( value ) ;
eve . stop ( ) ;
} ) ( - 1 ) ;
eve . on ( "savage.util.attr.r" , function ( value ) {
if ( this . type == "rect" ) {
eve . stop ( ) ;
$ ( this . node , {
rx : value ,
ry : value
} ) ;
}
} ) ( - 1 ) ;
eve . on ( "savage.util.attr.text" , function ( value ) {
if ( this . type == "text" ) {
var i = 0 ,
2013-09-09 07:22:14 +00:00
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 ) ) ;
2013-08-05 08:04:30 +00:00
}
2013-09-09 07:22:14 +00:00
out . normalize && out . normalize ( ) ;
return out ;
} ;
2013-08-05 08:04:30 +00:00
while ( node . firstChild ) {
node . removeChild ( node . firstChild ) ;
}
2013-09-09 07:22:14 +00:00
var tuned = tuner ( value ) ;
while ( tuned . firstChild ) {
node . appendChild ( tuned . firstChild ) ;
}
2013-08-05 08:04:30 +00:00
}
eve . stop ( ) ;
} ) ( - 1 ) ;
// default
var availableAttributes = {
rect : {
x : 0 ,
y : 0 ,
width : 0 ,
height : 0 ,
rx : 0 ,
ry : 0 ,
"class" : 0
} ,
circle : {
cx : 0 ,
cy : 0 ,
r : 0 ,
"class" : 0
} ,
ellipse : {
cx : 0 ,
cy : 0 ,
rx : 0 ,
ry : 0 ,
"class" : 0
} ,
line : {
x1 : 0 ,
y1 : 0 ,
x2 : 0 ,
y2 : 0 ,
"class" : 0
} ,
polyline : {
points : "" ,
"class" : 0
} ,
polygon : {
points : "" ,
"class" : 0
} ,
text : {
x : 0 ,
y : 0 ,
dx : 0 ,
dy : 0 ,
rotate : 0 ,
textLength : 0 ,
lengthAdjust : 0 ,
"class" : 0
} ,
tspan : {
x : 0 ,
y : 0 ,
dx : 0 ,
dy : 0 ,
rotate : 0 ,
textLength : 0 ,
lengthAdjust : 0 ,
"class" : 0
} ,
textPath : {
"xlink:href" : 0 ,
startOffset : 0 ,
method : 0 ,
spacing : 0 ,
"class" : 0
} ,
marker : {
viewBox : 0 ,
preserveAspectRatio : 0 ,
refX : 0 ,
refY : 0 ,
markerUnits : 0 ,
markerWidth : 0 ,
markerHeight : 0 ,
orient : 0 ,
"class" : 0
} ,
2013-09-06 03:40:22 +00:00
use : {
"class" : 0 ,
externalResourcesRequired : 0 ,
x : 0 ,
y : 0 ,
width : 0 ,
height : 0 ,
"xlink:href" : 0
} ,
2013-08-05 08:04:30 +00:00
linearGradient : {
x1 : 0 ,
y1 : 0 ,
x2 : 0 ,
y2 : 0 ,
gradientUnits : 0 ,
gradientTransform : 0 ,
spreadMethod : 0 ,
"xlink:href" : 0 ,
"class" : 0
} ,
radialGradient : {
cx : 0 ,
cy : 0 ,
r : 0 ,
fx : 0 ,
fy : 0 ,
gradientUnits : 0 ,
gradientTransform : 0 ,
spreadMethod : 0 ,
"xlink:href" : 0 ,
"class" : 0
} ,
stop : {
offset : 0 ,
"class" : 0
} ,
pattern : {
viewBox : 0 ,
preserveAspectRatio : 0 ,
x : 0 ,
y : 0 ,
width : 0 ,
height : 0 ,
patternUnits : 0 ,
patternContentUnits : 0 ,
patternTransform : 0 ,
"xlink:href" : 0 ,
"class" : 0
} ,
clipPath : {
transform : 0 ,
clipPathUnits : 0 ,
"class" : 0
} ,
mask : {
x : 0 ,
y : 0 ,
width : 0 ,
height : 0 ,
maskUnits : 0 ,
maskContentUnits : 0 ,
"class" : 0
} ,
image : {
preserveAspectRatio : 0 ,
transform : 0 ,
x : 0 ,
y : 0 ,
width : 0 ,
height : 0 ,
"xlink:href" : 0 ,
"class" : 0
} ,
path : {
d : "" ,
"class" : 0
2013-09-02 04:26:51 +00:00
} ,
2013-09-12 01:08:19 +00:00
g : {
"class" : 0
} ,
2013-09-02 04:26:51 +00:00
feDistantLight : {
azimuth : 0 ,
elevation : 0
} ,
fePointLight : {
x : 0 ,
y : 0 ,
z : 0
} ,
feSpotLight : {
x : 0 ,
y : 0 ,
z : 0 ,
pointsAtX : 0 ,
pointsAtY : 0 ,
pointsAtZ : 0 ,
specularExponent : 0 ,
limitingConeAngle : 0
} ,
feBlend : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
in2 : 0 ,
mode : 0
} ,
feColorMatrix : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
type : 0 ,
values : 0
} ,
feComponentTransfer : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0
} ,
feComposite : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
in2 : 0 ,
operator : 0 ,
k1 : 0 ,
k2 : 0 ,
k3 : 0 ,
k4 : 0
} ,
feConvolveMatrix : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
order : 0 ,
kernelMatrix : 0 ,
divisor : 0 ,
bias : 0 ,
targetX : 0 ,
targetY : 0 ,
edgeMode : 0 ,
kernelUnitLength : 0 ,
preserveAlpha : 0
} ,
feDiffuseLighting : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
surfaceScale : 0 ,
diffuseConstant : 0 ,
kernelUnitLength : 0
} ,
feDisplacementMap : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
in2 : 0 ,
scale : 0 ,
xChannelSelector : 0 ,
yChannelSelector : 0
} ,
feFlood : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"flood-color" : 0 ,
"flood-opacity" : 0
} ,
feGaussianBlur : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
stdDeviation : 0
} ,
feImage : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
externalResourcesRequired : 0 ,
preserveAspectRatio : 0 ,
"xlink:href" : 0
} ,
feMerge : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0
} ,
feMergeNode : {
"in" : 0
} ,
feMorphology : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
operator : 0 ,
radius : 0
} ,
feOffset : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
dx : 0 ,
dy : 0
} ,
feSpecularLighting : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0 ,
surfaceScale : 0 ,
specularConstant : 0 ,
specularExponent : 0 ,
kernelUnitLength : 0
} ,
feTile : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
"in" : 0
} ,
feTurbulence : {
height : 0 ,
result : 0 ,
width : 0 ,
x : 0 ,
y : 0 ,
"class" : 0 ,
style : 0 ,
baseFrequency : 0 ,
numOctaves : 0 ,
seed : 0 ,
stitchTiles : 0 ,
type : 0
2013-08-05 08:04:30 +00:00
}
} ;
2013-09-02 04:26:51 +00:00
availableAttributes . feFuncR = availableAttributes . feFuncG = availableAttributes . feFuncB = availableAttributes . feFuncA = {
type : 0 ,
tableValues : 0 ,
slope : 0 ,
intercept : 0 ,
amplitude : 0 ,
exponent : 0 ,
offset : 0
} ;
2013-08-05 08:04:30 +00:00
eve . on ( "savage.util.attr" , function ( value ) {
var att = eve . nt ( ) ;
att = att . substring ( att . lastIndexOf ( "." ) + 1 ) ;
var style = att . replace ( /-(\w)/gi , function ( all , letter ) {
return letter . toUpperCase ( ) ;
} ) ;
if ( availableAttributes [ has ] ( this . type ) && availableAttributes [ this . type ] [ has ] ( att ) ) {
value == null ? this . node . removeAttribute ( att ) : this . node . setAttribute ( att , value ) ;
} else {
this . node . style [ style ] = value == null ? E : value ;
}
} ) ;
eve . on ( "savage.util.getattr.transform" , function ( ) {
eve . stop ( ) ;
return this . transform ( ) ;
} ) ( - 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 Savage ( 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 ( "savage.util.getattr.marker-end" , getter ( "end" ) ) ( - 1 ) ;
eve . on ( "savage.util.getattr.markerEnd" , getter ( "end" ) ) ( - 1 ) ;
eve . on ( "savage.util.getattr.marker-start" , getter ( "start" ) ) ( - 1 ) ;
eve . on ( "savage.util.getattr.markerStart" , getter ( "start" ) ) ( - 1 ) ;
eve . on ( "savage.util.getattr.marker-mid" , getter ( "mid" ) ) ( - 1 ) ;
eve . on ( "savage.util.getattr.markerMid" , getter ( "mid" ) ) ( - 1 ) ;
eve . on ( "savage.util.attr.marker-end" , setter ( "end" ) ) ( - 1 ) ;
eve . on ( "savage.util.attr.markerEnd" , setter ( "end" ) ) ( - 1 ) ;
eve . on ( "savage.util.attr.marker-start" , setter ( "start" ) ) ( - 1 ) ;
eve . on ( "savage.util.attr.markerStart" , setter ( "start" ) ) ( - 1 ) ;
eve . on ( "savage.util.attr.marker-mid" , setter ( "mid" ) ) ( - 1 ) ;
eve . on ( "savage.util.attr.markerMid" , setter ( "mid" ) ) ( - 1 ) ;
} ( ) ) ;
eve . on ( "savage.util.getattr.r" , function ( ) {
if ( this . type == "rect" && $ ( this . node , "rx" ) == $ ( this . node , "ry" ) ) {
eve . stop ( ) ;
return $ ( this . node , "rx" ) ;
}
} ) ( - 1 ) ;
2013-09-09 07:22:14 +00:00
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 ( "savage.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 ) ;
2013-09-05 00:10:45 +00:00
eve . on ( "savage.util.getattr.#text" , function ( ) {
return this . node . textContent ;
} ) ( - 1 ) ;
2013-08-05 08:04:30 +00:00
eve . on ( "savage.util.getattr.viewBox" , function ( ) {
eve . stop ( ) ;
var vb = $ ( this . node , "viewBox" ) . split ( separator ) ;
return Savage . _ . box ( + vb [ 0 ] , + vb [ 1 ] , + vb [ 2 ] , + vb [ 3 ] ) ;
// TODO: investigate why I need to z-index it
} ) ( - 1 ) ;
eve . on ( "savage.util.getattr.points" , function ( ) {
var p = $ ( this . node , "points" ) ;
eve . stop ( ) ;
return p . split ( separator ) ;
} ) ;
eve . on ( "savage.util.getattr.path" , function ( ) {
var p = $ ( this . node , "d" ) ;
eve . stop ( ) ;
return p ;
} ) ;
// default
eve . on ( "savage.util.getattr" , function ( ) {
var att = eve . nt ( ) ;
att = att . substring ( att . lastIndexOf ( "." ) + 1 ) ;
var style = att . replace ( /-(\w)/gi , function ( all , letter ) {
return letter . toUpperCase ( ) ;
} ) ;
if ( availableAttributes [ has ] ( this . type ) && availableAttributes [ this . type ] [ has ] ( att ) ) {
return this . node . getAttribute ( att ) ;
} else {
return glob . doc . defaultView . getComputedStyle ( this . node , null ) . getPropertyValue ( style ) ;
}
} ) ;
Savage . plugin = function ( f ) {
f ( Savage , Element , Paper , glob ) ;
} ;
return Savage ;
} ( ) ) ;
2013-08-15 08:29:47 +00:00
// 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.
2013-08-05 08:04:30 +00:00
Savage . plugin ( function ( Savage , Element , Paper , glob ) {
var elproto = Element . prototype ,
is = Savage . is ,
clone = Savage . _ . clone ,
has = "hasOwnProperty" ,
p2s = /,?([a-z]),?/gi ,
toFloat = parseFloat ,
math = Math ,
PI = math . PI ,
mmin = math . min ,
mmax = math . max ,
pow = math . pow ,
abs = math . abs ;
function paths ( ps ) {
var p = paths . ps = paths . ps || { } ;
if ( p [ ps ] ) {
p [ ps ] . sleep = 100 ;
} else {
p [ ps ] = {
sleep : 100
} ;
}
setTimeout ( function ( ) {
for ( var key in p ) if ( p [ has ] ( key ) && key != ps ) {
p [ key ] . sleep -- ;
! p [ key ] . sleep && delete p [ key ] ;
}
} ) ;
return p [ ps ] ;
}
function box ( x , y , width , height ) {
if ( x == null ) {
x = y = width = height = 0 ;
}
if ( y == null ) {
y = x . y ;
width = x . width ;
height = x . height ;
x = x . x ;
}
return {
x : x ,
y : y ,
width : width ,
w : width ,
height : height ,
h : height ,
x2 : x + width ,
y2 : y + height ,
cx : x + width / 2 ,
cy : y + height / 2 ,
r1 : math . min ( width , height ) / 2 ,
r2 : math . max ( width , height ) / 2 ,
r0 : math . sqrt ( width * width + height * height ) / 2 ,
path : rectPath ( x , y , width , height ) ,
vb : [ x , y , width , height ] . join ( " " )
} ;
}
function toString ( ) {
return this . join ( "," ) . replace ( p2s , "$1" ) ;
}
function pathClone ( pathArray ) {
var res = clone ( pathArray ) ;
res . toString = toString ;
return res ;
}
function getPointAtSegmentLength ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , length ) {
if ( length == null ) {
return bezlen ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y ) ;
} else {
return findDotsAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y ,
getTotLen ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , length ) ) ;
}
}
function getLengthFactory ( istotal , subpath ) {
function O ( val ) {
return + ( + val ) . toFixed ( 3 ) ;
}
return function ( path , length , onlystart ) {
2013-09-05 13:24:16 +00:00
if ( path instanceof Element ) {
path = path . attr ( "d" ) ;
}
2013-08-05 08:04:30 +00:00
path = path2curve ( path ) ;
var x , y , p , l , sp = "" , subpaths = { } , point ,
len = 0 ;
for ( var i = 0 , ii = path . length ; i < ii ; i ++ ) {
p = path [ i ] ;
if ( p [ 0 ] == "M" ) {
x = + p [ 1 ] ;
y = + p [ 2 ] ;
} else {
l = getPointAtSegmentLength ( x , y , p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , p [ 5 ] , p [ 6 ] ) ;
if ( len + l > length ) {
if ( subpath && ! subpaths . start ) {
point = getPointAtSegmentLength ( x , y , p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , p [ 5 ] , p [ 6 ] , length - len ) ;
sp += [
"C" + O ( point . start . x ) ,
O ( point . start . y ) ,
O ( point . m . x ) ,
O ( point . m . y ) ,
O ( point . x ) ,
O ( point . y )
] ;
if ( onlystart ) { return sp ; }
subpaths . start = sp ;
sp = [
"M" + O ( point . x ) ,
O ( point . y ) + "C" + O ( point . n . x ) ,
O ( point . n . y ) ,
O ( point . end . x ) ,
O ( point . end . y ) ,
O ( p [ 5 ] ) ,
O ( p [ 6 ] )
] . join ( ) ;
len += l ;
x = + p [ 5 ] ;
y = + p [ 6 ] ;
continue ;
}
if ( ! istotal && ! subpath ) {
point = getPointAtSegmentLength ( x , y , p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , p [ 5 ] , p [ 6 ] , length - len ) ;
return point ;
}
}
len += l ;
x = + p [ 5 ] ;
y = + p [ 6 ] ;
}
sp += p . shift ( ) + p ;
}
subpaths . end = sp ;
point = istotal ? len : subpath ? subpaths : findDotsAtSegment ( x , y , p [ 0 ] , p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , p [ 5 ] , 1 ) ;
return point ;
} ;
}
var getTotalLength = getLengthFactory ( 1 ) ,
getPointAtLength = getLengthFactory ( ) ,
getSubpathsAtLength = getLengthFactory ( 0 , 1 ) ;
function findDotsAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , t ) {
var t1 = 1 - t ,
t13 = pow ( t1 , 3 ) ,
t12 = pow ( t1 , 2 ) ,
t2 = t * t ,
t3 = t2 * t ,
x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x ,
y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y ,
mx = p1x + 2 * t * ( c1x - p1x ) + t2 * ( c2x - 2 * c1x + p1x ) ,
my = p1y + 2 * t * ( c1y - p1y ) + t2 * ( c2y - 2 * c1y + p1y ) ,
nx = c1x + 2 * t * ( c2x - c1x ) + t2 * ( p2x - 2 * c2x + c1x ) ,
ny = c1y + 2 * t * ( c2y - c1y ) + t2 * ( p2y - 2 * c2y + c1y ) ,
ax = t1 * p1x + t * c1x ,
ay = t1 * p1y + t * c1y ,
cx = t1 * c2x + t * p2x ,
cy = t1 * c2y + t * p2y ,
alpha = ( 90 - math . atan2 ( mx - nx , my - ny ) * 180 / PI ) ;
// (mx > nx || my < ny) && (alpha += 180);
return {
x : x ,
y : y ,
m : { x : mx , y : my } ,
n : { x : nx , y : ny } ,
start : { x : ax , y : ay } ,
end : { x : cx , y : cy } ,
alpha : alpha
} ;
}
function bezierBBox ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y ) {
if ( ! Savage . is ( p1x , "array" ) ) {
p1x = [ p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y ] ;
}
var bbox = curveDim . apply ( null , p1x ) ;
return box (
bbox . min . x ,
bbox . min . y ,
bbox . max . x - bbox . min . x ,
bbox . max . y - bbox . min . y
) ;
}
function isPointInsideBBox ( bbox , x , y ) {
return x >= bbox . x &&
x <= bbox . x + bbox . width &&
y >= bbox . y &&
y <= bbox . y + bbox . height ;
}
function isBBoxIntersect ( bbox1 , bbox2 ) {
bbox1 = box ( bbox1 ) ;
bbox2 = box ( bbox2 ) ;
return isPointInsideBBox ( bbox2 , bbox1 . x , bbox1 . y )
|| isPointInsideBBox ( bbox2 , bbox1 . x2 , bbox1 . y )
|| isPointInsideBBox ( bbox2 , bbox1 . x , bbox1 . y2 )
|| isPointInsideBBox ( bbox2 , bbox1 . x2 , bbox1 . y2 )
|| isPointInsideBBox ( bbox1 , bbox2 . x , bbox2 . y )
|| isPointInsideBBox ( bbox1 , bbox2 . x2 , bbox2 . y )
|| isPointInsideBBox ( bbox1 , bbox2 . x , bbox2 . y2 )
|| isPointInsideBBox ( bbox1 , bbox2 . x2 , bbox2 . y2 )
|| ( bbox1 . x < bbox2 . x2 && bbox1 . x > bbox2 . x
|| bbox2 . x < bbox1 . x2 && bbox2 . x > bbox1 . x )
&& ( bbox1 . y < bbox2 . y2 && bbox1 . y > bbox2 . y
|| bbox2 . y < bbox1 . y2 && bbox2 . y > bbox1 . y ) ;
}
function base3 ( t , p1 , p2 , p3 , p4 ) {
var t1 = - 3 * p1 + 9 * p2 - 9 * p3 + 3 * p4 ,
t2 = t * t1 + 6 * p1 - 12 * p2 + 6 * p3 ;
return t * t2 - 3 * p1 + 3 * p2 ;
}
function bezlen ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 , z ) {
if ( z == null ) {
z = 1 ;
}
z = z > 1 ? 1 : z < 0 ? 0 : z ;
var z2 = z / 2 ,
n = 12 ,
2013-08-09 12:13:22 +00:00
Tvalues = [ - . 1252 , . 1252 , - . 3678 , . 3678 , - . 5873 , . 5873 , - . 7699 , . 7699 , - . 9041 , . 9041 , - . 9816 , . 9816 ] ,
2013-08-05 08:04:30 +00:00
Cvalues = [ 0.2491 , 0.2491 , 0.2335 , 0.2335 , 0.2032 , 0.2032 , 0.1601 , 0.1601 , 0.1069 , 0.1069 , 0.0472 , 0.0472 ] ,
sum = 0 ;
for ( var i = 0 ; i < n ; i ++ ) {
var ct = z2 * Tvalues [ i ] + z2 ,
xbase = base3 ( ct , x1 , x2 , x3 , x4 ) ,
ybase = base3 ( ct , y1 , y2 , y3 , y4 ) ,
comb = xbase * xbase + ybase * ybase ;
sum += Cvalues [ i ] * math . sqrt ( comb ) ;
}
return z2 * sum ;
}
function getTotLen ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 , ll ) {
if ( ll < 0 || bezlen ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 ) < ll ) {
return ;
}
var t = 1 ,
step = t / 2 ,
t2 = t - step ,
l ,
e = . 01 ;
l = bezlen ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 , t2 ) ;
while ( abs ( l - ll ) > e ) {
step /= 2 ;
t2 += ( l < ll ? 1 : - 1 ) * step ;
l = bezlen ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 , t2 ) ;
}
return t2 ;
}
function intersect ( x1 , y1 , x2 , y2 , x3 , y3 , x4 , y4 ) {
if (
mmax ( x1 , x2 ) < mmin ( x3 , x4 ) ||
mmin ( x1 , x2 ) > mmax ( x3 , x4 ) ||
mmax ( y1 , y2 ) < mmin ( y3 , y4 ) ||
mmin ( y1 , y2 ) > mmax ( y3 , y4 )
) {
return ;
}
var nx = ( x1 * y2 - y1 * x2 ) * ( x3 - x4 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ,
ny = ( x1 * y2 - y1 * x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ,
denominator = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 ) ;
if ( ! denominator ) {
return ;
}
var px = nx / denominator ,
py = ny / denominator ,
px2 = + px . toFixed ( 2 ) ,
py2 = + py . toFixed ( 2 ) ;
if (
px2 < + mmin ( x1 , x2 ) . toFixed ( 2 ) ||
px2 > + mmax ( x1 , x2 ) . toFixed ( 2 ) ||
px2 < + mmin ( x3 , x4 ) . toFixed ( 2 ) ||
px2 > + mmax ( x3 , x4 ) . toFixed ( 2 ) ||
py2 < + mmin ( y1 , y2 ) . toFixed ( 2 ) ||
py2 > + mmax ( y1 , y2 ) . toFixed ( 2 ) ||
py2 < + mmin ( y3 , y4 ) . toFixed ( 2 ) ||
py2 > + mmax ( y3 , y4 ) . toFixed ( 2 )
) {
return ;
}
return { x : px , y : py } ;
}
function inter ( bez1 , bez2 ) {
return interHelper ( bez1 , bez2 ) ;
}
function interCount ( bez1 , bez2 ) {
return interHelper ( bez1 , bez2 , 1 ) ;
}
function interHelper ( bez1 , bez2 , justCount ) {
var bbox1 = bezierBBox ( bez1 ) ,
bbox2 = bezierBBox ( bez2 ) ;
if ( ! isBBoxIntersect ( bbox1 , bbox2 ) ) {
return justCount ? 0 : [ ] ;
}
var l1 = bezlen . apply ( 0 , bez1 ) ,
l2 = bezlen . apply ( 0 , bez2 ) ,
n1 = ~ ~ ( l1 / 5 ) ,
n2 = ~ ~ ( l2 / 5 ) ,
dots1 = [ ] ,
dots2 = [ ] ,
xy = { } ,
res = justCount ? 0 : [ ] ;
for ( var i = 0 ; i < n1 + 1 ; i ++ ) {
var p = findDotsAtSegment . apply ( 0 , bez1 . concat ( i / n1 ) ) ;
dots1 . push ( { x : p . x , y : p . y , t : i / n1 } ) ;
}
for ( i = 0 ; i < n2 + 1 ; i ++ ) {
p = findDotsAtSegment . apply ( 0 , bez2 . concat ( i / n2 ) ) ;
dots2 . push ( { x : p . x , y : p . y , t : i / n2 } ) ;
}
for ( i = 0 ; i < n1 ; i ++ ) {
for ( var j = 0 ; j < n2 ; j ++ ) {
var di = dots1 [ i ] ,
di1 = dots1 [ i + 1 ] ,
dj = dots2 [ j ] ,
dj1 = dots2 [ j + 1 ] ,
ci = abs ( di1 . x - di . x ) < . 001 ? "y" : "x" ,
cj = abs ( dj1 . x - dj . x ) < . 001 ? "y" : "x" ,
is = intersect ( di . x , di . y , di1 . x , di1 . y , dj . x , dj . y , dj1 . x , dj1 . y ) ;
if ( is ) {
if ( xy [ is . x . toFixed ( 4 ) ] == is . y . toFixed ( 4 ) ) {
continue ;
}
xy [ is . x . toFixed ( 4 ) ] = is . y . toFixed ( 4 ) ;
var t1 = di . t + abs ( ( is [ ci ] - di [ ci ] ) / ( di1 [ ci ] - di [ ci ] ) ) * ( di1 . t - di . t ) ,
t2 = dj . t + abs ( ( is [ cj ] - dj [ cj ] ) / ( dj1 [ cj ] - dj [ cj ] ) ) * ( dj1 . t - dj . t ) ;
if ( t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1 ) {
if ( justCount ) {
res ++ ;
} else {
res . push ( {
x : is . x ,
y : is . y ,
t1 : t1 ,
t2 : t2
} ) ;
}
}
}
}
}
return res ;
}
function pathIntersection ( path1 , path2 ) {
return interPathHelper ( path1 , path2 ) ;
}
function pathIntersectionNumber ( path1 , path2 ) {
return interPathHelper ( path1 , path2 , 1 ) ;
}
function interPathHelper ( path1 , path2 , justCount ) {
path1 = path2curve ( path1 ) ;
path2 = path2curve ( path2 ) ;
var x1 , y1 , x2 , y2 , x1m , y1m , x2m , y2m , bez1 , bez2 ,
res = justCount ? 0 : [ ] ;
for ( var i = 0 , ii = path1 . length ; i < ii ; i ++ ) {
var pi = path1 [ i ] ;
if ( pi [ 0 ] == "M" ) {
x1 = x1m = pi [ 1 ] ;
y1 = y1m = pi [ 2 ] ;
} else {
if ( pi [ 0 ] == "C" ) {
bez1 = [ x1 , y1 ] . concat ( pi . slice ( 1 ) ) ;
x1 = bez1 [ 6 ] ;
y1 = bez1 [ 7 ] ;
} else {
bez1 = [ x1 , y1 , x1 , y1 , x1m , y1m , x1m , y1m ] ;
x1 = x1m ;
y1 = y1m ;
}
for ( var j = 0 , jj = path2 . length ; j < jj ; j ++ ) {
var pj = path2 [ j ] ;
if ( pj [ 0 ] == "M" ) {
x2 = x2m = pj [ 1 ] ;
y2 = y2m = pj [ 2 ] ;
} else {
if ( pj [ 0 ] == "C" ) {
bez2 = [ x2 , y2 ] . concat ( pj . slice ( 1 ) ) ;
x2 = bez2 [ 6 ] ;
y2 = bez2 [ 7 ] ;
} else {
bez2 = [ x2 , y2 , x2 , y2 , x2m , y2m , x2m , y2m ] ;
x2 = x2m ;
y2 = y2m ;
}
var intr = interHelper ( bez1 , bez2 , justCount ) ;
if ( justCount ) {
res += intr ;
} else {
for ( var k = 0 , kk = intr . length ; k < kk ; k ++ ) {
intr [ k ] . segment1 = i ;
intr [ k ] . segment2 = j ;
intr [ k ] . bez1 = bez1 ;
intr [ k ] . bez2 = bez2 ;
}
res = res . concat ( intr ) ;
}
}
}
}
}
return res ;
}
function isPointInsidePath ( path , x , y ) {
var bbox = pathBBox ( path ) ;
return isPointInsideBBox ( bbox , x , y ) &&
interPathHelper ( path , [ [ "M" , x , y ] , [ "H" , bbox . x2 + 10 ] ] , 1 ) % 2 == 1 ;
}
function pathBBox ( path ) {
var pth = paths ( path ) ;
if ( pth . bbox ) {
return clone ( pth . bbox ) ;
}
if ( ! path ) {
return box ( ) ;
}
path = path2curve ( path ) ;
var x = 0 ,
y = 0 ,
X = [ ] ,
Y = [ ] ,
p ;
for ( var i = 0 , ii = path . length ; i < ii ; i ++ ) {
p = path [ i ] ;
if ( p [ 0 ] == "M" ) {
x = p [ 1 ] ;
y = p [ 2 ] ;
X . push ( x ) ;
Y . push ( y ) ;
} else {
var dim = curveDim ( x , y , p [ 1 ] , p [ 2 ] , p [ 3 ] , p [ 4 ] , p [ 5 ] , p [ 6 ] ) ;
X = X . concat ( dim . min . x , dim . max . x ) ;
Y = Y . concat ( dim . min . y , dim . max . y ) ;
x = p [ 5 ] ;
y = p [ 6 ] ;
}
}
var xmin = mmin . apply ( 0 , X ) ,
ymin = mmin . apply ( 0 , Y ) ,
xmax = mmax . apply ( 0 , X ) ,
ymax = mmax . apply ( 0 , Y ) ,
bb = box ( xmin , ymin , xmax - xmin , ymax - ymin ) ;
pth . bbox = clone ( bb ) ;
return bb ;
}
function rectPath ( x , y , w , h , r ) {
if ( r ) {
return [
[ "M" , x + r , y ] ,
[ "l" , w - r * 2 , 0 ] ,
[ "a" , r , r , 0 , 0 , 1 , r , r ] ,
[ "l" , 0 , h - r * 2 ] ,
[ "a" , r , r , 0 , 0 , 1 , - r , r ] ,
[ "l" , r * 2 - w , 0 ] ,
[ "a" , r , r , 0 , 0 , 1 , - r , - r ] ,
[ "l" , 0 , r * 2 - h ] ,
[ "a" , r , r , 0 , 0 , 1 , r , - r ] ,
[ "z" ]
] ;
}
var res = [ [ "M" , x , y ] , [ "l" , w , 0 ] , [ "l" , 0 , h ] , [ "l" , - w , 0 ] , [ "z" ] ] ;
res . toString = toString ;
return res ;
}
function ellipsePath ( x , y , rx , ry , a ) {
if ( a == null && ry == null ) {
ry = rx ;
}
if ( a != null ) {
var rad = Math . PI / 180 ,
x1 = x + rx * Math . cos ( - ry * rad ) ,
x2 = x + rx * Math . cos ( - a * rad ) ,
y1 = y + rx * Math . sin ( - ry * rad ) ,
y2 = y + rx * Math . sin ( - a * rad ) ,
res = [ [ "M" , x1 , y1 ] , [ "A" , rx , rx , 0 , + ( a - ry > 180 ) , 0 , x2 , y2 ] ] ;
} else {
res = [
[ "M" , x , y ] ,
[ "m" , 0 , - ry ] ,
[ "a" , rx , ry , 0 , 1 , 1 , 0 , 2 * ry ] ,
[ "a" , rx , ry , 0 , 1 , 1 , 0 , - 2 * ry ] ,
[ "z" ]
] ;
}
res . toString = toString ;
return res ;
}
2013-09-10 00:22:05 +00:00
var unit2px = Savage . _unit2px ,
getPath = {
2013-08-05 08:04:30 +00:00
path : function ( el ) {
return el . attr ( "path" ) ;
} ,
circle : function ( el ) {
var attr = unit2px ( el ) ;
return ellipsePath ( attr . cx , attr . cy , attr . r ) ;
} ,
ellipse : function ( el ) {
var attr = unit2px ( el ) ;
return ellipsePath ( attr . cx , attr . cy , attr . rx , attr . ry ) ;
} ,
rect : function ( el ) {
var attr = unit2px ( el ) ;
return rectPath ( attr . x , attr . y , attr . width , attr . height , attr . rx , attr . ry ) ;
} ,
image : function ( el ) {
var attr = unit2px ( el ) ;
return rectPath ( attr . x , attr . y , attr . width , attr . height ) ;
} ,
text : function ( el ) {
var bbox = el . node . getBBox ( ) ;
return rectPath ( bbox . x , bbox . y , bbox . width , bbox . height ) ;
} ,
g : function ( el ) {
var bbox = el . node . getBBox ( ) ;
return rectPath ( bbox . x , bbox . y , bbox . width , bbox . height ) ;
} ,
symbol : function ( el ) {
var bbox = el . getBBox ( ) ;
return rectPath ( bbox . x , bbox . y , bbox . width , bbox . height ) ;
}
} ;
function pathToRelative ( pathArray ) {
var pth = paths ( pathArray ) ;
if ( pth . rel ) {
return pathClone ( pth . rel ) ;
}
if ( ! Savage . is ( pathArray , "array" ) || ! Savage . is ( pathArray && pathArray [ 0 ] , "array" ) ) {
pathArray = Savage . parsePathString ( pathArray ) ;
}
var res = [ ] ,
x = 0 ,
y = 0 ,
mx = 0 ,
my = 0 ,
start = 0 ;
if ( pathArray [ 0 ] [ 0 ] == "M" ) {
x = pathArray [ 0 ] [ 1 ] ;
y = pathArray [ 0 ] [ 2 ] ;
mx = x ;
my = y ;
start ++ ;
res . push ( [ "M" , x , y ] ) ;
}
for ( var i = start , ii = pathArray . length ; i < ii ; i ++ ) {
var r = res [ i ] = [ ] ,
pa = pathArray [ i ] ;
if ( pa [ 0 ] != lowerCase . call ( pa [ 0 ] ) ) {
r [ 0 ] = lowerCase . call ( pa [ 0 ] ) ;
switch ( r [ 0 ] ) {
case "a" :
r [ 1 ] = pa [ 1 ] ;
r [ 2 ] = pa [ 2 ] ;
r [ 3 ] = pa [ 3 ] ;
r [ 4 ] = pa [ 4 ] ;
r [ 5 ] = pa [ 5 ] ;
r [ 6 ] = + ( pa [ 6 ] - x ) . toFixed ( 3 ) ;
r [ 7 ] = + ( pa [ 7 ] - y ) . toFixed ( 3 ) ;
break ;
case "v" :
r [ 1 ] = + ( pa [ 1 ] - y ) . toFixed ( 3 ) ;
break ;
case "m" :
mx = pa [ 1 ] ;
my = pa [ 2 ] ;
default :
for ( var j = 1 , jj = pa . length ; j < jj ; j ++ ) {
r [ j ] = + ( pa [ j ] - ( ( j % 2 ) ? x : y ) ) . toFixed ( 3 ) ;
}
}
} else {
r = res [ i ] = [ ] ;
if ( pa [ 0 ] == "m" ) {
mx = pa [ 1 ] + x ;
my = pa [ 2 ] + y ;
}
for ( var k = 0 , kk = pa . length ; k < kk ; k ++ ) {
res [ i ] [ k ] = pa [ k ] ;
}
}
var len = res [ i ] . length ;
switch ( res [ i ] [ 0 ] ) {
case "z" :
x = mx ;
y = my ;
break ;
case "h" :
x += + res [ i ] [ len - 1 ] ;
break ;
case "v" :
y += + res [ i ] [ len - 1 ] ;
break ;
default :
x += + res [ i ] [ len - 2 ] ;
y += + res [ i ] [ len - 1 ] ;
}
}
res . toString = toString ;
pth . rel = pathClone ( res ) ;
return res ;
}
function pathToAbsolute ( pathArray ) {
var pth = paths ( pathArray ) ;
if ( pth . abs ) {
return pathClone ( pth . abs ) ;
}
if ( ! is ( pathArray , "array" ) || ! is ( pathArray && pathArray [ 0 ] , "array" ) ) { // rough assumption
pathArray = Savage . parsePathString ( pathArray ) ;
}
if ( ! pathArray || ! pathArray . length ) {
return [ [ "M" , 0 , 0 ] ] ;
}
var res = [ ] ,
x = 0 ,
y = 0 ,
mx = 0 ,
my = 0 ,
start = 0 ,
pa0 ;
if ( pathArray [ 0 ] [ 0 ] == "M" ) {
x = + pathArray [ 0 ] [ 1 ] ;
y = + pathArray [ 0 ] [ 2 ] ;
mx = x ;
my = y ;
start ++ ;
res [ 0 ] = [ "M" , x , y ] ;
}
var crz = pathArray . length == 3 &&
pathArray [ 0 ] [ 0 ] == "M" &&
pathArray [ 1 ] [ 0 ] . toUpperCase ( ) == "R" &&
pathArray [ 2 ] [ 0 ] . toUpperCase ( ) == "Z" ;
for ( var r , pa , i = start , ii = pathArray . length ; i < ii ; i ++ ) {
res . push ( r = [ ] ) ;
pa = pathArray [ i ] ;
pa0 = pa [ 0 ] ;
if ( pa0 != pa0 . toUpperCase ( ) ) {
r [ 0 ] = pa0 . toUpperCase ( ) ;
switch ( r [ 0 ] ) {
case "A" :
r [ 1 ] = pa [ 1 ] ;
r [ 2 ] = pa [ 2 ] ;
r [ 3 ] = pa [ 3 ] ;
r [ 4 ] = pa [ 4 ] ;
r [ 5 ] = pa [ 5 ] ;
r [ 6 ] = + ( pa [ 6 ] + x ) ;
r [ 7 ] = + ( pa [ 7 ] + y ) ;
break ;
case "V" :
r [ 1 ] = + pa [ 1 ] + y ;
break ;
case "H" :
r [ 1 ] = + pa [ 1 ] + x ;
break ;
case "R" :
var dots = [ x , y ] . concat ( pa . slice ( 1 ) ) ;
for ( var j = 2 , jj = dots . length ; j < jj ; j ++ ) {
dots [ j ] = + dots [ j ] + x ;
dots [ ++ j ] = + dots [ j ] + y ;
}
res . pop ( ) ;
res = res . concat ( catmullRom2bezier ( dots , crz ) ) ;
break ;
case "O" :
res . pop ( ) ;
dots = ellipsePath ( x , y , pa [ 1 ] , pa [ 2 ] ) ;
dots . push ( dots [ 0 ] ) ;
res = res . concat ( dots ) ;
break ;
case "U" :
res . pop ( ) ;
res = res . concat ( ellipsePath ( x , y , pa [ 1 ] , pa [ 2 ] , pa [ 3 ] ) ) ;
r = [ "U" ] . concat ( res [ res . length - 1 ] . slice ( - 2 ) ) ;
break ;
case "M" :
mx = + pa [ 1 ] + x ;
my = + pa [ 2 ] + y ;
default :
for ( j = 1 , jj = pa . length ; j < jj ; j ++ ) {
r [ j ] = + pa [ j ] + ( ( j % 2 ) ? x : y ) ;
}
}
} else if ( pa0 == "R" ) {
dots = [ x , y ] . concat ( pa . slice ( 1 ) ) ;
res . pop ( ) ;
res = res . concat ( catmullRom2bezier ( dots , crz ) ) ;
r = [ "R" ] . concat ( pa . slice ( - 2 ) ) ;
} else if ( pa0 == "O" ) {
res . pop ( ) ;
dots = ellipsePath ( x , y , pa [ 1 ] , pa [ 2 ] ) ;
dots . push ( dots [ 0 ] ) ;
res = res . concat ( dots ) ;
} else if ( pa0 == "U" ) {
res . pop ( ) ;
res = res . concat ( ellipsePath ( x , y , pa [ 1 ] , pa [ 2 ] , pa [ 3 ] ) ) ;
r = [ "U" ] . concat ( res [ res . length - 1 ] . slice ( - 2 ) ) ;
} else {
for ( var k = 0 , kk = pa . length ; k < kk ; k ++ ) {
r [ k ] = pa [ k ] ;
}
}
pa0 = pa0 . toUpperCase ( ) ;
if ( pa0 != "O" ) {
switch ( r [ 0 ] ) {
case "Z" :
x = mx ;
y = my ;
break ;
case "H" :
x = r [ 1 ] ;
break ;
case "V" :
y = r [ 1 ] ;
break ;
case "M" :
mx = r [ r . length - 2 ] ;
my = r [ r . length - 1 ] ;
default :
x = r [ r . length - 2 ] ;
y = r [ r . length - 1 ] ;
}
}
}
res . toString = toString ;
pth . abs = pathClone ( res ) ;
return res ;
}
function l2c ( x1 , y1 , x2 , y2 ) {
return [ x1 , y1 , x2 , y2 , x2 , y2 ] ;
}
function q2c ( x1 , y1 , ax , ay , x2 , y2 ) {
var _13 = 1 / 3 ,
_23 = 2 / 3 ;
return [
_13 * x1 + _23 * ax ,
_13 * y1 + _23 * ay ,
_13 * x2 + _23 * ax ,
_13 * y2 + _23 * ay ,
x2 ,
y2
] ;
}
function a2c ( x1 , y1 , rx , ry , angle , large _arc _flag , sweep _flag , x2 , y2 , recursive ) {
// for more information of where this math came from visit:
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
var _120 = PI * 120 / 180 ,
rad = PI / 180 * ( + angle || 0 ) ,
res = [ ] ,
xy ,
2013-09-12 01:23:53 +00:00
rotate = Savage . _ . cacher ( function ( x , y , rad ) {
2013-08-05 08:04:30 +00:00
var X = x * math . cos ( rad ) - y * math . sin ( rad ) ,
Y = x * math . sin ( rad ) + y * math . cos ( rad ) ;
return { x : X , y : Y } ;
} ) ;
if ( ! recursive ) {
xy = rotate ( x1 , y1 , - rad ) ;
x1 = xy . x ;
y1 = xy . y ;
xy = rotate ( x2 , y2 , - rad ) ;
x2 = xy . x ;
y2 = xy . y ;
var cos = math . cos ( PI / 180 * angle ) ,
sin = math . sin ( PI / 180 * angle ) ,
x = ( x1 - x2 ) / 2 ,
y = ( y1 - y2 ) / 2 ;
var h = ( x * x ) / ( rx * rx ) + ( y * y ) / ( ry * ry ) ;
if ( h > 1 ) {
h = math . sqrt ( h ) ;
rx = h * rx ;
ry = h * ry ;
}
var rx2 = rx * rx ,
ry2 = ry * ry ,
k = ( large _arc _flag == sweep _flag ? - 1 : 1 ) *
math . sqrt ( abs ( ( rx2 * ry2 - rx2 * y * y - ry2 * x * x ) / ( rx2 * y * y + ry2 * x * x ) ) ) ,
cx = k * rx * y / ry + ( x1 + x2 ) / 2 ,
cy = k * - ry * x / rx + ( y1 + y2 ) / 2 ,
f1 = math . asin ( ( ( y1 - cy ) / ry ) . toFixed ( 9 ) ) ,
f2 = math . asin ( ( ( y2 - cy ) / ry ) . toFixed ( 9 ) ) ;
f1 = x1 < cx ? PI - f1 : f1 ;
f2 = x2 < cx ? PI - f2 : f2 ;
f1 < 0 && ( f1 = PI * 2 + f1 ) ;
f2 < 0 && ( f2 = PI * 2 + f2 ) ;
if ( sweep _flag && f1 > f2 ) {
f1 = f1 - PI * 2 ;
}
if ( ! sweep _flag && f2 > f1 ) {
f2 = f2 - PI * 2 ;
}
} else {
f1 = recursive [ 0 ] ;
f2 = recursive [ 1 ] ;
cx = recursive [ 2 ] ;
cy = recursive [ 3 ] ;
}
var df = f2 - f1 ;
if ( abs ( df ) > _120 ) {
var f2old = f2 ,
x2old = x2 ,
y2old = y2 ;
f2 = f1 + _120 * ( sweep _flag && f2 > f1 ? 1 : - 1 ) ;
x2 = cx + rx * math . cos ( f2 ) ;
y2 = cy + ry * math . sin ( f2 ) ;
res = a2c ( x2 , y2 , rx , ry , angle , 0 , sweep _flag , x2old , y2old , [ f2 , f2old , cx , cy ] ) ;
}
df = f2 - f1 ;
var c1 = math . cos ( f1 ) ,
s1 = math . sin ( f1 ) ,
c2 = math . cos ( f2 ) ,
s2 = math . sin ( f2 ) ,
t = math . tan ( df / 4 ) ,
hx = 4 / 3 * rx * t ,
hy = 4 / 3 * ry * t ,
m1 = [ x1 , y1 ] ,
m2 = [ x1 + hx * s1 , y1 - hy * c1 ] ,
m3 = [ x2 + hx * s2 , y2 - hy * c2 ] ,
m4 = [ x2 , y2 ] ;
m2 [ 0 ] = 2 * m1 [ 0 ] - m2 [ 0 ] ;
m2 [ 1 ] = 2 * m1 [ 1 ] - m2 [ 1 ] ;
if ( recursive ) {
return [ m2 , m3 , m4 ] . concat ( res ) ;
} else {
res = [ m2 , m3 , m4 ] . concat ( res ) . join ( ) . split ( "," ) ;
var newres = [ ] ;
for ( var i = 0 , ii = res . length ; i < ii ; i ++ ) {
newres [ i ] = i % 2 ? rotate ( res [ i - 1 ] , res [ i ] , rad ) . y : rotate ( res [ i ] , res [ i + 1 ] , rad ) . x ;
}
return newres ;
}
}
function findDotAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , t ) {
var t1 = 1 - t ;
return {
x : pow ( t1 , 3 ) * p1x + pow ( t1 , 2 ) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow ( t , 3 ) * p2x ,
y : pow ( t1 , 3 ) * p1y + pow ( t1 , 2 ) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow ( t , 3 ) * p2y
} ;
}
function curveDim ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y ) {
var a = ( c2x - 2 * c1x + p1x ) - ( p2x - 2 * c2x + c1x ) ,
b = 2 * ( c1x - p1x ) - 2 * ( c2x - c1x ) ,
c = p1x - c1x ,
t1 = ( - b + math . sqrt ( b * b - 4 * a * c ) ) / 2 / a ,
t2 = ( - b - math . sqrt ( b * b - 4 * a * c ) ) / 2 / a ,
y = [ p1y , p2y ] ,
x = [ p1x , p2x ] ,
dot ;
abs ( t1 ) > "1e12" && ( t1 = . 5 ) ;
abs ( t2 ) > "1e12" && ( t2 = . 5 ) ;
if ( t1 > 0 && t1 < 1 ) {
dot = findDotAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , t1 ) ;
x . push ( dot . x ) ;
y . push ( dot . y ) ;
}
if ( t2 > 0 && t2 < 1 ) {
dot = findDotAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , t2 ) ;
x . push ( dot . x ) ;
y . push ( dot . y ) ;
}
a = ( c2y - 2 * c1y + p1y ) - ( p2y - 2 * c2y + c1y ) ;
b = 2 * ( c1y - p1y ) - 2 * ( c2y - c1y ) ;
c = p1y - c1y ;
t1 = ( - b + math . sqrt ( b * b - 4 * a * c ) ) / 2 / a ;
t2 = ( - b - math . sqrt ( b * b - 4 * a * c ) ) / 2 / a ;
abs ( t1 ) > "1e12" && ( t1 = . 5 ) ;
abs ( t2 ) > "1e12" && ( t2 = . 5 ) ;
if ( t1 > 0 && t1 < 1 ) {
dot = findDotAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , t1 ) ;
x . push ( dot . x ) ;
y . push ( dot . y ) ;
}
if ( t2 > 0 && t2 < 1 ) {
dot = findDotAtSegment ( p1x , p1y , c1x , c1y , c2x , c2y , p2x , p2y , t2 ) ;
x . push ( dot . x ) ;
y . push ( dot . y ) ;
}
return {
min : { x : mmin . apply ( 0 , x ) , y : mmin . apply ( 0 , y ) } ,
max : { x : mmax . apply ( 0 , x ) , y : mmax . apply ( 0 , y ) }
} ;
}
function path2curve ( path , path2 ) {
var pth = ! path2 && paths ( path ) ;
if ( ! path2 && pth . curve ) {
return pathClone ( pth . curve ) ;
}
var p = pathToAbsolute ( path ) ,
p2 = path2 && pathToAbsolute ( path2 ) ,
attrs = { x : 0 , y : 0 , bx : 0 , by : 0 , X : 0 , Y : 0 , qx : null , qy : null } ,
attrs2 = { x : 0 , y : 0 , bx : 0 , by : 0 , X : 0 , Y : 0 , qx : null , qy : null } ,
processPath = function ( path , d ) {
var nx , ny ;
if ( ! path ) {
return [ "C" , d . x , d . y , d . x , d . y , d . x , d . y ] ;
}
! ( path [ 0 ] in { T : 1 , Q : 1 } ) && ( d . qx = d . qy = null ) ;
switch ( path [ 0 ] ) {
case "M" :
d . X = path [ 1 ] ;
d . Y = path [ 2 ] ;
break ;
case "A" :
path = [ "C" ] . concat ( a2c . apply ( 0 , [ d . x , d . y ] . concat ( path . slice ( 1 ) ) ) ) ;
break ;
case "S" :
nx = d . x + ( d . x - ( d . bx || d . x ) ) ;
ny = d . y + ( d . y - ( d . by || d . y ) ) ;
path = [ "C" , nx , ny ] . concat ( path . slice ( 1 ) ) ;
break ;
case "T" :
d . qx = d . x + ( d . x - ( d . qx || d . x ) ) ;
d . qy = d . y + ( d . y - ( d . qy || d . y ) ) ;
path = [ "C" ] . concat ( q2c ( d . x , d . y , d . qx , d . qy , path [ 1 ] , path [ 2 ] ) ) ;
break ;
case "Q" :
d . qx = path [ 1 ] ;
d . qy = path [ 2 ] ;
path = [ "C" ] . concat ( q2c ( d . x , d . y , path [ 1 ] , path [ 2 ] , path [ 3 ] , path [ 4 ] ) ) ;
break ;
case "L" :
path = [ "C" ] . concat ( l2c ( d . x , d . y , path [ 1 ] , path [ 2 ] ) ) ;
break ;
case "H" :
path = [ "C" ] . concat ( l2c ( d . x , d . y , path [ 1 ] , d . y ) ) ;
break ;
case "V" :
path = [ "C" ] . concat ( l2c ( d . x , d . y , d . x , path [ 1 ] ) ) ;
break ;
case "Z" :
path = [ "C" ] . concat ( l2c ( d . x , d . y , d . X , d . Y ) ) ;
break ;
}
return path ;
} ,
fixArc = function ( pp , i ) {
if ( pp [ i ] . length > 7 ) {
pp [ i ] . shift ( ) ;
var pi = pp [ i ] ;
while ( pi . length ) {
pp . splice ( i ++ , 0 , [ "C" ] . concat ( pi . splice ( 0 , 6 ) ) ) ;
}
pp . splice ( i , 1 ) ;
ii = mmax ( p . length , p2 && p2 . length || 0 ) ;
}
} ,
fixM = function ( path1 , path2 , a1 , a2 , i ) {
if ( path1 && path2 && path1 [ i ] [ 0 ] == "M" && path2 [ i ] [ 0 ] != "M" ) {
path2 . splice ( i , 0 , [ "M" , a2 . x , a2 . y ] ) ;
a1 . bx = 0 ;
a1 . by = 0 ;
a1 . x = path1 [ i ] [ 1 ] ;
a1 . y = path1 [ i ] [ 2 ] ;
ii = mmax ( p . length , p2 && p2 . length || 0 ) ;
}
} ;
for ( var i = 0 , ii = mmax ( p . length , p2 && p2 . length || 0 ) ; i < ii ; i ++ ) {
p [ i ] = processPath ( p [ i ] , attrs ) ;
fixArc ( p , i ) ;
p2 && ( p2 [ i ] = processPath ( p2 [ i ] , attrs2 ) ) ;
p2 && fixArc ( p2 , i ) ;
fixM ( p , p2 , attrs , attrs2 , i ) ;
fixM ( p2 , p , attrs2 , attrs , i ) ;
var seg = p [ i ] ,
seg2 = p2 && p2 [ i ] ,
seglen = seg . length ,
seg2len = p2 && seg2 . length ;
attrs . x = seg [ seglen - 2 ] ;
attrs . y = seg [ seglen - 1 ] ;
attrs . bx = toFloat ( seg [ seglen - 4 ] ) || attrs . x ;
attrs . by = toFloat ( seg [ seglen - 3 ] ) || attrs . y ;
attrs2 . bx = p2 && ( toFloat ( seg2 [ seg2len - 4 ] ) || attrs2 . x ) ;
attrs2 . by = p2 && ( toFloat ( seg2 [ seg2len - 3 ] ) || attrs2 . y ) ;
attrs2 . x = p2 && seg2 [ seg2len - 2 ] ;
attrs2 . y = p2 && seg2 [ seg2len - 1 ] ;
}
if ( ! p2 ) {
pth . curve = pathClone ( p ) ;
}
return p2 ? [ p , p2 ] : p ;
}
function mapPath ( path , matrix ) {
if ( ! matrix ) {
return path ;
}
var x , y , i , j , ii , jj , pathi ;
path = path2curve ( path ) ;
for ( i = 0 , ii = path . length ; i < ii ; i ++ ) {
pathi = path [ i ] ;
for ( j = 1 , jj = pathi . length ; j < jj ; j += 2 ) {
x = matrix . x ( pathi [ j ] , pathi [ j + 1 ] ) ;
y = matrix . y ( pathi [ j ] , pathi [ j + 1 ] ) ;
pathi [ j ] = x ;
pathi [ j + 1 ] = y ;
}
}
return path ;
}
// http://schepers.cc/getting-to-the-point
function catmullRom2bezier ( crp , z ) {
var d = [ ] ;
for ( var i = 0 , iLen = crp . length ; iLen - 2 * ! z > i ; i += 2 ) {
var p = [
{ x : + crp [ i - 2 ] , y : + crp [ i - 1 ] } ,
{ x : + crp [ i ] , y : + crp [ i + 1 ] } ,
{ x : + crp [ i + 2 ] , y : + crp [ i + 3 ] } ,
{ x : + crp [ i + 4 ] , y : + crp [ i + 5 ] }
] ;
if ( z ) {
if ( ! i ) {
p [ 0 ] = { x : + crp [ iLen - 2 ] , y : + crp [ iLen - 1 ] } ;
} else if ( iLen - 4 == i ) {
p [ 3 ] = { x : + crp [ 0 ] , y : + crp [ 1 ] } ;
} else if ( iLen - 2 == i ) {
p [ 2 ] = { x : + crp [ 0 ] , y : + crp [ 1 ] } ;
p [ 3 ] = { x : + crp [ 2 ] , y : + crp [ 3 ] } ;
}
} else {
if ( iLen - 4 == i ) {
p [ 3 ] = p [ 2 ] ;
} else if ( ! i ) {
p [ 0 ] = { x : + crp [ i ] , y : + crp [ i + 1 ] } ;
}
}
d . push ( [ "C" ,
( - p [ 0 ] . x + 6 * p [ 1 ] . x + p [ 2 ] . x ) / 6 ,
( - p [ 0 ] . y + 6 * p [ 1 ] . y + p [ 2 ] . y ) / 6 ,
( p [ 1 ] . x + 6 * p [ 2 ] . x - p [ 3 ] . x ) / 6 ,
( p [ 1 ] . y + 6 * p [ 2 ] . y - p [ 3 ] . y ) / 6 ,
p [ 2 ] . x ,
p [ 2 ] . y
] ) ;
}
return d ;
}
// export
Savage . path = paths ;
/ * \
* Savage . path . getTotalLength
[ method ]
* *
* Returns length of the given path in pixels .
* *
- path ( string ) SVG path string .
* *
= ( number ) length .
\ * /
Savage . path . getTotalLength = getTotalLength ;
/ * \
* Savage . path . getPointAtLength
[ method ]
* *
* Return coordinates of the point located at the given length on the given path .
* *
- path ( string ) SVG path string
- length ( number )
* *
= ( object ) representation of the point :
o {
o x : ( number ) x coordinate
o y : ( number ) y coordinate
o alpha : ( number ) angle of derivative
o }
\ * /
Savage . path . getPointAtLength = getPointAtLength ;
/ * \
* Savage . path . getSubpath
[ method ]
* *
* Return subpath of a given path from given length to given length .
* *
- path ( string ) SVG path string
- from ( number ) position of the start of the segment
- to ( number ) position of the end of the segment
* *
= ( string ) pathstring for the segment
\ * /
Savage . path . getSubpath = function ( path , from , to ) {
if ( this . getTotalLength ( path ) - to < 1e-6 ) {
return getSubpathsAtLength ( path , from ) . end ;
}
var a = getSubpathsAtLength ( path , to , 1 ) ;
return from ? getSubpathsAtLength ( a , from ) . end : a ;
} ;
/ * \
* Element . getTotalLength
[ method ]
* *
* Returns length of the path in pixels . Only works for element of “ path ” type .
= ( number ) length .
\ * /
elproto . getTotalLength = function ( ) {
if ( this . node . getTotalLength ) {
return this . node . getTotalLength ( ) ;
}
} ;
/ * \
* Element . getPointAtLength
[ method ]
* *
* Return coordinates of the point located at the given length on the given path . Only works for element of “ path ” type .
* *
- length ( number )
* *
= ( object ) representation of the point :
o {
o x : ( number ) x coordinate
o y : ( number ) y coordinate
o alpha : ( number ) angle of derivative
o }
\ * /
elproto . getPointAtLength = function ( length ) {
return getPointAtLength ( this . attr ( "d" ) , length ) ;
} ;
/ * \
* Element . getSubpath
[ method ]
* *
* Return subpath of a given element from given length to given length . Only works for element of “ path ” type .
* *
- from ( number ) position of the start of the segment
- to ( number ) position of the end of the segment
* *
= ( string ) pathstring for the segment
\ * /
elproto . getSubpath = function ( from , to ) {
2013-09-05 13:24:16 +00:00
return Savage . path . getSubpath ( this . attr ( "d" ) , from , to ) ;
2013-08-05 08:04:30 +00:00
} ;
Savage . _ . box = box ;
/ * \
2013-09-16 06:55:33 +00:00
* Savage . path . findDotsAtSegment
2013-08-05 08:04:30 +00:00
[ method ]
* *
* Utility method
* *
* Find dot coordinates on the given cubic bezier curve at the given t .
- p1x ( number ) x of the first point of the curve
- p1y ( number ) y of the first point of the curve
- c1x ( number ) x of the first anchor of the curve
- c1y ( number ) y of the first anchor of the curve
- c2x ( number ) x of the second anchor of the curve
- c2y ( number ) y of the second anchor of the curve
- p2x ( number ) x of the second point of the curve
- p2y ( number ) y of the second point of the curve
- t ( number ) position on the curve ( 0. . 1 )
= ( object ) point information in format :
o {
o x : ( number ) x coordinate of the point
o y : ( number ) y coordinate of the point
o m : {
o x : ( number ) x coordinate of the left anchor
o y : ( number ) y coordinate of the left anchor
o }
o n : {
o x : ( number ) x coordinate of the right anchor
o y : ( number ) y coordinate of the right anchor
o }
o start : {
o x : ( number ) x coordinate of the start of the curve
o y : ( number ) y coordinate of the start of the curve
o }
o end : {
o x : ( number ) x coordinate of the end of the curve
o y : ( number ) y coordinate of the end of the curve
o }
o alpha : ( number ) angle of the curve derivative at the point
o }
\ * /
Savage . path . findDotsAtSegment = findDotsAtSegment ;
/ * \
* Savage . path . bezierBBox
[ method ]
* *
* Utility method
* *
* Return bounding box of a given cubic bezier curve
- p1x ( number ) x of the first point of the curve
- p1y ( number ) y of the first point of the curve
- c1x ( number ) x of the first anchor of the curve
- c1y ( number ) y of the first anchor of the curve
- c2x ( number ) x of the second anchor of the curve
- c2y ( number ) y of the second anchor of the curve
- p2x ( number ) x of the second point of the curve
- p2y ( number ) y of the second point of the curve
* or
- bez ( array ) array of six points for bezier curve
= ( object ) point information in format :
o {
o min : {
o x : ( number ) x coordinate of the left point
o y : ( number ) y coordinate of the top point
o }
o max : {
o x : ( number ) x coordinate of the right point
o y : ( number ) y coordinate of the bottom point
o }
o }
\ * /
Savage . path . bezierBBox = bezierBBox ;
/ * \
* Savage . path . isPointInsideBBox
[ method ]
* *
* Utility method
* *
* Returns ` true ` if given point is inside bounding box .
- bbox ( string ) bounding box
- x ( string ) x coordinate of the point
- y ( string ) y coordinate of the point
= ( boolean ) ` true ` if point inside
\ * /
Savage . path . isPointInsideBBox = isPointInsideBBox ;
/ * \
* Savage . path . isBBoxIntersect
[ method ]
* *
* Utility method
* *
* Returns ` true ` if two bounding boxes intersect
- bbox1 ( string ) first bounding box
- bbox2 ( string ) second bounding box
= ( boolean ) ` true ` if they intersect
\ * /
Savage . path . isBBoxIntersect = isBBoxIntersect ;
/ * \
* Savage . path . intersection
[ method ]
* *
* Utility method
* *
* Finds intersections of two paths
- path1 ( string ) path string
- path2 ( string ) path string
= ( array ) dots of intersection
o [
o {
o x : ( number ) x coordinate of the point
o y : ( number ) y coordinate of the point
o t1 : ( number ) t value for segment of path1
o t2 : ( number ) t value for segment of path2
o segment1 : ( number ) order number for segment of path1
o segment2 : ( number ) order number for segment of path2
o bez1 : ( array ) eight coordinates representing beziér curve for the segment of path1
o bez2 : ( array ) eight coordinates representing beziér curve for the segment of path2
o }
o ]
\ * /
Savage . path . intersection = pathIntersection ;
Savage . path . intersectionNumber = pathIntersectionNumber ;
/ * \
* Savage . path . isPointInside
[ method ]
* *
* Utility method
* *
* Returns ` true ` if given point is inside a given closed path .
- path ( string ) path string
- x ( number ) x of the point
- y ( number ) y of the point
= ( boolean ) true , if point is inside the path
\ * /
Savage . path . isPointInside = isPointInsidePath ;
/ * \
2013-09-16 06:55:33 +00:00
* Savage . path . getBBox
2013-08-05 08:04:30 +00:00
[ method ]
* *
* Utility method
* *
* Return bounding box of a given path
- path ( string ) path string
= ( object ) bounding box
o {
o x : ( number ) x coordinate of the left top point of the box
o y : ( number ) y coordinate of the left top point of the box
o x2 : ( number ) x coordinate of the right bottom point of the box
o y2 : ( number ) y coordinate of the right bottom point of the box
o width : ( number ) width of the box
o height : ( number ) height of the box
o }
\ * /
Savage . path . getBBox = pathBBox ;
Savage . path . get = getPath ;
/ * \
* Savage . path . toRelative
[ method ]
* *
* Utility method
* *
* Converts path coordinates into relative values .
- path ( string ) path string
= ( array ) path string
\ * /
Savage . path . toRelative = pathToRelative ;
/ * \
* Savage . path . toAbsolute
[ method ]
* *
* Utility method
* *
* Converts path coordinates into absolute values .
- path ( string ) path string
= ( array ) path string
\ * /
Savage . path . toAbsolute = pathToAbsolute ;
/ * \
* Savage . path . toCubic
[ method ]
* *
* Utility method
* *
* Converts path to a new path where all segments are cubic bezier curves .
- pathString ( string | array ) path string or array of segments
= ( array ) array of segments .
\ * /
Savage . path . toCubic = path2curve ;
/ * \
* Savage . path . map
[ method ]
* *
* Transform the path string with given matrix .
- path ( string ) path string
- matrix ( object ) see @ Matrix
= ( string ) transformed path string
\ * /
Savage . path . map = mapPath ;
Savage . path . toString = toString ;
Savage . path . clone = pathClone ;
} ) ;
2013-08-15 08:29:47 +00:00
// 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.
2013-08-05 08:04:30 +00:00
Savage . plugin ( function ( Savage , Element , Paper , glob ) {
var mmax = Math . max ,
mmin = Math . min ;
// Set
var Set = function ( items ) {
this . items = [ ] ;
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 function returns ` false ` it will stop loop 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 ;
} ;
setproto . attr = function ( value ) {
for ( var i = 0 , ii = this . items . length ; i < ii ; i ++ ) {
this . items [ i ] . attr ( value ) ;
}
return this ;
} ;
/ * \
* Set . clear
[ method ]
* *
* Removeds all elements from the set
\ * /
setproto . clear = function ( ) {
while ( this . length ) {
this . pop ( ) ;
}
} ;
/ * \
* Set . splice
[ method ]
* *
* Removes given element 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 & 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 ;
}
} ;
setproto . insertAfter = function ( el ) {
var i = this . items . length ;
while ( i -- ) {
this . items [ i ] . insertAfter ( el ) ;
}
return this ;
} ;
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
} ;
} ;
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 "Savage\u2018s set" ;
} ;
setproto . type = "set" ;
// export
Savage . set = function ( ) {
var set = new Set ;
if ( arguments . length ) {
set . push . apply ( set , Array . prototype . slice . call ( arguments , 0 ) ) ;
}
return set ;
} ;
} ) ;
2013-08-15 08:29:47 +00:00
// 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.
2013-08-05 08:04:30 +00:00
Savage . plugin ( function ( Savage , 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 ) {
t2 = Str ( t2 ) . replace ( /\.{3}|\u2026/g , t1 ) ;
t1 = Savage . parseTransformString ( t1 ) || [ ] ;
t2 = Savage . parseTransformString ( t2 ) || [ ] ;
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 ] ) )
) {
return ;
}
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 getColour ( clr ) {
return Savage . rgb ( clr [ 0 ] , clr [ 1 ] , clr [ 2 ] ) ;
}
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 Savage.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 ;
}
Element . prototype . equal = function ( name , b ) {
var A , B , a = Str ( this . attr ( name ) || "" ) ;
if ( a == + a && b == + b ) {
return {
from : + a ,
to : + b ,
f : getNumber
} ;
}
if ( names [ name ] == "colour" ) {
A = Savage . color ( a ) ;
B = Savage . 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 == "transform" || name == "gradientTransform" || name == "patternTransform" ) {
// TODO: b could be an SVG transform string or matrix
2013-09-09 00:33:59 +00:00
return equaliseTransform ( a , b ) ;
2013-08-05 08:04:30 +00:00
}
if ( name == "d" || name == "path" ) {
A = Savage . path . toCubic ( a , b ) ;
return {
from : path2array ( A [ 0 ] ) ,
to : path2array ( A [ 1 ] ) ,
f : getPath ( A [ 0 ] )
} ;
}
var aUnit = a . match ( reUnit ) ,
bUnit = b . match ( reUnit ) ;
if ( aUnit && 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
} ;
}
} ;
} ) ;
2013-08-15 08:29:47 +00:00
// 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.
2013-08-05 08:04:30 +00:00
Savage . plugin ( function ( Savage , Element , Paper , glob ) {
var elproto = Element . prototype ,
has = "hasOwnProperty" ,
supportsTouch = "createTouch" in glob . doc ,
events = [
"click" , "dblclick" , "mousedown" , "mousemove" , "mouseout" ,
"mouseover" , "mouseup" , "touchstart" , "touchmove" , "touchend" ,
"touchcancel"
] ,
touchMap = {
mousedown : "touchstart" ,
mousemove : "touchmove" ,
mouseup : "touchend"
} ,
getScroll = function ( xy ) {
var name = xy == "y" ? "scrollTop" : "scrollLeft" ;
return glob . doc . documentElement [ name ] || glob . doc . body [ name ] ;
} ,
preventDefault = function ( ) {
this . returnValue = false ;
} ,
preventTouch = function ( ) {
return this . originalEvent . preventDefault ( ) ;
} ,
stopPropagation = function ( ) {
this . cancelBubble = true ;
} ,
stopTouch = function ( ) {
return this . originalEvent . stopPropagation ( ) ;
} ,
addEvent = ( function ( ) {
if ( glob . doc . addEventListener ) {
return function ( obj , type , fn , element ) {
var realName = supportsTouch && touchMap [ type ] ? touchMap [ type ] : type ,
f = function ( e ) {
var scrollY = getScroll ( "y" ) ,
scrollX = getScroll ( "x" ) ,
x = e . clientX + scrollX ,
y = e . clientY + scrollY ;
if ( supportsTouch && touchMap [ has ] ( type ) ) {
for ( var i = 0 , ii = e . targetTouches && e . targetTouches . length ; i < ii ; i ++ ) {
if ( e . targetTouches [ i ] . target == obj ) {
var olde = e ;
e = e . targetTouches [ i ] ;
e . originalEvent = olde ;
e . preventDefault = preventTouch ;
e . stopPropagation = stopTouch ;
break ;
}
}
}
return fn . call ( element , e , x , y ) ;
} ;
obj . addEventListener ( realName , f , false ) ;
return function ( ) {
obj . removeEventListener ( realName , f , false ) ;
return true ;
} ;
} ;
} else if ( glob . doc . attachEvent ) {
return function ( obj , type , fn , element ) {
var f = function ( e ) {
e = e || glob . win . event ;
var scrollY = getScroll ( "y" ) ,
scrollX = getScroll ( "x" ) ,
x = e . clientX + scrollX ,
y = e . clientY + scrollY ;
e . preventDefault = e . preventDefault || preventDefault ;
e . stopPropagation = e . stopPropagation || stopPropagation ;
return fn . call ( element , e , x , y ) ;
} ;
obj . attachEvent ( "on" + type , f ) ;
var detacher = function ( ) {
obj . detachEvent ( "on" + type , f ) ;
return true ;
} ;
return detacher ;
} ;
}
} ) ( ) ,
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 . length ,
touch ;
while ( i -- ) {
touch = e . touches [ i ] ;
if ( touch . identifier == dragi . el . _drag . id ) {
x = touch . clientX ;
y = touch . clientY ;
( e . originalEvent ? e . originalEvent : e ) . preventDefault ( ) ;
break ;
}
}
} else {
e . preventDefault ( ) ;
}
var node = dragi . el . node ,
o ,
2013-09-12 02:56:46 +00:00
glob = Savage . _ . glob ,
2013-08-05 08:04:30 +00:00
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("savage.drag.over." + dragi.el.id, dragi.el, o);
x += scrollX ;
y += scrollY ;
eve ( "savage.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 ) {
Savage . unmousemove ( dragMove ) . unmouseup ( dragUp ) ;
var i = drag . length ,
dragi ;
while ( i -- ) {
dragi = drag [ i ] ;
dragi . el . _drag = { } ;
eve ( "savage.drag.end." + dragi . el . id , dragi . end _scope || dragi . start _scope || dragi . move _scope || dragi . el , e ) ;
}
drag = [ ] ;
} ;
/ * \
* Element . click
[ method ]
* *
* Adds event handler for click for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . unclick
[ method ]
* *
* Removes event handler for click for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . dblclick
[ method ]
* *
* Adds event handler for double click for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . undblclick
[ method ]
* *
* Removes event handler for double click for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . mousedown
[ method ]
* *
* Adds event handler for mousedown for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . unmousedown
[ method ]
* *
* Removes event handler for mousedown for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . mousemove
[ method ]
* *
* Adds event handler for mousemove for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . unmousemove
[ method ]
* *
* Removes event handler for mousemove for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . mouseout
[ method ]
* *
* Adds event handler for mouseout for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . unmouseout
[ method ]
* *
* Removes event handler for mouseout for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . mouseover
[ method ]
* *
* Adds event handler for mouseover for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . unmouseover
[ method ]
* *
* Removes event handler for mouseover for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . mouseup
[ method ]
* *
* Adds event handler for mouseup for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . unmouseup
[ method ]
* *
* Removes event handler for mouseup for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . touchstart
[ method ]
* *
* Adds event handler for touchstart for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . untouchstart
[ method ]
* *
* Removes event handler for touchstart for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . touchmove
[ method ]
* *
* Adds event handler for touchmove for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . untouchmove
[ method ]
* *
* Removes event handler for touchmove for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . touchend
[ method ]
* *
* Adds event handler for touchend for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . untouchend
[ method ]
* *
* Removes event handler for touchend for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . touchcancel
[ method ]
* *
* Adds event handler for touchcancel for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
/ * \
* Element . untouchcancel
[ method ]
* *
* Removes event handler for touchcancel for the element .
- handler ( function ) handler for the event
= ( object ) @ Element
\ * /
for ( var i = events . length ; i -- ; ) {
( function ( eventName ) {
Savage [ eventName ] = elproto [ eventName ] = function ( fn , scope ) {
if ( Savage . is ( fn , "function" ) ) {
this . events = this . events || [ ] ;
this . events . push ( {
name : eventName ,
f : fn ,
unbind : addEvent ( this . shape || this . node || glob . doc , eventName , fn , scope || this )
} ) ;
}
return this ;
} ;
2013-09-12 02:56:46 +00:00
Savage [ "un" + eventName ] =
elproto [ "un" + eventName ] = function ( fn ) {
2013-08-05 08:04:30 +00:00
var events = this . events || [ ] ,
l = events . length ;
2013-09-12 02:56:46 +00:00
while ( l -- ) if ( events [ l ] . name == eventName &&
( events [ l ] . f == fn || ! fn ) ) {
2013-08-05 08:04:30 +00:00
events [ l ] . unbind ( ) ;
events . splice ( l , 1 ) ;
! events . length && delete this . events ;
return this ;
}
return this ;
} ;
} ) ( events [ i ] ) ;
}
/ * \
* Element . hover
[ method ]
* *
* Adds event handlers for hover for 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 event handlers for hover for 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 = [ ] ;
/ * \
* Element . drag
[ method ]
* *
* Adds event handlers for drag of the element .
- 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 will be triggered : ` drag.start.<id> ` on start ,
* ` drag.end.<id> ` on end and ` drag.move.<id> ` on every move . When element will be dragged over another element
* ` drag.over.<id> ` will be fired as well .
*
* Start event and start handler will be 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 will be 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 will be 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 ) {
if ( ! arguments . length ) {
var origTransform ;
return this . drag ( function ( dx , dy ) {
this . attr ( {
transform : origTransform + ( origTransform ? "T" : "t" ) + [ dx , dy ]
} ) ;
} , function ( ) {
origTransform = this . transform ( ) . local ;
} ) ;
}
function start ( e ) {
( e . originalEvent || e ) . preventDefault ( ) ;
var scrollY = getScroll ( "y" ) ,
scrollX = getScroll ( "x" ) ;
this . _drag . x = e . clientX + scrollX ;
this . _drag . y = e . clientY + scrollY ;
this . _drag . id = e . identifier ;
! drag . length && Savage . mousemove ( dragMove ) . mouseup ( dragUp ) ;
drag . push ( { el : this , move _scope : move _scope , start _scope : start _scope , end _scope : end _scope } ) ;
onstart && eve . on ( "savage.drag.start." + this . id , onstart ) ;
onmove && eve . on ( "savage.drag.move." + this . id , onmove ) ;
onend && eve . on ( "savage.drag.end." + this . id , onend ) ;
eve ( "savage.drag.start." + this . id , start _scope || move _scope || this , e . clientX + scrollX , e . clientY + scrollY , e ) ;
}
this . _drag = { } ;
draggable . push ( { el : this , start : start } ) ;
this . mousedown ( start ) ;
return this ;
} ;
2013-09-12 02:56:46 +00:00
/ *
2013-08-05 08:04:30 +00:00
* Element . onDragOver
[ method ]
* *
* Shortcut for assigning event handler for ` drag.over.<id> ` event , where id is id of the element ( see @ Element . id ) .
- f ( function ) handler for event , first argument would be the element you are dragging over
\ * /
2013-09-12 02:56:46 +00:00
// elproto.onDragOver = function (f) {
// f ? eve.on("savage.drag.over." + this.id, f) : eve.unbind("savage.drag.over." + this.id);
// };
2013-08-05 08:04:30 +00:00
/ * \
* Element . undrag
[ method ]
* *
* Removes all drag event handlers from given element .
\ * /
elproto . undrag = function ( ) {
var i = draggable . length ;
while ( i -- ) if ( draggable [ i ] . el == this ) {
this . unmousedown ( draggable [ i ] . start ) ;
draggable . splice ( i , 1 ) ;
eve . unbind ( "savage.drag.*." + this . id ) ;
}
! draggable . length && Savage . unmousemove ( dragMove ) . unmouseup ( dragUp ) ;
2013-09-12 02:56:46 +00:00
return this ;
2013-08-05 08:04:30 +00:00
} ;
2013-08-15 08:29:47 +00:00
} ) ;
// 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.
Savage . plugin ( function ( Savage , Element , Paper , glob ) {
var elproto = Element . prototype ,
pproto = Paper . prototype ,
rgurl = /^\s*url\((.+)\)/ ,
Str = String ,
$ = Savage . _ . $ ;
Savage . filter = { } ;
2013-08-22 10:00:40 +00:00
/ * \
* Paper . filter
[ method ]
* *
* Creates filter element
* *
- filstr ( string ) SVG fragment of filter provided as a string .
= ( object ) @ Element
* Note : It is recommended to use filters embedded into page inside empty SVG element .
> Usage
| var f = paper . filter ( '<feGaussianBlur stdDeviation="2"/>' ) ,
| c = paper . circle ( 10 , 10 , 10 ) . attr ( {
| filter : f
| } ) ;
\ * /
2013-08-15 08:29:47 +00:00
pproto . filter = function ( filstr ) {
var f = Savage . parse ( Str ( filstr ) ) ,
id = Savage . _ . id ( ) ,
2013-08-22 10:00:40 +00:00
width = this . node . offsetWidth ,
height = this . node . offsetHeight ,
2013-08-15 08:29:47 +00:00
filter = $ ( "filter" ) ;
$ ( filter , {
id : id ,
filterUnits : "userSpaceOnUse" ,
x : 0 ,
y : 0 ,
width : width ,
height : height
} ) ;
filter . appendChild ( f . node ) ;
this . defs . appendChild ( filter ) ;
return new Element ( filter ) ;
} ;
eve . on ( "savage.util.getattr.filter" , function ( ) {
eve . stop ( ) ;
var p = $ ( this . node , "filter" ) ;
if ( p ) {
var match = Str ( p ) . match ( rgurl ) ;
return match && Savage . select ( match [ 1 ] ) ;
}
} ) ;
eve . on ( "savage.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 : "url(#" + id + ")"
} ) ;
}
2013-09-05 00:10:45 +00:00
if ( ! value || value == "none" ) {
2013-08-15 08:29:47 +00:00
eve . stop ( ) ;
this . node . removeAttribute ( "filter" ) ;
}
} ) ;
2013-08-22 10:00:40 +00:00
/ * \
2013-09-10 02:22:49 +00:00
* Savage . filter . blur
2013-08-22 10:00:40 +00:00
[ method ]
* *
* Returns string of the blur filter .
* *
- x ( number ) amount of horisontal blur in px .
- y ( number ) # optional amount of vertical blur in px .
= ( string ) filter representation
> Usage
| var f = paper . filter ( Savage . filter . blur ( 5 , 10 ) ) ,
| c = paper . circle ( 10 , 10 , 10 ) . attr ( {
| filter : f
| } ) ;
\ * /
2013-08-15 08:29:47 +00:00
Savage . filter . blur = function ( x , y ) {
if ( x == null ) {
x = 2 ;
}
var def = y == null ? x : [ x , y ] ;
return Savage . format ( '\<feGaussianBlur stdDeviation="{def}"/>' , {
def : def
} ) ;
} ;
Savage . filter . blur . toString = function ( ) {
return this ( ) ;
} ;
2013-08-22 10:00:40 +00:00
/ * \
2013-09-10 02:22:49 +00:00
* Savage . filter . shadow
2013-08-22 10:00:40 +00:00
[ method ]
* *
2013-09-10 02:22:49 +00:00
* Returns string of the shadow filter .
2013-08-22 10:00:40 +00:00
* *
- dx ( number ) horisontal shift of the shadow in px .
- dy ( number ) vertical shift of the shadow in px .
- blur ( number ) # optional amount of blur .
- color ( string ) # optional color of the shadow .
= ( string ) filter representation
> Usage
2013-09-10 02:22:49 +00:00
| var f = paper . filter ( Savage . filter . shadow ( 0 , 2 , 3 ) ) ,
2013-08-22 10:00:40 +00:00
| c = paper . circle ( 10 , 10 , 10 ) . attr ( {
| filter : f
| } ) ;
\ * /
2013-08-15 08:29:47 +00:00
Savage . filter . shadow = function ( dx , dy , blur , color ) {
2013-09-10 02:22:49 +00:00
color = color || "#000" ;
2013-08-15 08:29:47 +00:00
if ( blur == null ) {
2013-09-05 00:10:45 +00:00
blur = 4 ;
2013-08-15 08:29:47 +00:00
}
if ( dx == null ) {
dx = 0 ;
2013-09-05 00:10:45 +00:00
dy = 2 ;
2013-08-15 08:29:47 +00:00
}
if ( dy == null ) {
dy = dx ;
}
2013-09-10 02:22:49 +00:00
return Savage . format ( '<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>' , {
color : color ,
2013-08-15 08:29:47 +00:00
dx : dx ,
dy : dy ,
blur : blur
} ) ;
} ;
Savage . filter . shadow . toString = function ( ) {
return this ( ) ;
} ;
2013-09-10 02:22:49 +00:00
/ * \
* Savage . filter . grayscale
[ method ]
* *
* Returns string of the grayscale filter .
* *
- amount ( number ) amount of filter ( ` 0..1 ` ) .
= ( string ) filter representation
\ * /
Savage . filter . grayscale = function ( amount ) {
if ( amount == null ) {
amount = 1 ;
}
return Savage . 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 )
} ) ;
} ;
Savage . filter . grayscale . toString = function ( ) {
return this ( ) ;
} ;
/ * \
* Savage . filter . sepia
[ method ]
* *
* Returns string of the sepia filter .
* *
- amount ( number ) amount of filter ( ` 0..1 ` ) .
= ( string ) filter representation
\ * /
Savage . filter . sepia = function ( amount ) {
if ( amount == null ) {
amount = 1 ;
}
return Savage . 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 )
} ) ;
} ;
Savage . filter . sepia . toString = function ( ) {
return this ( ) ;
} ;
/ * \
* Savage . filter . saturate
[ method ]
* *
* Returns string of the saturate filter .
* *
- amount ( number ) amount of filter ( ` 0..1 ` ) .
= ( string ) filter representation
\ * /
Savage . filter . saturate = function ( amount ) {
if ( amount == null ) {
amount = 1 ;
}
return Savage . format ( '<feColorMatrix type="saturate" values="{amount}"/>' , {
amount : 1 - amount
} ) ;
} ;
Savage . filter . saturate . toString = function ( ) {
return this ( ) ;
} ;
/ * \
* Savage . filter . hueRotate
[ method ]
* *
* Returns string of the hue - rotate filter .
* *
- angle ( number ) angle of rotation .
= ( string ) filter representation
\ * /
Savage . filter . hueRotate = function ( angle ) {
angle = angle || 0 ;
return Savage . format ( '<feColorMatrix type="hueRotate" values="{angle}"/>' , {
angle : angle
} ) ;
} ;
Savage . filter . hueRotate . toString = function ( ) {
return this ( ) ;
} ;
/ * \
* Savage . filter . invert
[ method ]
* *
* Returns string of the invert filter .
* *
- amount ( number ) amount of filter ( ` 0..1 ` ) .
= ( string ) filter representation
\ * /
Savage . filter . invert = function ( amount ) {
if ( amount == null ) {
amount = 1 ;
}
return Savage . 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
} ) ;
} ;
Savage . filter . invert . toString = function ( ) {
return this ( ) ;
} ;
/ * \
* Savage . filter . brightness
[ method ]
* *
* Returns string of the brightness filter .
* *
- amount ( number ) amount of filter ( ` 0..1 ` ) .
= ( string ) filter representation
\ * /
Savage . filter . brightness = function ( amount ) {
if ( amount == null ) {
amount = 1 ;
}
return Savage . format ( '<feComponentTransfer><feFuncR type="linear" slope="{amount}"/><feFuncG type="linear" slope="{amount}"/><feFuncB type="linear" slope="{amount}"/></feComponentTransfer>' , {
amount : amount
} ) ;
} ;
Savage . filter . brightness . toString = function ( ) {
return this ( ) ;
} ;
/ * \
* Savage . filter . contrast
[ method ]
* *
* Returns string of the contrast filter .
* *
- amount ( number ) amount of filter ( ` 0..1 ` ) .
= ( string ) filter representation
\ * /
Savage . filter . contrast = function ( amount ) {
if ( amount == null ) {
amount = 1 ;
}
return Savage . 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 : . 5 - amount / 2
} ) ;
} ;
Savage . filter . contrast . toString = function ( ) {
return this ( ) ;
} ;
2013-08-05 08:04:30 +00:00
} ) ;