2015-09-26 07:51:26 -07:00
/*! VelocityJS.org (1.2.3). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */
2015-06-26 08:53:49 -07:00
/ * * * * * * * * * * * * * * * * * * * * * * * * *
Velocity jQuery Shim
* * * * * * * * * * * * * * * * * * * * * * * * * /
/*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */
/* This file contains the jQuery functions that Velocity relies on, thereby removing Velocity's dependency on a full copy of jQuery, and allowing it to work in any environment. */
/* These shimmed functions are only used if jQuery isn't present. If both this shim and jQuery are loaded, Velocity defaults to jQuery proper. */
/* Browser support: Using this shim instead of jQuery proper removes support for IE8. */
; ( function ( window ) {
/ * * * * * * * * * * * * * * *
Setup
* * * * * * * * * * * * * * * /
/* If jQuery is already loaded, there's no point in loading this shim. */
if ( window . jQuery ) {
return ;
}
/* jQuery base. */
var $ = function ( selector , context ) {
return new $ . fn . init ( selector , context ) ;
} ;
/ * * * * * * * * * * * * * * * * * * * *
Private Methods
* * * * * * * * * * * * * * * * * * * * /
/* jQuery */
$ . isWindow = function ( obj ) {
/* jshint eqeqeq: false */
return obj != null && obj == obj . window ;
} ;
/* jQuery */
$ . type = function ( obj ) {
if ( obj == null ) {
return obj + "" ;
}
return typeof obj === "object" || typeof obj === "function" ?
class2type [ toString . call ( obj ) ] || "object" :
typeof obj ;
} ;
/* jQuery */
$ . isArray = Array . isArray || function ( obj ) {
return $ . type ( obj ) === "array" ;
} ;
/* jQuery */
function isArraylike ( obj ) {
var length = obj . length ,
type = $ . type ( obj ) ;
if ( type === "function" || $ . isWindow ( obj ) ) {
return false ;
}
if ( obj . nodeType === 1 && length ) {
return true ;
}
return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj ;
}
/ * * * * * * * * * * * * * * *
$ Methods
* * * * * * * * * * * * * * * /
/* jQuery: Support removed for IE<9. */
$ . isPlainObject = function ( obj ) {
var key ;
if ( ! obj || $ . type ( obj ) !== "object" || obj . nodeType || $ . isWindow ( obj ) ) {
return false ;
}
try {
if ( obj . constructor &&
! hasOwn . call ( obj , "constructor" ) &&
! hasOwn . call ( obj . constructor . prototype , "isPrototypeOf" ) ) {
return false ;
}
} catch ( e ) {
return false ;
}
for ( key in obj ) { }
return key === undefined || hasOwn . call ( obj , key ) ;
} ;
/* jQuery */
$ . each = function ( obj , callback , args ) {
var value ,
i = 0 ,
length = obj . length ,
isArray = isArraylike ( obj ) ;
if ( args ) {
if ( isArray ) {
for ( ; i < length ; i ++ ) {
value = callback . apply ( obj [ i ] , args ) ;
if ( value === false ) {
break ;
}
}
} else {
for ( i in obj ) {
value = callback . apply ( obj [ i ] , args ) ;
if ( value === false ) {
break ;
}
}
}
} else {
if ( isArray ) {
for ( ; i < length ; i ++ ) {
value = callback . call ( obj [ i ] , i , obj [ i ] ) ;
if ( value === false ) {
break ;
}
}
} else {
for ( i in obj ) {
value = callback . call ( obj [ i ] , i , obj [ i ] ) ;
if ( value === false ) {
break ;
}
}
}
}
return obj ;
} ;
/* Custom */
$ . data = function ( node , key , value ) {
/* $.getData() */
if ( value === undefined ) {
var id = node [ $ . expando ] ,
store = id && cache [ id ] ;
if ( key === undefined ) {
return store ;
} else if ( store ) {
if ( key in store ) {
return store [ key ] ;
}
}
/* $.setData() */
} else if ( key !== undefined ) {
var id = node [ $ . expando ] || ( node [ $ . expando ] = ++ $ . uuid ) ;
cache [ id ] = cache [ id ] || { } ;
cache [ id ] [ key ] = value ;
return value ;
}
} ;
/* Custom */
$ . removeData = function ( node , keys ) {
var id = node [ $ . expando ] ,
store = id && cache [ id ] ;
if ( store ) {
$ . each ( keys , function ( _ , key ) {
delete store [ key ] ;
} ) ;
}
} ;
/* jQuery */
$ . extend = function ( ) {
var src , copyIsArray , copy , name , options , clone ,
target = arguments [ 0 ] || { } ,
i = 1 ,
length = arguments . length ,
deep = false ;
if ( typeof target === "boolean" ) {
deep = target ;
target = arguments [ i ] || { } ;
i ++ ;
}
if ( typeof target !== "object" && $ . type ( target ) !== "function" ) {
target = { } ;
}
if ( i === length ) {
target = this ;
i -- ;
}
for ( ; i < length ; i ++ ) {
if ( ( options = arguments [ i ] ) != null ) {
for ( name in options ) {
src = target [ name ] ;
copy = options [ name ] ;
if ( target === copy ) {
continue ;
}
if ( deep && copy && ( $ . isPlainObject ( copy ) || ( copyIsArray = $ . isArray ( copy ) ) ) ) {
if ( copyIsArray ) {
copyIsArray = false ;
clone = src && $ . isArray ( src ) ? src : [ ] ;
} else {
clone = src && $ . isPlainObject ( src ) ? src : { } ;
}
target [ name ] = $ . extend ( deep , clone , copy ) ;
} else if ( copy !== undefined ) {
target [ name ] = copy ;
}
}
}
}
return target ;
} ;
/* jQuery 1.4.3 */
$ . queue = function ( elem , type , data ) {
function $makeArray ( arr , results ) {
var ret = results || [ ] ;
if ( arr != null ) {
if ( isArraylike ( Object ( arr ) ) ) {
/* $.merge */
( function ( first , second ) {
var len = + second . length ,
j = 0 ,
i = first . length ;
while ( j < len ) {
first [ i ++ ] = second [ j ++ ] ;
}
if ( len !== len ) {
while ( second [ j ] !== undefined ) {
first [ i ++ ] = second [ j ++ ] ;
}
}
first . length = i ;
return first ;
} ) ( ret , typeof arr === "string" ? [ arr ] : arr ) ;
} else {
[ ] . push . call ( ret , arr ) ;
}
}
return ret ;
}
if ( ! elem ) {
return ;
}
type = ( type || "fx" ) + "queue" ;
var q = $ . data ( elem , type ) ;
if ( ! data ) {
return q || [ ] ;
}
if ( ! q || $ . isArray ( data ) ) {
q = $ . data ( elem , type , $makeArray ( data ) ) ;
} else {
q . push ( data ) ;
}
return q ;
} ;
/* jQuery 1.4.3 */
$ . dequeue = function ( elems , type ) {
/* Custom: Embed element iteration. */
$ . each ( elems . nodeType ? [ elems ] : elems , function ( i , elem ) {
type = type || "fx" ;
var queue = $ . queue ( elem , type ) ,
fn = queue . shift ( ) ;
if ( fn === "inprogress" ) {
fn = queue . shift ( ) ;
}
if ( fn ) {
if ( type === "fx" ) {
queue . unshift ( "inprogress" ) ;
}
fn . call ( elem , function ( ) {
$ . dequeue ( elem , type ) ;
} ) ;
}
} ) ;
} ;
/ * * * * * * * * * * * * * * * * * *
$ . fn Methods
* * * * * * * * * * * * * * * * * * /
/* jQuery */
$ . fn = $ . prototype = {
init : function ( selector ) {
/* Just return the element wrapped inside an array; don't proceed with the actual jQuery node wrapping process. */
if ( selector . nodeType ) {
this [ 0 ] = selector ;
return this ;
} else {
throw new Error ( "Not a DOM node." ) ;
}
} ,
offset : function ( ) {
/* jQuery altered code: Dropped disconnected DOM node checking. */
var box = this [ 0 ] . getBoundingClientRect ? this [ 0 ] . getBoundingClientRect ( ) : { top : 0 , left : 0 } ;
return {
top : box . top + ( window . pageYOffset || document . scrollTop || 0 ) - ( document . clientTop || 0 ) ,
left : box . left + ( window . pageXOffset || document . scrollLeft || 0 ) - ( document . clientLeft || 0 )
} ;
} ,
position : function ( ) {
/* jQuery */
function offsetParent ( ) {
var offsetParent = this . offsetParent || document ;
while ( offsetParent && ( ! offsetParent . nodeType . toLowerCase === "html" && offsetParent . style . position === "static" ) ) {
offsetParent = offsetParent . offsetParent ;
}
return offsetParent || document ;
}
/* Zepto */
var elem = this [ 0 ] ,
offsetParent = offsetParent . apply ( elem ) ,
offset = this . offset ( ) ,
parentOffset = /^(?:body|html)$/i . test ( offsetParent . nodeName ) ? { top : 0 , left : 0 } : $ ( offsetParent ) . offset ( )
offset . top -= parseFloat ( elem . style . marginTop ) || 0 ;
offset . left -= parseFloat ( elem . style . marginLeft ) || 0 ;
if ( offsetParent . style ) {
parentOffset . top += parseFloat ( offsetParent . style . borderTopWidth ) || 0
parentOffset . left += parseFloat ( offsetParent . style . borderLeftWidth ) || 0
}
return {
top : offset . top - parentOffset . top ,
left : offset . left - parentOffset . left
} ;
}
} ;
/ * * * * * * * * * * * * * * * * * * * * * *
Private Variables
* * * * * * * * * * * * * * * * * * * * * * /
/* For $.data() */
var cache = { } ;
$ . expando = "velocity" + ( new Date ( ) . getTime ( ) ) ;
$ . uuid = 0 ;
/* For $.queue() */
var class2type = { } ,
hasOwn = class2type . hasOwnProperty ,
toString = class2type . toString ;
var types = "Boolean Number String Function Array Date RegExp Object Error" . split ( " " ) ;
for ( var i = 0 ; i < types . length ; i ++ ) {
class2type [ "[object " + types [ i ] + "]" ] = types [ i ] . toLowerCase ( ) ;
}
/* Makes $(node) possible, without having to call init. */
$ . fn . init . prototype = $ . fn ;
/* Globalize Velocity onto the window, and assign its Utilities property. */
window . Velocity = { Utilities : $ } ;
} ) ( window ) ;
/ * * * * * * * * * * * * * * * * * *
Velocity . js
* * * * * * * * * * * * * * * * * * /
; ( function ( factory ) {
/* CommonJS module. */
if ( typeof module === "object" && typeof module . exports === "object" ) {
module . exports = factory ( ) ;
/* AMD module. */
} else if ( typeof define === "function" && define . amd ) {
define ( factory ) ;
/* Browser globals. */
} else {
factory ( ) ;
}
} ( function ( ) {
return function ( global , window , document , undefined ) {
/ * * * * * * * * * * * * * * *
Summary
* * * * * * * * * * * * * * * /
/ *
- CSS : CSS stack that works independently from the rest of Velocity .
- animate ( ) : Core animation method that iterates over the targeted elements and queues the incoming call onto each element individually .
- Pre - Queueing : Prepare the element for animation by instantiating its data cache and processing the call ' s options .
- Queueing : The logic that runs once the call has reached its point of execution in the element ' s $ . queue ( ) stack .
Most logic is placed here to avoid risking it becoming stale ( if the element ' s properties have changed ) .
- Pushing : Consolidation of the tween data followed by its push onto the global in - progress calls container .
- tick ( ) : The single requestAnimationFrame loop responsible for tweening all in - progress calls .
- completeCall ( ) : Handles the cleanup process for each Velocity call .
* /
/ * * * * * * * * * * * * * * * * * * * * *
Helper Functions
* * * * * * * * * * * * * * * * * * * * * /
/* IE detection. Gist: https://gist.github.com/julianshapiro/9098609 */
var IE = ( function ( ) {
if ( document . documentMode ) {
return document . documentMode ;
} else {
for ( var i = 7 ; i > 4 ; i -- ) {
var div = document . createElement ( "div" ) ;
div . innerHTML = "<!--[if IE " + i + "]><span></span><![endif]-->" ;
if ( div . getElementsByTagName ( "span" ) . length ) {
div = null ;
return i ;
}
}
}
return undefined ;
} ) ( ) ;
/* rAF shim. Gist: https://gist.github.com/julianshapiro/9497513 */
var rAFShim = ( function ( ) {
var timeLast = 0 ;
return window . webkitRequestAnimationFrame || window . mozRequestAnimationFrame || function ( callback ) {
var timeCurrent = ( new Date ( ) ) . getTime ( ) ,
timeDelta ;
/* Dynamically set delay on a per-tick basis to match 60fps. */
/* Technique by Erik Moller. MIT license: https://gist.github.com/paulirish/1579671 */
timeDelta = Math . max ( 0 , 16 - ( timeCurrent - timeLast ) ) ;
timeLast = timeCurrent + timeDelta ;
return setTimeout ( function ( ) { callback ( timeCurrent + timeDelta ) ; } , timeDelta ) ;
} ;
} ) ( ) ;
/* Array compacting. Copyright Lo-Dash. MIT License: https://github.com/lodash/lodash/blob/master/LICENSE.txt */
function compactSparseArray ( array ) {
var index = - 1 ,
length = array ? array . length : 0 ,
result = [ ] ;
while ( ++ index < length ) {
var value = array [ index ] ;
if ( value ) {
result . push ( value ) ;
}
}
return result ;
}
function sanitizeElements ( elements ) {
/* Unwrap jQuery/Zepto objects. */
if ( Type . isWrapped ( elements ) ) {
elements = [ ] . slice . call ( elements ) ;
/* Wrap a single element in an array so that $.each() can iterate with the element instead of its node's children. */
} else if ( Type . isNode ( elements ) ) {
elements = [ elements ] ;
}
return elements ;
}
var Type = {
isString : function ( variable ) {
return ( typeof variable === "string" ) ;
} ,
isArray : Array . isArray || function ( variable ) {
return Object . prototype . toString . call ( variable ) === "[object Array]" ;
} ,
isFunction : function ( variable ) {
return Object . prototype . toString . call ( variable ) === "[object Function]" ;
} ,
isNode : function ( variable ) {
return variable && variable . nodeType ;
} ,
/* Copyright Martin Bohm. MIT License: https://gist.github.com/Tomalak/818a78a226a0738eaade */
isNodeList : function ( variable ) {
return typeof variable === "object" &&
/^\[object (HTMLCollection|NodeList|Object)\]$/ . test ( Object . prototype . toString . call ( variable ) ) &&
variable . length !== undefined &&
( variable . length === 0 || ( typeof variable [ 0 ] === "object" && variable [ 0 ] . nodeType > 0 ) ) ;
} ,
/* Determine if variable is a wrapped jQuery or Zepto element. */
isWrapped : function ( variable ) {
return variable && ( variable . jquery || ( window . Zepto && window . Zepto . zepto . isZ ( variable ) ) ) ;
} ,
isSVG : function ( variable ) {
return window . SVGElement && ( variable instanceof window . SVGElement ) ;
} ,
isEmptyObject : function ( variable ) {
for ( var name in variable ) {
return false ;
}
return true ;
}
} ;
/ * * * * * * * * * * * * * * * * *
Dependencies
* * * * * * * * * * * * * * * * * /
var $ ,
isJQuery = false ;
if ( global . fn && global . fn . jquery ) {
$ = global ;
isJQuery = true ;
} else {
$ = window . Velocity . Utilities ;
}
if ( IE <= 8 && ! isJQuery ) {
throw new Error ( "Velocity: IE8 and below require jQuery to be loaded before Velocity." ) ;
} else if ( IE <= 7 ) {
/* Revert to jQuery's $.animate(), and lose Velocity's extra features. */
jQuery . fn . velocity = jQuery . fn . animate ;
/* Now that $.fn.velocity is aliased, abort this Velocity declaration. */
return ;
}
/ * * * * * * * * * * * * * * * * *
Constants
* * * * * * * * * * * * * * * * * /
var DURATION _DEFAULT = 400 ,
EASING _DEFAULT = "swing" ;
/ * * * * * * * * * * * * *
State
* * * * * * * * * * * * * /
var Velocity = {
/* Container for page-wide Velocity state data. */
State : {
/* Detect mobile devices to determine if mobileHA should be turned on. */
isMobile : /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i . test ( navigator . userAgent ) ,
/* The mobileHA option's behavior changes on older Android devices (Gingerbread, versions 2.3.3-2.3.7). */
isAndroid : /Android/i . test ( navigator . userAgent ) ,
isGingerbread : /Android 2\.3\.[3-7]/i . test ( navigator . userAgent ) ,
isChrome : window . chrome ,
isFirefox : /Firefox/i . test ( navigator . userAgent ) ,
/* Create a cached element for re-use when checking for CSS property prefixes. */
prefixElement : document . createElement ( "div" ) ,
/* Cache every prefix match to avoid repeating lookups. */
prefixMatches : { } ,
/* Cache the anchor used for animating window scrolling. */
scrollAnchor : null ,
/* Cache the browser-specific property names associated with the scroll anchor. */
scrollPropertyLeft : null ,
scrollPropertyTop : null ,
/* Keep track of whether our RAF tick is running. */
isTicking : false ,
/* Container for every in-progress call to Velocity. */
calls : [ ]
} ,
/* Velocity's custom CSS stack. Made global for unit testing. */
CSS : { /* Defined below. */ } ,
/* A shim of the jQuery utility functions used by Velocity -- provided by Velocity's optional jQuery shim. */
Utilities : $ ,
/* Container for the user's custom animation redirects that are referenced by name in place of the properties map argument. */
Redirects : { /* Manually registered by the user. */ } ,
Easings : { /* Defined below. */ } ,
/* Attempt to use ES6 Promises by default. Users can override this with a third-party promises library. */
Promise : window . Promise ,
/* Velocity option defaults, which can be overriden by the user. */
defaults : {
queue : "" ,
duration : DURATION _DEFAULT ,
easing : EASING _DEFAULT ,
begin : undefined ,
complete : undefined ,
progress : undefined ,
display : undefined ,
visibility : undefined ,
loop : false ,
delay : false ,
mobileHA : true ,
/* Advanced: Set to false to prevent property values from being cached between consecutive Velocity-initiated chain calls. */
_cacheValues : true
} ,
/* A design goal of Velocity is to cache data wherever possible in order to avoid DOM requerying. Accordingly, each element has a data cache. */
init : function ( element ) {
$ . data ( element , "velocity" , {
/* Store whether this is an SVG element, since its properties are retrieved and updated differently than standard HTML elements. */
isSVG : Type . isSVG ( element ) ,
/ * K e e p t r a c k o f w h e t h e r t h e e l e m e n t i s c u r r e n t l y b e i n g a n i m a t e d b y V e l o c i t y .
This is used to ensure that property values are not transferred between non - consecutive ( stale ) calls . * /
isAnimating : false ,
/* A reference to the element's live computedStyle object. Learn more here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */
computedStyle : null ,
/ * T w e e n d a t a i s c a c h e d f o r e a c h a n i m a t i o n o n t h e e l e m e n t s o t h a t d a t a c a n b e p a s s e d a c r o s s c a l l s - -
in particular , end values are used as subsequent start values in consecutive Velocity calls . * /
tweensContainer : null ,
/ * T h e f u l l r o o t p r o p e r t y v a l u e s o f e a c h C S S h o o k b e i n g a n i m a t e d o n t h i s e l e m e n t a r e c a c h e d s o t h a t :
1 ) Concurrently - animating hooks sharing the same root can have their root values ' merged into one while tweening .
2 ) Post - hook - injection root values can be transferred over to consecutively chained Velocity calls as starting root values . * /
rootPropertyValueCache : { } ,
/* A cache for transform updates, which must be manually flushed via CSS.flushTransformCache(). */
transformCache : { }
} ) ;
} ,
/* A parallel to jQuery's $.css(), used for getting/setting Velocity's hooked CSS properties. */
hook : null , /* Defined below. */
/* Velocity-wide animation time remapping for testing purposes. */
mock : false ,
version : { major : 1 , minor : 2 , patch : 2 } ,
/* Set to 1 or 2 (most verbose) to output debug info to console. */
debug : false
} ;
/* Retrieve the appropriate scroll anchor and property name for the browser: https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY */
if ( window . pageYOffset !== undefined ) {
Velocity . State . scrollAnchor = window ;
Velocity . State . scrollPropertyLeft = "pageXOffset" ;
Velocity . State . scrollPropertyTop = "pageYOffset" ;
} else {
Velocity . State . scrollAnchor = document . documentElement || document . body . parentNode || document . body ;
Velocity . State . scrollPropertyLeft = "scrollLeft" ;
Velocity . State . scrollPropertyTop = "scrollTop" ;
}
/* Shorthand alias for jQuery's $.data() utility. */
function Data ( element ) {
/* Hardcode a reference to the plugin name. */
var response = $ . data ( element , "velocity" ) ;
/* jQuery <=1.4.2 returns null instead of undefined when no match is found. We normalize this behavior. */
return response === null ? undefined : response ;
} ;
/ * * * * * * * * * * * * * *
Easing
* * * * * * * * * * * * * * /
/* Step easing generator. */
function generateStep ( steps ) {
return function ( p ) {
return Math . round ( p * steps ) * ( 1 / steps ) ;
} ;
}
/* Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
function generateBezier ( mX1 , mY1 , mX2 , mY2 ) {
var NEWTON _ITERATIONS = 4 ,
NEWTON _MIN _SLOPE = 0.001 ,
SUBDIVISION _PRECISION = 0.0000001 ,
SUBDIVISION _MAX _ITERATIONS = 10 ,
kSplineTableSize = 11 ,
kSampleStepSize = 1.0 / ( kSplineTableSize - 1.0 ) ,
float32ArraySupported = "Float32Array" in window ;
/* Must contain four arguments. */
if ( arguments . length !== 4 ) {
return false ;
}
/* Arguments must be numbers. */
for ( var i = 0 ; i < 4 ; ++ i ) {
if ( typeof arguments [ i ] !== "number" || isNaN ( arguments [ i ] ) || ! isFinite ( arguments [ i ] ) ) {
return false ;
}
}
/* X values must be in the [0, 1] range. */
mX1 = Math . min ( mX1 , 1 ) ;
mX2 = Math . min ( mX2 , 1 ) ;
mX1 = Math . max ( mX1 , 0 ) ;
mX2 = Math . max ( mX2 , 0 ) ;
var mSampleValues = float32ArraySupported ? new Float32Array ( kSplineTableSize ) : new Array ( kSplineTableSize ) ;
function A ( aA1 , aA2 ) { return 1.0 - 3.0 * aA2 + 3.0 * aA1 ; }
function B ( aA1 , aA2 ) { return 3.0 * aA2 - 6.0 * aA1 ; }
function C ( aA1 ) { return 3.0 * aA1 ; }
function calcBezier ( aT , aA1 , aA2 ) {
return ( ( A ( aA1 , aA2 ) * aT + B ( aA1 , aA2 ) ) * aT + C ( aA1 ) ) * aT ;
}
function getSlope ( aT , aA1 , aA2 ) {
return 3.0 * A ( aA1 , aA2 ) * aT * aT + 2.0 * B ( aA1 , aA2 ) * aT + C ( aA1 ) ;
}
function newtonRaphsonIterate ( aX , aGuessT ) {
for ( var i = 0 ; i < NEWTON _ITERATIONS ; ++ i ) {
var currentSlope = getSlope ( aGuessT , mX1 , mX2 ) ;
if ( currentSlope === 0.0 ) return aGuessT ;
var currentX = calcBezier ( aGuessT , mX1 , mX2 ) - aX ;
aGuessT -= currentX / currentSlope ;
}
return aGuessT ;
}
function calcSampleValues ( ) {
for ( var i = 0 ; i < kSplineTableSize ; ++ i ) {
mSampleValues [ i ] = calcBezier ( i * kSampleStepSize , mX1 , mX2 ) ;
}
}
function binarySubdivide ( aX , aA , aB ) {
var currentX , currentT , i = 0 ;
do {
currentT = aA + ( aB - aA ) / 2.0 ;
currentX = calcBezier ( currentT , mX1 , mX2 ) - aX ;
if ( currentX > 0.0 ) {
aB = currentT ;
} else {
aA = currentT ;
}
} while ( Math . abs ( currentX ) > SUBDIVISION _PRECISION && ++ i < SUBDIVISION _MAX _ITERATIONS ) ;
return currentT ;
}
function getTForX ( aX ) {
var intervalStart = 0.0 ,
currentSample = 1 ,
lastSample = kSplineTableSize - 1 ;
for ( ; currentSample != lastSample && mSampleValues [ currentSample ] <= aX ; ++ currentSample ) {
intervalStart += kSampleStepSize ;
}
-- currentSample ;
var dist = ( aX - mSampleValues [ currentSample ] ) / ( mSampleValues [ currentSample + 1 ] - mSampleValues [ currentSample ] ) ,
guessForT = intervalStart + dist * kSampleStepSize ,
initialSlope = getSlope ( guessForT , mX1 , mX2 ) ;
if ( initialSlope >= NEWTON _MIN _SLOPE ) {
return newtonRaphsonIterate ( aX , guessForT ) ;
} else if ( initialSlope == 0.0 ) {
return guessForT ;
} else {
return binarySubdivide ( aX , intervalStart , intervalStart + kSampleStepSize ) ;
}
}
var _precomputed = false ;
function precompute ( ) {
_precomputed = true ;
if ( mX1 != mY1 || mX2 != mY2 ) calcSampleValues ( ) ;
}
var f = function ( aX ) {
if ( ! _precomputed ) precompute ( ) ;
if ( mX1 === mY1 && mX2 === mY2 ) return aX ;
if ( aX === 0 ) return 0 ;
if ( aX === 1 ) return 1 ;
return calcBezier ( getTForX ( aX ) , mY1 , mY2 ) ;
} ;
f . getControlPoints = function ( ) { return [ { x : mX1 , y : mY1 } , { x : mX2 , y : mY2 } ] ; } ;
var str = "generateBezier(" + [ mX1 , mY1 , mX2 , mY2 ] + ")" ;
f . toString = function ( ) { return str ; } ;
return f ;
}
/* Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
/ * G i v e n a t e n s i o n , f r i c t i o n , a n d d u r a t i o n , a s i m u l a t i o n a t 6 0 F P S w i l l f i r s t r u n w i t h o u t a d e f i n e d d u r a t i o n i n o r d e r t o c a l c u l a t e t h e f u l l p a t h . A s e c o n d p a s s
then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration - constrained animation . * /
var generateSpringRK4 = ( function ( ) {
function springAccelerationForState ( state ) {
return ( - state . tension * state . x ) - ( state . friction * state . v ) ;
}
function springEvaluateStateWithDerivative ( initialState , dt , derivative ) {
var state = {
x : initialState . x + derivative . dx * dt ,
v : initialState . v + derivative . dv * dt ,
tension : initialState . tension ,
friction : initialState . friction
} ;
return { dx : state . v , dv : springAccelerationForState ( state ) } ;
}
function springIntegrateState ( state , dt ) {
var a = {
dx : state . v ,
dv : springAccelerationForState ( state )
} ,
b = springEvaluateStateWithDerivative ( state , dt * 0.5 , a ) ,
c = springEvaluateStateWithDerivative ( state , dt * 0.5 , b ) ,
d = springEvaluateStateWithDerivative ( state , dt , c ) ,
dxdt = 1.0 / 6.0 * ( a . dx + 2.0 * ( b . dx + c . dx ) + d . dx ) ,
dvdt = 1.0 / 6.0 * ( a . dv + 2.0 * ( b . dv + c . dv ) + d . dv ) ;
state . x = state . x + dxdt * dt ;
state . v = state . v + dvdt * dt ;
return state ;
}
return function springRK4Factory ( tension , friction , duration ) {
var initState = {
x : - 1 ,
v : 0 ,
tension : null ,
friction : null
} ,
path = [ 0 ] ,
time _lapsed = 0 ,
tolerance = 1 / 10000 ,
DT = 16 / 1000 ,
have _duration , dt , last _state ;
tension = parseFloat ( tension ) || 500 ;
friction = parseFloat ( friction ) || 20 ;
duration = duration || null ;
initState . tension = tension ;
initState . friction = friction ;
have _duration = duration !== null ;
/* Calculate the actual time it takes for this animation to complete with the provided conditions. */
if ( have _duration ) {
/* Run the simulation without a duration. */
time _lapsed = springRK4Factory ( tension , friction ) ;
/* Compute the adjusted time delta. */
dt = time _lapsed / duration * DT ;
} else {
dt = DT ;
}
while ( true ) {
/* Next/step function .*/
last _state = springIntegrateState ( last _state || initState , dt ) ;
/* Store the position. */
path . push ( 1 + last _state . x ) ;
time _lapsed += 16 ;
/* If the change threshold is reached, break. */
if ( ! ( Math . abs ( last _state . x ) > tolerance && Math . abs ( last _state . v ) > tolerance ) ) {
break ;
}
}
/ * I f d u r a t i o n i s n o t d e f i n e d , r e t u r n t h e a c t u a l t i m e r e q u i r e d f o r c o m p l e t i n g t h i s a n i m a t i o n . O t h e r w i s e , r e t u r n a c l o s u r e t h a t h o l d s t h e
computed path and returns a snapshot of the position according to a given percentComplete . * /
return ! have _duration ? time _lapsed : function ( percentComplete ) { return path [ ( percentComplete * ( path . length - 1 ) ) | 0 ] ; } ;
} ;
} ( ) ) ;
/* jQuery easings. */
Velocity . Easings = {
linear : function ( p ) { return p ; } ,
swing : function ( p ) { return 0.5 - Math . cos ( p * Math . PI ) / 2 } ,
/* Bonus "spring" easing, which is a less exaggerated version of easeInOutElastic. */
spring : function ( p ) { return 1 - ( Math . cos ( p * 4.5 * Math . PI ) * Math . exp ( - p * 6 ) ) ; }
} ;
/* CSS3 and Robert Penner easings. */
$ . each (
[
[ "ease" , [ 0.25 , 0.1 , 0.25 , 1.0 ] ] ,
[ "ease-in" , [ 0.42 , 0.0 , 1.00 , 1.0 ] ] ,
[ "ease-out" , [ 0.00 , 0.0 , 0.58 , 1.0 ] ] ,
[ "ease-in-out" , [ 0.42 , 0.0 , 0.58 , 1.0 ] ] ,
[ "easeInSine" , [ 0.47 , 0 , 0.745 , 0.715 ] ] ,
[ "easeOutSine" , [ 0.39 , 0.575 , 0.565 , 1 ] ] ,
[ "easeInOutSine" , [ 0.445 , 0.05 , 0.55 , 0.95 ] ] ,
[ "easeInQuad" , [ 0.55 , 0.085 , 0.68 , 0.53 ] ] ,
[ "easeOutQuad" , [ 0.25 , 0.46 , 0.45 , 0.94 ] ] ,
[ "easeInOutQuad" , [ 0.455 , 0.03 , 0.515 , 0.955 ] ] ,
[ "easeInCubic" , [ 0.55 , 0.055 , 0.675 , 0.19 ] ] ,
[ "easeOutCubic" , [ 0.215 , 0.61 , 0.355 , 1 ] ] ,
[ "easeInOutCubic" , [ 0.645 , 0.045 , 0.355 , 1 ] ] ,
[ "easeInQuart" , [ 0.895 , 0.03 , 0.685 , 0.22 ] ] ,
[ "easeOutQuart" , [ 0.165 , 0.84 , 0.44 , 1 ] ] ,
[ "easeInOutQuart" , [ 0.77 , 0 , 0.175 , 1 ] ] ,
[ "easeInQuint" , [ 0.755 , 0.05 , 0.855 , 0.06 ] ] ,
[ "easeOutQuint" , [ 0.23 , 1 , 0.32 , 1 ] ] ,
[ "easeInOutQuint" , [ 0.86 , 0 , 0.07 , 1 ] ] ,
[ "easeInExpo" , [ 0.95 , 0.05 , 0.795 , 0.035 ] ] ,
[ "easeOutExpo" , [ 0.19 , 1 , 0.22 , 1 ] ] ,
[ "easeInOutExpo" , [ 1 , 0 , 0 , 1 ] ] ,
[ "easeInCirc" , [ 0.6 , 0.04 , 0.98 , 0.335 ] ] ,
[ "easeOutCirc" , [ 0.075 , 0.82 , 0.165 , 1 ] ] ,
[ "easeInOutCirc" , [ 0.785 , 0.135 , 0.15 , 0.86 ] ]
] , function ( i , easingArray ) {
Velocity . Easings [ easingArray [ 0 ] ] = generateBezier . apply ( null , easingArray [ 1 ] ) ;
} ) ;
/* Determine the appropriate easing type given an easing input. */
function getEasing ( value , duration ) {
var easing = value ;
/ * T h e e a s i n g o p t i o n c a n e i t h e r b e a s t r i n g t h a t r e f e r e n c e s a p r e - r e g i s t e r e d e a s i n g ,
or it can be a two - / f o u r - i t e m a r r a y o f i n t e g e r s t o b e c o n v e r t e d i n t o a b e z i e r / s p r i n g f u n c t i o n . * /
if ( Type . isString ( value ) ) {
/* Ensure that the easing has been assigned to jQuery's Velocity.Easings object. */
if ( ! Velocity . Easings [ value ] ) {
easing = false ;
}
} else if ( Type . isArray ( value ) && value . length === 1 ) {
easing = generateStep . apply ( null , value ) ;
} else if ( Type . isArray ( value ) && value . length === 2 ) {
/* springRK4 must be passed the animation's duration. */
/ * N o t e : I f t h e s p r i n g R K 4 a r r a y c o n t a i n s n o n - n u m b e r s , g e n e r a t e S p r i n g R K 4 ( ) r e t u r n s a n e a s i n g
function generated with default tension and friction values . * /
easing = generateSpringRK4 . apply ( null , value . concat ( [ duration ] ) ) ;
} else if ( Type . isArray ( value ) && value . length === 4 ) {
/* Note: If the bezier array contains non-numbers, generateBezier() returns false. */
easing = generateBezier . apply ( null , value ) ;
} else {
easing = false ;
}
/ * R e v e r t t o t h e V e l o c i t y - w i d e d e f a u l t e a s i n g t y p e , o r f a l l b a c k t o " s w i n g " ( w h i c h i s a l s o j Q u e r y ' s d e f a u l t )
if the Velocity - wide default has been incorrectly modified . * /
if ( easing === false ) {
if ( Velocity . Easings [ Velocity . defaults . easing ] ) {
easing = Velocity . defaults . easing ;
} else {
easing = EASING _DEFAULT ;
}
}
return easing ;
}
/ * * * * * * * * * * * * * * * * *
CSS Stack
* * * * * * * * * * * * * * * * * /
/ * T h e C S S o b j e c t i s a h i g h l y c o n d e n s e d a n d p e r f o r m a n t C S S s t a c k t h a t f u l l y r e p l a c e s j Q u e r y ' s .
It handles the validation , getting , and setting of both standard CSS properties and CSS property hooks . * /
/* Note: A "CSS" shorthand is aliased so that our code is easier to read. */
var CSS = Velocity . CSS = {
/ * * * * * * * * * * * * *
RegEx
* * * * * * * * * * * * * /
RegEx : {
isHex : /^#([A-f\d]{3}){1,2}$/i ,
/* Unwrap a property value's surrounding text, e.g. "rgba(4, 3, 2, 1)" ==> "4, 3, 2, 1" and "rect(4px 3px 2px 1px)" ==> "4px 3px 2px 1px". */
valueUnwrap : /^[A-z]+\((.*)\)$/i ,
wrappedValueAlreadyExtracted : /[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/ ,
/* Split a multi-value property into an array of subvalues, e.g. "rgba(4, 3, 2, 1) 4px 3px 2px 1px" ==> [ "rgba(4, 3, 2, 1)", "4px", "3px", "2px", "1px" ]. */
valueSplit : /([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/ig
} ,
/ * * * * * * * * * * * *
Lists
* * * * * * * * * * * * /
Lists : {
colors : [ "fill" , "stroke" , "stopColor" , "color" , "backgroundColor" , "borderColor" , "borderTopColor" , "borderRightColor" , "borderBottomColor" , "borderLeftColor" , "outlineColor" ] ,
transformsBase : [ "translateX" , "translateY" , "scale" , "scaleX" , "scaleY" , "skewX" , "skewY" , "rotateZ" ] ,
transforms3D : [ "transformPerspective" , "translateZ" , "scaleZ" , "rotateX" , "rotateY" ]
} ,
/ * * * * * * * * * * * *
Hooks
* * * * * * * * * * * * /
/ * H o o k s a l l o w a s u b p r o p e r t y ( e . g . " b o x S h a d o w B l u r " ) o f a c o m p o u n d - v a l u e C S S p r o p e r t y
( e . g . "boxShadow: X Y Blur Spread Color" ) to be animated as if it were a discrete property . * /
/ * N o t e : B e y o n d e n a b l i n g f i n e - g r a i n e d p r o p e r t y a n i m a t i o n , h o o k i n g i s n e c e s s a r y s i n c e V e l o c i t y o n l y
tweens properties with single numeric values ; unlike CSS transitions , Velocity does not interpolate compound - values . * /
Hooks : {
/ * * * * * * * * * * * * * * * * * * * *
Registration
* * * * * * * * * * * * * * * * * * * * /
/* Templates are a concise way of indicating which subproperties must be individually registered for each compound-value CSS property. */
/* Each template consists of the compound-value's base name, its constituent subproperty names, and those subproperties' default values. */
templates : {
"textShadow" : [ "Color X Y Blur" , "black 0px 0px 0px" ] ,
"boxShadow" : [ "Color X Y Blur Spread" , "black 0px 0px 0px 0px" ] ,
"clip" : [ "Top Right Bottom Left" , "0px 0px 0px 0px" ] ,
"backgroundPosition" : [ "X Y" , "0% 0%" ] ,
"transformOrigin" : [ "X Y Z" , "50% 50% 0px" ] ,
"perspectiveOrigin" : [ "X Y" , "50% 50%" ]
} ,
/ * A " r e g i s t e r e d " h o o k i s o n e t h a t h a s b e e n c o n v e r t e d f r o m i t s t e m p l a t e f o r m i n t o a l i v e ,
tweenable property . It contains data to associate it with its root property . * /
registered : {
/ * N o t e : A r e g i s t e r e d h o o k l o o k s l i k e t h i s = = > t e x t S h a d o w B l u r : [ " t e x t S h a d o w " , 3 ] ,
which consists of the subproperty 's name, the associated root property' s name ,
and the subproperty 's position in the root' s value . * /
} ,
/* Convert the templates into individual hooks then append them to the registered object above. */
register : function ( ) {
/ * C o l o r h o o k s r e g i s t r a t i o n : C o l o r s a r e d e f a u l t e d t o w h i t e - - a s o p p o s e d t o b l a c k - - s i n c e c o l o r s t h a t a r e
currently set to "transparent" default to their respective template below when color - animated ,
and white is typically a closer match to transparent than black is . An exception is made for text ( "color" ) ,
which is almost always set closer to black than white . * /
for ( var i = 0 ; i < CSS . Lists . colors . length ; i ++ ) {
var rgbComponents = ( CSS . Lists . colors [ i ] === "color" ) ? "0 0 0 1" : "255 255 255 1" ;
CSS . Hooks . templates [ CSS . Lists . colors [ i ] ] = [ "Red Green Blue Alpha" , rgbComponents ] ;
}
var rootProperty ,
hookTemplate ,
hookNames ;
/ * I n I E , c o l o r v a l u e s i n s i d e c o m p o u n d - v a l u e p r o p e r t i e s a r e p o s i t i o n e d a t t h e e n d t h e v a l u e i n s t e a d o f a t t h e b e g i n n i n g .
Thus , we re - arrange the templates accordingly . * /
if ( IE ) {
for ( rootProperty in CSS . Hooks . templates ) {
hookTemplate = CSS . Hooks . templates [ rootProperty ] ;
hookNames = hookTemplate [ 0 ] . split ( " " ) ;
var defaultValues = hookTemplate [ 1 ] . match ( CSS . RegEx . valueSplit ) ;
if ( hookNames [ 0 ] === "Color" ) {
/* Reposition both the hook's name and its default value to the end of their respective strings. */
hookNames . push ( hookNames . shift ( ) ) ;
defaultValues . push ( defaultValues . shift ( ) ) ;
/* Replace the existing template for the hook's root property. */
CSS . Hooks . templates [ rootProperty ] = [ hookNames . join ( " " ) , defaultValues . join ( " " ) ] ;
}
}
}
/* Hook registration. */
for ( rootProperty in CSS . Hooks . templates ) {
hookTemplate = CSS . Hooks . templates [ rootProperty ] ;
hookNames = hookTemplate [ 0 ] . split ( " " ) ;
for ( var i in hookNames ) {
var fullHookName = rootProperty + hookNames [ i ] ,
hookPosition = i ;
/ * F o r e a c h h o o k , r e g i s t e r i t s f u l l n a m e ( e . g . t e x t S h a d o w B l u r ) w i t h i t s r o o t p r o p e r t y ( e . g . t e x t S h a d o w )
and the hook 's position in its template' s default value string . * /
CSS . Hooks . registered [ fullHookName ] = [ rootProperty , hookPosition ] ;
}
}
} ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Injection and Extraction
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Look up the root property associated with the hook (e.g. return "textShadow" for "textShadowBlur"). */
/* Since a hook cannot be set directly (the browser won't recognize it), style updating for hooks is routed through the hook's root property. */
getRoot : function ( property ) {
var hookData = CSS . Hooks . registered [ property ] ;
if ( hookData ) {
return hookData [ 0 ] ;
} else {
/* If there was no hook match, return the property name untouched. */
return property ;
}
} ,
/ * C o n v e r t a n y r o o t P r o p e r t y V a l u e , n u l l o r o t h e r w i s e , i n t o a s p a c e - d e l i m i t e d l i s t o f h o o k v a l u e s s o t h a t
the targeted hook can be injected or extracted at its standard position . * /
cleanRootPropertyValue : function ( rootProperty , rootPropertyValue ) {
/* If the rootPropertyValue is wrapped with "rgb()", "clip()", etc., remove the wrapping to normalize the value before manipulation. */
if ( CSS . RegEx . valueUnwrap . test ( rootPropertyValue ) ) {
rootPropertyValue = rootPropertyValue . match ( CSS . RegEx . valueUnwrap ) [ 1 ] ;
}
/ * I f r o o t P r o p e r t y V a l u e i s a C S S n u l l - v a l u e ( f r o m w h i c h t h e r e ' s i n h e r e n t l y n o h o o k v a l u e t o e x t r a c t ) ,
default to the root ' s default value as defined in CSS . Hooks . templates . * /
/ * N o t e : C S S n u l l - v a l u e s i n c l u d e " n o n e " , " a u t o " , a n d " t r a n s p a r e n t " . T h e y m u s t b e c o n v e r t e d i n t o t h e i r
zero - values ( e . g . textShadow : "none" == > textShadow : "0px 0px 0px black" ) for hook manipulation to proceed . * /
if ( CSS . Values . isCSSNullValue ( rootPropertyValue ) ) {
rootPropertyValue = CSS . Hooks . templates [ rootProperty ] [ 1 ] ;
}
return rootPropertyValue ;
} ,
/* Extracted the hook's value from its root property's value. This is used to get the starting value of an animating hook. */
extractValue : function ( fullHookName , rootPropertyValue ) {
var hookData = CSS . Hooks . registered [ fullHookName ] ;
if ( hookData ) {
var hookRoot = hookData [ 0 ] ,
hookPosition = hookData [ 1 ] ;
rootPropertyValue = CSS . Hooks . cleanRootPropertyValue ( hookRoot , rootPropertyValue ) ;
/* Split rootPropertyValue into its constituent hook values then grab the desired hook at its standard position. */
return rootPropertyValue . toString ( ) . match ( CSS . RegEx . valueSplit ) [ hookPosition ] ;
} else {
/* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */
return rootPropertyValue ;
}
} ,
/ * I n j e c t t h e h o o k ' s v a l u e i n t o i t s r o o t p r o p e r t y ' s v a l u e . T h i s i s u s e d t o p i e c e b a c k t o g e t h e r t h e r o o t p r o p e r t y
once Velocity has updated one of its individually hooked values through tweening . * /
injectValue : function ( fullHookName , hookValue , rootPropertyValue ) {
var hookData = CSS . Hooks . registered [ fullHookName ] ;
if ( hookData ) {
var hookRoot = hookData [ 0 ] ,
hookPosition = hookData [ 1 ] ,
rootPropertyValueParts ,
rootPropertyValueUpdated ;
rootPropertyValue = CSS . Hooks . cleanRootPropertyValue ( hookRoot , rootPropertyValue ) ;
/ * S p l i t r o o t P r o p e r t y V a l u e i n t o i t s i n d i v i d u a l h o o k v a l u e s , r e p l a c e t h e t a r g e t e d v a l u e w i t h h o o k V a l u e ,
then reconstruct the rootPropertyValue string . * /
rootPropertyValueParts = rootPropertyValue . toString ( ) . match ( CSS . RegEx . valueSplit ) ;
rootPropertyValueParts [ hookPosition ] = hookValue ;
rootPropertyValueUpdated = rootPropertyValueParts . join ( " " ) ;
return rootPropertyValueUpdated ;
} else {
/* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */
return rootPropertyValue ;
}
}
} ,
/ * * * * * * * * * * * * * * * * * * *
Normalizations
* * * * * * * * * * * * * * * * * * * /
/ * N o r m a l i z a t i o n s s t a n d a r d i z e C S S p r o p e r t y m a n i p u l a t i o n b y p o l l y f i l l i n g b r o w s e r - s p e c i f i c i m p l e m e n t a t i o n s ( e . g . o p a c i t y )
and reformatting special properties ( e . g . clip , rgba ) to look like standard ones . * /
Normalizations : {
/ * N o r m a l i z a t i o n s a r e p a s s e d a n o r m a l i z a t i o n t a r g e t ( e i t h e r t h e p r o p e r t y ' s n a m e , i t s e x t r a c t e d v a l u e , o r i t s i n j e c t e d v a l u e ) ,
the targeted element ( which may need to be queried ) , and the targeted property value . * /
registered : {
clip : function ( type , element , propertyValue ) {
switch ( type ) {
case "name" :
return "clip" ;
/* Clip needs to be unwrapped and stripped of its commas during extraction. */
case "extract" :
var extracted ;
/* If Velocity also extracted this value, skip extraction. */
if ( CSS . RegEx . wrappedValueAlreadyExtracted . test ( propertyValue ) ) {
extracted = propertyValue ;
} else {
/* Remove the "rect()" wrapper. */
extracted = propertyValue . toString ( ) . match ( CSS . RegEx . valueUnwrap ) ;
/* Strip off commas. */
extracted = extracted ? extracted [ 1 ] . replace ( /,(\s+)?/g , " " ) : propertyValue ;
}
return extracted ;
/* Clip needs to be re-wrapped during injection. */
case "inject" :
return "rect(" + propertyValue + ")" ;
}
} ,
blur : function ( type , element , propertyValue ) {
switch ( type ) {
case "name" :
return Velocity . State . isFirefox ? "filter" : "-webkit-filter" ;
case "extract" :
var extracted = parseFloat ( propertyValue ) ;
/* If extracted is NaN, meaning the value isn't already extracted. */
if ( ! ( extracted || extracted === 0 ) ) {
var blurComponent = propertyValue . toString ( ) . match ( /blur\(([0-9]+[A-z]+)\)/i ) ;
/* If the filter string had a blur component, return just the blur value and unit type. */
if ( blurComponent ) {
extracted = blurComponent [ 1 ] ;
/* If the component doesn't exist, default blur to 0. */
} else {
extracted = 0 ;
}
}
return extracted ;
/* Blur needs to be re-wrapped during injection. */
case "inject" :
/* For the blur effect to be fully de-applied, it needs to be set to "none" instead of 0. */
if ( ! parseFloat ( propertyValue ) ) {
return "none" ;
} else {
return "blur(" + propertyValue + ")" ;
}
}
} ,
/* <=IE8 do not support the standard opacity property. They use filter:alpha(opacity=INT) instead. */
opacity : function ( type , element , propertyValue ) {
if ( IE <= 8 ) {
switch ( type ) {
case "name" :
return "filter" ;
case "extract" :
/ * < = I E 8 r e t u r n a " f i l t e r " v a l u e o f " a l p h a ( o p a c i t y = \ d { 1 , 3 } ) " .
Extract the value and convert it to a decimal value to match the standard CSS opacity property ' s formatting . * /
var extracted = propertyValue . toString ( ) . match ( /alpha\(opacity=(.*)\)/i ) ;
if ( extracted ) {
/* Convert to decimal value. */
propertyValue = extracted [ 1 ] / 100 ;
} else {
/* When extracting opacity, default to 1 since a null value means opacity hasn't been set. */
propertyValue = 1 ;
}
return propertyValue ;
case "inject" :
/* Opacified elements are required to have their zoom property set to a non-zero value. */
element . style . zoom = 1 ;
/ * S e t t i n g t h e f i l t e r p r o p e r t y o n e l e m e n t s w i t h c e r t a i n f o n t p r o p e r t y c o m b i n a t i o n s c a n r e s u l t i n a
highly unappealing ultra - bolding effect . There ' s no way to remedy this throughout a tween , but dropping the
value altogether ( when opacity hits 1 ) at leasts ensures that the glitch is gone post - tweening . * /
if ( parseFloat ( propertyValue ) >= 1 ) {
return "" ;
} else {
/* As per the filter property's spec, convert the decimal value to a whole number and wrap the value. */
return "alpha(opacity=" + parseInt ( parseFloat ( propertyValue ) * 100 , 10 ) + ")" ;
}
}
/* With all other browsers, normalization is not required; return the same values that were passed in. */
} else {
switch ( type ) {
case "name" :
return "opacity" ;
case "extract" :
return propertyValue ;
case "inject" :
return propertyValue ;
}
}
}
} ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Batched Registrations
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Note: Batched normalizations extend the CSS.Normalizations.registered object. */
register : function ( ) {
/ * * * * * * * * * * * * * * * * *
Transforms
* * * * * * * * * * * * * * * * * /
/ * T r a n s f o r m s a r e t h e s u b p r o p e r t i e s c o n t a i n e d b y t h e C S S " t r a n s f o r m " p r o p e r t y . T r a n s f o r m s m u s t u n d e r g o n o r m a l i z a t i o n
so that they can be referenced in a properties map by their individual names . * /
/ * N o t e : W h e n t r a n s f o r m s a r e " s e t " , t h e y a r e a c t u a l l y a s s i g n e d t o a p e r - e l e m e n t t r a n s f o r m C a c h e . W h e n a l l t r a n s f o r m
setting is complete complete , CSS . flushTransformCache ( ) must be manually called to flush the values to the DOM .
Transform setting is batched in this way to improve performance : the transform style only needs to be updated
once when multiple transform subproperties are being animated simultaneously . * /
/ * N o t e : I E 9 a n d A n d r o i d G i n g e r b r e a d h a v e s u p p o r t f o r 2 D - - b u t n o t 3 D - - t r a n s f o r m s . S i n c e a n i m a t i n g u n s u p p o r t e d
transform properties results in the browser ignoring the * entire * transform string , we prevent these 3 D values
from being normalized for these browsers so that tweening skips these properties altogether
( since it will ignore them as being unsupported by the browser . ) * /
if ( ! ( IE <= 9 ) && ! Velocity . State . isGingerbread ) {
/ * N o t e : S i n c e t h e s t a n d a l o n e C S S " p e r s p e c t i v e " p r o p e r t y a n d t h e C S S t r a n s f o r m " p e r s p e c t i v e " s u b p r o p e r t y
share the same name , the latter is given a unique token within Velocity : "transformPerspective" . * /
CSS . Lists . transformsBase = CSS . Lists . transformsBase . concat ( CSS . Lists . transforms3D ) ;
}
for ( var i = 0 ; i < CSS . Lists . transformsBase . length ; i ++ ) {
/ * W r a p t h e d y n a m i c a l l y g e n e r a t e d n o r m a l i z a t i o n f u n c t i o n i n a n e w s c o p e s o t h a t t r a n s f o r m N a m e ' s v a l u e i s
paired with its respective function . ( Otherwise , all functions would take the final for loop ' s transformName . ) * /
( function ( ) {
var transformName = CSS . Lists . transformsBase [ i ] ;
CSS . Normalizations . registered [ transformName ] = function ( type , element , propertyValue ) {
switch ( type ) {
/* The normalized property name is the parent "transform" property -- the property that is actually set in CSS. */
case "name" :
return "transform" ;
/* Transform values are cached onto a per-element transformCache object. */
case "extract" :
/* If this transform has yet to be assigned a value, return its null value. */
if ( Data ( element ) === undefined || Data ( element ) . transformCache [ transformName ] === undefined ) {
/* Scale CSS.Lists.transformsBase default to 1 whereas all other transform properties default to 0. */
return /^scale/i . test ( transformName ) ? 1 : 0 ;
/ * W h e n t r a n s f o r m v a l u e s a r e s e t , t h e y a r e w r a p p e d i n p a r e n t h e s e s a s p e r t h e C S S s p e c .
Thus , when extracting their values ( for tween calculations ) , we strip off the parentheses . * /
} else {
return Data ( element ) . transformCache [ transformName ] . replace ( /[()]/g , "" ) ;
}
case "inject" :
var invalid = false ;
/ * I f a n i n d i v i d u a l t r a n s f o r m p r o p e r t y c o n t a i n s a n u n s u p p o r t e d u n i t t y p e , t h e b r o w s e r i g n o r e s t h e * e n t i r e * t r a n s f o r m p r o p e r t y .
Thus , protect users from themselves by skipping setting for transform values supplied with invalid unit types . * /
/* Switch on the base transform type; ignore the axis by removing the last letter from the transform's name. */
switch ( transformName . substr ( 0 , transformName . length - 1 ) ) {
/* Whitelist unit types for each transform. */
case "translate" :
invalid = ! /(%|px|em|rem|vw|vh|\d)$/i . test ( propertyValue ) ;
break ;
/* Since an axis-free "scale" property is supported as well, a little hack is used here to detect it by chopping off its last letter. */
case "scal" :
case "scale" :
/ * C h r o m e o n A n d r o i d h a s a b u g i n w h i c h s c a l e d e l e m e n t s b l u r i f t h e i r i n i t i a l s c a l e
value is below 1 ( which can happen with forcefeeding ) . Thus , we detect a yet - unset scale property
and ensure that its first value is always 1. More info : http : //stackoverflow.com/questions/10417890/css3-animations-with-transform-causes-blurred-elements-on-webkit/10417962#10417962 */
if ( Velocity . State . isAndroid && Data ( element ) . transformCache [ transformName ] === undefined && propertyValue < 1 ) {
propertyValue = 1 ;
}
invalid = ! /(\d)$/i . test ( propertyValue ) ;
break ;
case "skew" :
invalid = ! /(deg|\d)$/i . test ( propertyValue ) ;
break ;
case "rotate" :
invalid = ! /(deg|\d)$/i . test ( propertyValue ) ;
break ;
}
if ( ! invalid ) {
/* As per the CSS spec, wrap the value in parentheses. */
Data ( element ) . transformCache [ transformName ] = "(" + propertyValue + ")" ;
}
/* Although the value is set on the transformCache object, return the newly-updated value for the calling code to process as normal. */
return Data ( element ) . transformCache [ transformName ] ;
}
} ;
} ) ( ) ;
}
/ * * * * * * * * * * * * *
Colors
* * * * * * * * * * * * * /
/ * S i n c e V e l o c i t y o n l y a n i m a t e s a s i n g l e n u m e r i c v a l u e p e r p r o p e r t y , c o l o r a n i m a t i o n i s a c h i e v e d b y h o o k i n g t h e i n d i v i d u a l R G B A c o m p o n e n t s o f C S S c o l o r p r o p e r t i e s .
Accordingly , color values must be normalized ( e . g . "#ff0000" , "red" , and "rgb(255, 0, 0)" == > "255 0 0 1" ) so that their components can be injected / extracted by CSS . Hooks logic . * /
for ( var i = 0 ; i < CSS . Lists . colors . length ; i ++ ) {
/ * W r a p t h e d y n a m i c a l l y g e n e r a t e d n o r m a l i z a t i o n f u n c t i o n i n a n e w s c o p e s o t h a t c o l o r N a m e ' s v a l u e i s p a i r e d w i t h i t s r e s p e c t i v e f u n c t i o n .
( Otherwise , all functions would take the final for loop ' s colorName . ) * /
( function ( ) {
var colorName = CSS . Lists . colors [ i ] ;
/* Note: In IE<=8, which support rgb but not rgba, color properties are reverted to rgb by stripping off the alpha component. */
CSS . Normalizations . registered [ colorName ] = function ( type , element , propertyValue ) {
switch ( type ) {
case "name" :
return colorName ;
/* Convert all color values into the rgb format. (Old IE can return hex values and color names instead of rgb/rgba.) */
case "extract" :
var extracted ;
/* If the color is already in its hookable form (e.g. "255 255 255 1") due to having been previously extracted, skip extraction. */
if ( CSS . RegEx . wrappedValueAlreadyExtracted . test ( propertyValue ) ) {
extracted = propertyValue ;
} else {
var converted ,
colorNames = {
black : "rgb(0, 0, 0)" ,
blue : "rgb(0, 0, 255)" ,
gray : "rgb(128, 128, 128)" ,
green : "rgb(0, 128, 0)" ,
red : "rgb(255, 0, 0)" ,
white : "rgb(255, 255, 255)"
} ;
/* Convert color names to rgb. */
if ( /^[A-z]+$/i . test ( propertyValue ) ) {
if ( colorNames [ propertyValue ] !== undefined ) {
converted = colorNames [ propertyValue ]
} else {
/* If an unmatched color name is provided, default to black. */
converted = colorNames . black ;
}
/* Convert hex values to rgb. */
} else if ( CSS . RegEx . isHex . test ( propertyValue ) ) {
converted = "rgb(" + CSS . Values . hexToRgb ( propertyValue ) . join ( " " ) + ")" ;
/* If the provided color doesn't match any of the accepted color formats, default to black. */
} else if ( ! ( /^rgba?\(/i . test ( propertyValue ) ) ) {
converted = colorNames . black ;
}
/ * R e m o v e t h e s u r r o u n d i n g " r g b / r g b a ( ) " s t r i n g t h e n r e p l a c e c o m m a s w i t h s p a c e s a n d s t r i p
repeated spaces ( in case the value included spaces to begin with ) . * /
extracted = ( converted || propertyValue ) . toString ( ) . match ( CSS . RegEx . valueUnwrap ) [ 1 ] . replace ( /,(\s+)?/g , " " ) ;
}
/* So long as this isn't <=IE8, add a fourth (alpha) component if it's missing and default it to 1 (visible). */
if ( ! ( IE <= 8 ) && extracted . split ( " " ) . length === 3 ) {
extracted += " 1" ;
}
return extracted ;
case "inject" :
/* If this is IE<=8 and an alpha component exists, strip it off. */
if ( IE <= 8 ) {
if ( propertyValue . split ( " " ) . length === 4 ) {
propertyValue = propertyValue . split ( /\s+/ ) . slice ( 0 , 3 ) . join ( " " ) ;
}
/* Otherwise, add a fourth (alpha) component if it's missing and default it to 1 (visible). */
} else if ( propertyValue . split ( " " ) . length === 3 ) {
propertyValue += " 1" ;
}
/ * R e - i n s e r t t h e b r o w s e r - a p p r o p r i a t e w r a p p e r ( " r g b / r g b a ( ) " ) , i n s e r t c o m m a s , a n d s t r i p o f f d e c i m a l u n i t s
on all values but the fourth ( R , G , and B only accept whole numbers ) . * /
return ( IE <= 8 ? "rgb" : "rgba" ) + "(" + propertyValue . replace ( /\s+/g , "," ) . replace ( /\.(\d)+(?=,)/g , "" ) + ")" ;
}
} ;
} ) ( ) ;
}
}
} ,
/ * * * * * * * * * * * * * * * * * * * * * * * *
CSS Property Names
* * * * * * * * * * * * * * * * * * * * * * * * /
Names : {
/ * C a m e l c a s e a p r o p e r t y n a m e i n t o i t s J a v a S c r i p t n o t a t i o n ( e . g . " b a c k g r o u n d - c o l o r " = = > " b a c k g r o u n d C o l o r " ) .
Camelcasing is used to normalize property names between and across calls . * /
camelCase : function ( property ) {
return property . replace ( /-(\w)/g , function ( match , subMatch ) {
return subMatch . toUpperCase ( ) ;
} ) ;
} ,
/* For SVG elements, some properties (namely, dimensional ones) are GET/SET via the element's HTML attributes (instead of via CSS styles). */
SVGAttribute : function ( property ) {
var SVGAttributes = "width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2" ;
/* Certain browsers require an SVG transform to be applied as an attribute. (Otherwise, application via CSS is preferable due to 3D support.) */
if ( IE || ( Velocity . State . isAndroid && ! Velocity . State . isChrome ) ) {
SVGAttributes += "|transform" ;
}
return new RegExp ( "^(" + SVGAttributes + ")$" , "i" ) . test ( property ) ;
} ,
/* Determine whether a property should be set with a vendor prefix. */
/ * I f a p r e f i x e d v e r s i o n o f t h e p r o p e r t y e x i s t s , r e t u r n i t . O t h e r w i s e , r e t u r n t h e o r i g i n a l p r o p e r t y n a m e .
If the property is not at all supported by the browser , return a false flag . * /
prefixCheck : function ( property ) {
/* If this property has already been checked, return the cached value. */
if ( Velocity . State . prefixMatches [ property ] ) {
return [ Velocity . State . prefixMatches [ property ] , true ] ;
} else {
var vendors = [ "" , "Webkit" , "Moz" , "ms" , "O" ] ;
for ( var i = 0 , vendorsLength = vendors . length ; i < vendorsLength ; i ++ ) {
var propertyPrefixed ;
if ( i === 0 ) {
propertyPrefixed = property ;
} else {
/* Capitalize the first letter of the property to conform to JavaScript vendor prefix notation (e.g. webkitFilter). */
propertyPrefixed = vendors [ i ] + property . replace ( /^\w/ , function ( match ) { return match . toUpperCase ( ) ; } ) ;
}
/* Check if the browser supports this property as prefixed. */
if ( Type . isString ( Velocity . State . prefixElement . style [ propertyPrefixed ] ) ) {
/* Cache the match. */
Velocity . State . prefixMatches [ property ] = propertyPrefixed ;
return [ propertyPrefixed , true ] ;
}
}
/* If the browser doesn't support this property in any form, include a false flag so that the caller can decide how to proceed. */
return [ property , false ] ;
}
}
} ,
/ * * * * * * * * * * * * * * * * * * * * * * * *
CSS Property Values
* * * * * * * * * * * * * * * * * * * * * * * * /
Values : {
/* Hex to RGB conversion. Copyright Tim Down: http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb */
hexToRgb : function ( hex ) {
var shortformRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i ,
longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i ,
rgbParts ;
hex = hex . replace ( shortformRegex , function ( m , r , g , b ) {
return r + r + g + g + b + b ;
} ) ;
rgbParts = longformRegex . exec ( hex ) ;
return rgbParts ? [ parseInt ( rgbParts [ 1 ] , 16 ) , parseInt ( rgbParts [ 2 ] , 16 ) , parseInt ( rgbParts [ 3 ] , 16 ) ] : [ 0 , 0 , 0 ] ;
} ,
isCSSNullValue : function ( value ) {
/ * T h e b r o w s e r d e f a u l t s C S S v a l u e s t h a t h a v e n o t b e e n s e t t o e i t h e r 0 o r o n e o f s e v e r a l p o s s i b l e n u l l - v a l u e s t r i n g s .
Thus , we check for both falsiness and these special strings . * /
/ * N u l l - v a l u e c h e c k i n g i s p e r f o r m e d t o d e f a u l t t h e s p e c i a l s t r i n g s t o 0 ( f o r t h e s a k e o f t w e e n i n g ) o r t h e i r h o o k
templates as defined as CSS . Hooks ( for the sake of hook injection / extraction ) . * /
/* Note: Chrome returns "rgba(0, 0, 0, 0)" for an undefined color whereas IE returns "transparent". */
return ( value == 0 || /^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i . test ( value ) ) ;
} ,
/* Retrieve a property's default unit type. Used for assigning a unit type when one is not supplied by the user. */
getUnitType : function ( property ) {
if ( /^(rotate|skew)/i . test ( property ) ) {
return "deg" ;
} else if ( /(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i . test ( property ) ) {
/* The above properties are unitless. */
return "" ;
} else {
/* Default to px for all other properties. */
return "px" ;
}
} ,
/* HTML elements default to an associated display type when they're not set to display:none. */
/* Note: This function is used for correctly setting the non-"none" display value in certain Velocity redirects, such as fadeIn/Out. */
getDisplayType : function ( element ) {
var tagName = element && element . tagName . toString ( ) . toLowerCase ( ) ;
if ( /^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/i . test ( tagName ) ) {
return "inline" ;
} else if ( /^(li)$/i . test ( tagName ) ) {
return "list-item" ;
} else if ( /^(tr)$/i . test ( tagName ) ) {
return "table-row" ;
} else if ( /^(table)$/i . test ( tagName ) ) {
return "table" ;
} else if ( /^(tbody)$/i . test ( tagName ) ) {
return "table-row-group" ;
/* Default to "block" when no match is found. */
} else {
return "block" ;
}
} ,
/* The class add/remove functions are used to temporarily apply a "velocity-animating" class to elements while they're animating. */
addClass : function ( element , className ) {
if ( element . classList ) {
element . classList . add ( className ) ;
} else {
element . className += ( element . className . length ? " " : "" ) + className ;
}
} ,
removeClass : function ( element , className ) {
if ( element . classList ) {
element . classList . remove ( className ) ;
} else {
element . className = element . className . toString ( ) . replace ( new RegExp ( "(^|\\s)" + className . split ( " " ) . join ( "|" ) + "(\\s|$)" , "gi" ) , " " ) ;
}
}
} ,
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Style Getting & Setting
* * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* The singular getPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */
getPropertyValue : function ( element , property , rootPropertyValue , forceStyleLookup ) {
/* Get an element's computed property value. */
/ * N o t e : R e t r i e v i n g t h e v a l u e o f a C S S p r o p e r t y c a n n o t s i m p l y b e p e r f o r m e d b y c h e c k i n g a n e l e m e n t ' s
style attribute ( which only reflects user - defined values ) . Instead , the browser must be queried for a property ' s
* computed * value . You can read more about getComputedStyle here : https : //developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */
function computePropertyValue ( element , property ) {
/ * W h e n b o x - s i z i n g i s n ' t s e t t o b o r d e r - b o x , h e i g h t a n d w i d t h s t y l e v a l u e s a r e i n c o r r e c t l y c o m p u t e d w h e n a n
element 's scrollbars are visible (which expands the element' s dimensions ) . Thus , we defer to the more accurate
offsetHeight / Width property , which includes the total dimensions for interior , border , padding , and scrollbar .
We subtract border and padding to get the sum of interior + scrollbar . * /
var computedValue = 0 ;
/ * I E < = 8 d o e s n ' t s u p p o r t w i n d o w . g e t C o m p u t e d S t y l e , t h u s w e d e f e r t o j Q u e r y , w h i c h h a s a n e x t e n s i v e a r r a y
of hacks to accurately retrieve IE8 property values . Re - implementing that logic here is not worth bloating the
codebase for a dying browser . The performance repercussions of using jQuery here are minimal since
Velocity is optimized to rarely ( and sometimes never ) query the DOM . Further , the $ . css ( ) codepath isn ' t that slow . * /
if ( IE <= 8 ) {
computedValue = $ . css ( element , property ) ; /* GET */
/ * A l l o t h e r b r o w s e r s s u p p o r t g e t C o m p u t e d S t y l e . T h e r e t u r n e d l i v e o b j e c t r e f e r e n c e i s c a c h e d o n t o i t s
associated element so that it does not need to be refetched upon every GET . * /
} else {
/ * B r o w s e r s d o n o t r e t u r n h e i g h t a n d w i d t h v a l u e s f o r e l e m e n t s t h a t a r e s e t t o d i s p l a y : " n o n e " . T h u s , w e t e m p o r a r i l y
toggle display to the element type ' s default value . * /
var toggleDisplay = false ;
if ( /^(width|height)$/ . test ( property ) && CSS . getPropertyValue ( element , "display" ) === 0 ) {
toggleDisplay = true ;
CSS . setPropertyValue ( element , "display" , CSS . Values . getDisplayType ( element ) ) ;
}
function revertDisplay ( ) {
if ( toggleDisplay ) {
CSS . setPropertyValue ( element , "display" , "none" ) ;
}
}
if ( ! forceStyleLookup ) {
if ( property === "height" && CSS . getPropertyValue ( element , "boxSizing" ) . toString ( ) . toLowerCase ( ) !== "border-box" ) {
var contentBoxHeight = element . offsetHeight - ( parseFloat ( CSS . getPropertyValue ( element , "borderTopWidth" ) ) || 0 ) - ( parseFloat ( CSS . getPropertyValue ( element , "borderBottomWidth" ) ) || 0 ) - ( parseFloat ( CSS . getPropertyValue ( element , "paddingTop" ) ) || 0 ) - ( parseFloat ( CSS . getPropertyValue ( element , "paddingBottom" ) ) || 0 ) ;
revertDisplay ( ) ;
return contentBoxHeight ;
} else if ( property === "width" && CSS . getPropertyValue ( element , "boxSizing" ) . toString ( ) . toLowerCase ( ) !== "border-box" ) {
var contentBoxWidth = element . offsetWidth - ( parseFloat ( CSS . getPropertyValue ( element , "borderLeftWidth" ) ) || 0 ) - ( parseFloat ( CSS . getPropertyValue ( element , "borderRightWidth" ) ) || 0 ) - ( parseFloat ( CSS . getPropertyValue ( element , "paddingLeft" ) ) || 0 ) - ( parseFloat ( CSS . getPropertyValue ( element , "paddingRight" ) ) || 0 ) ;
revertDisplay ( ) ;
return contentBoxWidth ;
}
}
var computedStyle ;
/ * F o r e l e m e n t s t h a t V e l o c i t y h a s n ' t b e e n c a l l e d o n d i r e c t l y ( e . g . w h e n V e l o c i t y q u e r i e s t h e D O M o n b e h a l f
of a parent of an element its animating ) , perform a direct getComputedStyle lookup since the object isn ' t cached . * /
if ( Data ( element ) === undefined ) {
computedStyle = window . getComputedStyle ( element , null ) ; /* GET */
/* If the computedStyle object has yet to be cached, do so now. */
} else if ( ! Data ( element ) . computedStyle ) {
computedStyle = Data ( element ) . computedStyle = window . getComputedStyle ( element , null ) ; /* GET */
/* If computedStyle is cached, use it. */
} else {
computedStyle = Data ( element ) . computedStyle ;
}
/ * I E a n d F i r e f o x d o n o t r e t u r n a v a l u e f o r t h e g e n e r i c b o r d e r C o l o r - - t h e y o n l y r e t u r n i n d i v i d u a l v a l u e s f o r e a c h b o r d e r s i d e ' s c o l o r .
Also , in all browsers , when border colors aren 't all the same, a compound value is returned that Velocity isn' t setup to parse .
So , as a polyfill for querying individual border side colors , we just return the top border ' s color and animate all borders from that value . * /
if ( property === "borderColor" ) {
property = "borderTopColor" ;
}
/ * I E 9 h a s a b u g i n w h i c h t h e " f i l t e r " p r o p e r t y m u s t b e a c c e s s e d f r o m c o m p u t e d S t y l e u s i n g t h e g e t P r o p e r t y V a l u e m e t h o d
instead of a direct property lookup . The getPropertyValue method is slower than a direct lookup , which is why we avoid it by default . * /
if ( IE === 9 && property === "filter" ) {
computedValue = computedStyle . getPropertyValue ( property ) ; /* GET */
} else {
computedValue = computedStyle [ property ] ;
}
/ * F a l l b a c k t o t h e p r o p e r t y ' s s t y l e v a l u e ( i f d e f i n e d ) w h e n c o m p u t e d V a l u e r e t u r n s n o t h i n g ,
which can happen when the element hasn ' t been painted . * /
if ( computedValue === "" || computedValue === null ) {
computedValue = element . style [ property ] ;
}
revertDisplay ( ) ;
}
/ * F o r t o p , r i g h t , b o t t o m , a n d l e f t ( T R B L ) v a l u e s t h a t a r e s e t t o " a u t o " o n e l e m e n t s o f " f i x e d " o r " a b s o l u t e " p o s i t i o n ,
defer to jQuery for converting "auto" to a numeric value . ( For elements with a "static" or "relative" position , "auto" has the same
effect as being set to 0 , so no conversion is necessary . ) * /
/ * A n e x a m p l e o f w h y n u m e r i c c o n v e r s i o n i s n e c e s s a r y : W h e n a n e l e m e n t w i t h " p o s i t i o n : a b s o l u t e " h a s a n u n t o u c h e d " l e f t "
property , which reverts to "auto" , left ' s value is 0 relative to its parent element , but is often non - zero relative
to its * containing * ( not parent ) element , which is the nearest "position:relative" ancestor or the viewport ( and always the viewport in the case of "position:fixed" ) . * /
if ( computedValue === "auto" && /^(top|right|bottom|left)$/i . test ( property ) ) {
var position = computePropertyValue ( element , "position" ) ; /* GET */
/ * F o r a b s o l u t e p o s i t i o n i n g , j Q u e r y ' s $ . p o s i t i o n ( ) o n l y r e t u r n s v a l u e s f o r t o p a n d l e f t ;
right and bottom will have their "auto" value reverted to 0. * /
/ * N o t e : A j Q u e r y o b j e c t m u s t b e c r e a t e d h e r e s i n c e j Q u e r y d o e s n ' t h a v e a l o w - l e v e l a l i a s f o r $ . p o s i t i o n ( ) .
Not a big deal since we ' re currently in a GET batch anyway . * /
if ( position === "fixed" || ( position === "absolute" && /top|left/i . test ( property ) ) ) {
/* Note: jQuery strips the pixel unit from its returned values; we re-add it here to conform with computePropertyValue's behavior. */
computedValue = $ ( element ) . position ( ) [ property ] + "px" ; /* GET */
}
}
return computedValue ;
}
var propertyValue ;
/ * I f t h i s i s a h o o k e d p r o p e r t y ( e . g . " c l i p L e f t " i n s t e a d o f t h e r o o t p r o p e r t y o f " c l i p " ) ,
extract the hook ' s value from a normalized rootPropertyValue using CSS . Hooks . extractValue ( ) . * /
if ( CSS . Hooks . registered [ property ] ) {
var hook = property ,
hookRoot = CSS . Hooks . getRoot ( hook ) ;
/ * I f a c a c h e d r o o t P r o p e r t y V a l u e w a s n ' t p a s s e d i n ( w h i c h V e l o c i t y a l w a y s a t t e m p t s t o d o i n o r d e r t o a v o i d r e q u e r y i n g t h e D O M ) ,
query the DOM for the root property ' s value . * /
if ( rootPropertyValue === undefined ) {
/* Since the browser is now being directly queried, use the official post-prefixing property name for this lookup. */
rootPropertyValue = CSS . getPropertyValue ( element , CSS . Names . prefixCheck ( hookRoot ) [ 0 ] ) ; /* GET */
}
/* If this root has a normalization registered, peform the associated normalization extraction. */
if ( CSS . Normalizations . registered [ hookRoot ] ) {
rootPropertyValue = CSS . Normalizations . registered [ hookRoot ] ( "extract" , element , rootPropertyValue ) ;
}
/* Extract the hook's value. */
propertyValue = CSS . Hooks . extractValue ( hook , rootPropertyValue ) ;
/ * I f t h i s i s a n o r m a l i z e d p r o p e r t y ( e . g . " o p a c i t y " b e c o m e s " f i l t e r " i n < = I E 8 ) o r " t r a n s l a t e X " b e c o m e s " t r a n s f o r m " ) ,
normalize the property ' s name and value , and handle the special case of transforms . * /
/ * N o t e : N o r m a l i z i n g a p r o p e r t y i s m u t u a l l y e x c l u s i v e f r o m h o o k i n g a p r o p e r t y s i n c e h o o k - e x t r a c t e d v a l u e s a r e s t r i c t l y
numerical and therefore do not require normalization extraction . * /
} else if ( CSS . Normalizations . registered [ property ] ) {
var normalizedPropertyName ,
normalizedPropertyValue ;
normalizedPropertyName = CSS . Normalizations . registered [ property ] ( "name" , element ) ;
/ * T r a n s f o r m v a l u e s a r e c a l c u l a t e d v i a n o r m a l i z a t i o n e x t r a c t i o n ( s e e b e l o w ) , w h i c h c h e c k s a g a i n s t t h e e l e m e n t ' s t r a n s f o r m C a c h e .
At no point do transform GETs ever actually query the DOM ; initial stylesheet values are never processed .
This is because parsing 3 D transform matrices is not always accurate and would bloat our codebase ;
thus , normalization extraction defaults initial transform values to their zero - values ( e . g . 1 for scaleX and 0 for translateX ) . * /
if ( normalizedPropertyName !== "transform" ) {
normalizedPropertyValue = computePropertyValue ( element , CSS . Names . prefixCheck ( normalizedPropertyName ) [ 0 ] ) ; /* GET */
/* If the value is a CSS null-value and this property has a hook template, use that zero-value template so that hooks can be extracted from it. */
if ( CSS . Values . isCSSNullValue ( normalizedPropertyValue ) && CSS . Hooks . templates [ property ] ) {
normalizedPropertyValue = CSS . Hooks . templates [ property ] [ 1 ] ;
}
}
propertyValue = CSS . Normalizations . registered [ property ] ( "extract" , element , normalizedPropertyValue ) ;
}
/* If a (numeric) value wasn't produced via hook extraction or normalization, query the DOM. */
if ( ! /^[\d-]/ . test ( propertyValue ) ) {
/ * F o r S V G e l e m e n t s , d i m e n s i o n a l p r o p e r t i e s ( w h i c h S V G A t t r i b u t e ( ) d e t e c t s ) a r e t w e e n e d v i a
their HTML attribute values instead of their CSS style values . * /
if ( Data ( element ) && Data ( element ) . isSVG && CSS . Names . SVGAttribute ( property ) ) {
/ * S i n c e t h e h e i g h t / w i d t h a t t r i b u t e v a l u e s m u s t b e s e t m a n u a l l y , t h e y d o n ' t r e f l e c t c o m p u t e d v a l u e s .
Thus , we use use getBBox ( ) to ensure we always get values for elements with undefined height / width attributes . * /
if ( /^(height|width)$/i . test ( property ) ) {
/* Firefox throws an error if .getBBox() is called on an SVG that isn't attached to the DOM. */
try {
propertyValue = element . getBBox ( ) [ property ] ;
} catch ( error ) {
propertyValue = 0 ;
}
/* Otherwise, access the attribute value directly. */
} else {
propertyValue = element . getAttribute ( property ) ;
}
} else {
propertyValue = computePropertyValue ( element , CSS . Names . prefixCheck ( property ) [ 0 ] ) ; /* GET */
}
}
/ * S i n c e p r o p e r t y l o o k u p s a r e f o r a n i m a t i o n p u r p o s e s ( w h i c h e n t a i l s c o m p u t i n g t h e n u m e r i c d e l t a b e t w e e n s t a r t a n d e n d v a l u e s ) ,
convert CSS null - values to an integer of value 0. * /
if ( CSS . Values . isCSSNullValue ( propertyValue ) ) {
propertyValue = 0 ;
}
if ( Velocity . debug >= 2 ) console . log ( "Get " + property + ": " + propertyValue ) ;
return propertyValue ;
} ,
/* The singular setPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */
setPropertyValue : function ( element , property , propertyValue , rootPropertyValue , scrollData ) {
var propertyName = property ;
/* In order to be subjected to call options and element queueing, scroll animation is routed through Velocity as if it were a standard CSS property. */
if ( property === "scroll" ) {
/* If a container option is present, scroll the container instead of the browser window. */
if ( scrollData . container ) {
scrollData . container [ "scroll" + scrollData . direction ] = propertyValue ;
/* Otherwise, Velocity defaults to scrolling the browser window. */
} else {
if ( scrollData . direction === "Left" ) {
window . scrollTo ( propertyValue , scrollData . alternateValue ) ;
} else {
window . scrollTo ( scrollData . alternateValue , propertyValue ) ;
}
}
} else {
/ * T r a n s f o r m s ( t r a n s l a t e X , r o t a t e Z , e t c . ) a r e a p p l i e d t o a p e r - e l e m e n t t r a n s f o r m C a c h e o b j e c t , w h i c h i s m a n u a l l y f l u s h e d v i a f l u s h T r a n s f o r m C a c h e ( ) .
Thus , for now , we merely cache transforms being SET . * /
if ( CSS . Normalizations . registered [ property ] && CSS . Normalizations . registered [ property ] ( "name" , element ) === "transform" ) {
/* Perform a normalization injection. */
/* Note: The normalization logic handles the transformCache updating. */
CSS . Normalizations . registered [ property ] ( "inject" , element , propertyValue ) ;
propertyName = "transform" ;
propertyValue = Data ( element ) . transformCache [ property ] ;
} else {
/* Inject hooks. */
if ( CSS . Hooks . registered [ property ] ) {
var hookName = property ,
hookRoot = CSS . Hooks . getRoot ( property ) ;
/* If a cached rootPropertyValue was not provided, query the DOM for the hookRoot's current value. */
rootPropertyValue = rootPropertyValue || CSS . getPropertyValue ( element , hookRoot ) ; /* GET */
propertyValue = CSS . Hooks . injectValue ( hookName , propertyValue , rootPropertyValue ) ;
property = hookRoot ;
}
/* Normalize names and values. */
if ( CSS . Normalizations . registered [ property ] ) {
propertyValue = CSS . Normalizations . registered [ property ] ( "inject" , element , propertyValue ) ;
property = CSS . Normalizations . registered [ property ] ( "name" , element ) ;
}
/* Assign the appropriate vendor prefix before performing an official style update. */
propertyName = CSS . Names . prefixCheck ( property ) [ 0 ] ;
/ * A t r y / c a t c h i s u s e d f o r I E < = 8 , w h i c h t h r o w s a n e r r o r w h e n " i n v a l i d " C S S v a l u e s a r e s e t , e . g . a n e g a t i v e w i d t h .
Try / catch is avoided for other browsers since it incurs a performance overhead . * /
if ( IE <= 8 ) {
try {
element . style [ propertyName ] = propertyValue ;
} catch ( error ) { if ( Velocity . debug ) console . log ( "Browser does not support [" + propertyValue + "] for [" + propertyName + "]" ) ; }
/* SVG elements have their dimensional properties (width, height, x, y, cx, etc.) applied directly as attributes instead of as styles. */
/* Note: IE8 does not support SVG elements, so it's okay that we skip it for SVG animation. */
} else if ( Data ( element ) && Data ( element ) . isSVG && CSS . Names . SVGAttribute ( property ) ) {
/* Note: For SVG attributes, vendor-prefixed property names are never used. */
/* Note: Not all CSS properties can be animated via attributes, but the browser won't throw an error for unsupported properties. */
element . setAttribute ( property , propertyValue ) ;
} else {
element . style [ propertyName ] = propertyValue ;
}
if ( Velocity . debug >= 2 ) console . log ( "Set " + property + " (" + propertyName + "): " + propertyValue ) ;
}
}
/* Return the normalized property name and value in case the caller wants to know how these values were modified before being applied to the DOM. */
return [ propertyName , propertyValue ] ;
} ,
/* To increase performance by batching transform updates into a single SET, transforms are not directly applied to an element until flushTransformCache() is called. */
/* Note: Velocity applies transform properties in the same order that they are chronogically introduced to the element's CSS styles. */
flushTransformCache : function ( element ) {
var transformString = "" ;
/ * C e r t a i n b r o w s e r s r e q u i r e t h a t S V G t r a n s f o r m s b e a p p l i e d a s a n a t t r i b u t e . H o w e v e r , t h e S V G t r a n s f o r m a t t r i b u t e t a k e s a m o d i f i e d v e r s i o n o f C S S ' s t r a n s f o r m s t r i n g
( units are dropped and , except for skewX / Y , subproperties are merged into their master property -- e . g . scaleX and scaleY are merged into scale ( X Y ) . * /
if ( ( IE || ( Velocity . State . isAndroid && ! Velocity . State . isChrome ) ) && Data ( element ) . isSVG ) {
/ * S i n c e t r a n s f o r m v a l u e s a r e s t o r e d i n t h e i r p a r e n t h e s e s - w r a p p e d f o r m , w e u s e a h e l p e r f u n c t i o n t o s t r i p o u t t h e i r n u m e r i c v a l u e s .
Further , SVG transform properties only take unitless ( representing pixels ) values , so it ' s okay that parseFloat ( ) strips the unit suffixed to the float value . * /
function getTransformFloat ( transformProperty ) {
return parseFloat ( CSS . getPropertyValue ( element , transformProperty ) ) ;
}
/ * C r e a t e a n o b j e c t t o o r g a n i z e a l l t h e t r a n s f o r m s t h a t w e ' l l a p p l y t o t h e S V G e l e m e n t . T o k e e p t h e l o g i c s i m p l e ,
we process * all * transform properties -- even those that may not be explicitly applied ( since they default to their zero - values anyway ) . * /
var SVGTransforms = {
translate : [ getTransformFloat ( "translateX" ) , getTransformFloat ( "translateY" ) ] ,
skewX : [ getTransformFloat ( "skewX" ) ] , skewY : [ getTransformFloat ( "skewY" ) ] ,
/ * I f t h e s c a l e p r o p e r t y i s s e t ( n o n - 1 ) , u s e t h a t v a l u e f o r t h e s c a l e X a n d s c a l e Y v a l u e s
( this behavior mimics the result of animating all these properties at once on HTML elements ) . * /
scale : getTransformFloat ( "scale" ) !== 1 ? [ getTransformFloat ( "scale" ) , getTransformFloat ( "scale" ) ] : [ getTransformFloat ( "scaleX" ) , getTransformFloat ( "scaleY" ) ] ,
/ * N o t e : S V G ' s r o t a t e t r a n s f o r m t a k e s t h r e e v a l u e s : r o t a t i o n d e g r e e s f o l l o w e d b y t h e X a n d Y v a l u e s
defining the rotation ' s origin point . We ignore the origin values ( default them to 0 ) . * /
rotate : [ getTransformFloat ( "rotateZ" ) , 0 , 0 ]
} ;
/ * I t e r a t e t h r o u g h t h e t r a n s f o r m p r o p e r t i e s i n t h e u s e r - d e f i n e d p r o p e r t y m a p o r d e r .
( This mimics the behavior of non - SVG transform animation . ) * /
$ . each ( Data ( element ) . transformCache , function ( transformName ) {
/ * E x c e p t f o r w i t h s k e w X / Y , r e v e r t t h e a x i s - s p e c i f i c t r a n s f o r m s u b p r o p e r t i e s t o t h e i r a x i s - f r e e m a s t e r
properties so that they match up with SVG ' s accepted transform properties . * /
if ( /^translate/i . test ( transformName ) ) {
transformName = "translate" ;
} else if ( /^scale/i . test ( transformName ) ) {
transformName = "scale" ;
} else if ( /^rotate/i . test ( transformName ) ) {
transformName = "rotate" ;
}
/* Check that we haven't yet deleted the property from the SVGTransforms container. */
if ( SVGTransforms [ transformName ] ) {
/* Append the transform property in the SVG-supported transform format. As per the spec, surround the space-delimited values in parentheses. */
transformString += transformName + "(" + SVGTransforms [ transformName ] . join ( " " ) + ")" + " " ;
/ * A f t e r p r o c e s s i n g a n S V G t r a n s f o r m p r o p e r t y , d e l e t e i t f r o m t h e S V G T r a n s f o r m s c o n t a i n e r s o w e d o n ' t
re - insert the same master property if we encounter another one of its axis - specific properties . * /
delete SVGTransforms [ transformName ] ;
}
} ) ;
} else {
var transformValue ,
perspective ;
/* Transform properties are stored as members of the transformCache object. Concatenate all the members into a string. */
$ . each ( Data ( element ) . transformCache , function ( transformName ) {
transformValue = Data ( element ) . transformCache [ transformName ] ;
/* Transform's perspective subproperty must be set first in order to take effect. Store it temporarily. */
if ( transformName === "transformPerspective" ) {
perspective = transformValue ;
return true ;
}
/* IE9 only supports one rotation type, rotateZ, which it refers to as "rotate". */
if ( IE === 9 && transformName === "rotateZ" ) {
transformName = "rotate" ;
}
transformString += transformName + transformValue + " " ;
} ) ;
/* If present, set the perspective subproperty first. */
if ( perspective ) {
transformString = "perspective" + perspective + " " + transformString ;
}
}
CSS . setPropertyValue ( element , "transform" , transformString ) ;
}
} ;
/* Register hooks and normalizations. */
CSS . Hooks . register ( ) ;
CSS . Normalizations . register ( ) ;
/* Allow hook setting in the same fashion as jQuery's $.css(). */
Velocity . hook = function ( elements , arg2 , arg3 ) {
var value = undefined ;
elements = sanitizeElements ( elements ) ;
$ . each ( elements , function ( i , element ) {
/* Initialize Velocity's per-element data cache if this element hasn't previously been animated. */
if ( Data ( element ) === undefined ) {
Velocity . init ( element ) ;
}
/* Get property value. If an element set was passed in, only return the value for the first element. */
if ( arg3 === undefined ) {
if ( value === undefined ) {
value = Velocity . CSS . getPropertyValue ( element , arg2 ) ;
}
/* Set property value. */
} else {
/* sPV returns an array of the normalized propertyName/propertyValue pair used to update the DOM. */
var adjustedSet = Velocity . CSS . setPropertyValue ( element , arg2 , arg3 ) ;
/* Transform properties don't automatically set. They have to be flushed to the DOM. */
if ( adjustedSet [ 0 ] === "transform" ) {
Velocity . CSS . flushTransformCache ( element ) ;
}
value = adjustedSet ;
}
} ) ;
return value ;
} ;
/ * * * * * * * * * * * * * * * * *
Animation
* * * * * * * * * * * * * * * * * /
var animate = function ( ) {
/ * * * * * * * * * * * * * * * * * *
Call Chain
* * * * * * * * * * * * * * * * * * /
/* Logic for determining what to return to the call stack when exiting out of Velocity. */
function getChain ( ) {
/ * I f w e a r e u s i n g t h e u t i l i t y f u n c t i o n , a t t e m p t t o r e t u r n t h i s c a l l ' s p r o m i s e . I f n o p r o m i s e l i b r a r y w a s d e t e c t e d ,
default to null instead of returning the targeted elements so that utility function ' s return value is standardized . * /
if ( isUtility ) {
return promiseData . promise || null ;
/* Otherwise, if we're using $.fn, return the jQuery-/Zepto-wrapped element set. */
} else {
return elementsWrapped ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * *
Arguments Assignment
* * * * * * * * * * * * * * * * * * * * * * * * * /
/ * T o a l l o w f o r e x p r e s s i v e C o f f e e S c r i p t c o d e , V e l o c i t y s u p p o r t s a n a l t e r n a t i v e s y n t a x i n w h i c h " e l e m e n t s " ( o r " e " ) , " p r o p e r t i e s " ( o r " p " ) , a n d " o p t i o n s " ( o r " o " )
objects are defined on a container object that 's passed in as Velocity' s sole argument . * /
/* Note: Some browsers automatically populate arguments with a "properties" object. We detect it by checking for its default "names" property. */
var syntacticSugar = ( arguments [ 0 ] && ( arguments [ 0 ] . p || ( ( $ . isPlainObject ( arguments [ 0 ] . properties ) && ! arguments [ 0 ] . properties . names ) || Type . isString ( arguments [ 0 ] . properties ) ) ) ) ,
/* Whether Velocity was called via the utility function (as opposed to on a jQuery/Zepto object). */
isUtility ,
/ * W h e n V e l o c i t y i s c a l l e d v i a t h e u t i l i t y f u n c t i o n ( $ . V e l o c i t y ( ) / V e l o c i t y ( ) ) , e l e m e n t s a r e e x p l i c i t l y
passed in as the first parameter . Thus , argument positioning varies . We normalize them here . * /
elementsWrapped ,
argumentIndex ;
var elements ,
propertiesMap ,
options ;
/* Detect jQuery/Zepto elements being animated via the $.fn method. */
if ( Type . isWrapped ( this ) ) {
isUtility = false ;
argumentIndex = 0 ;
elements = this ;
elementsWrapped = this ;
/* Otherwise, raw elements are being animated via the utility function. */
} else {
isUtility = true ;
argumentIndex = 1 ;
elements = syntacticSugar ? ( arguments [ 0 ] . elements || arguments [ 0 ] . e ) : arguments [ 0 ] ;
}
elements = sanitizeElements ( elements ) ;
if ( ! elements ) {
return ;
}
if ( syntacticSugar ) {
propertiesMap = arguments [ 0 ] . properties || arguments [ 0 ] . p ;
options = arguments [ 0 ] . options || arguments [ 0 ] . o ;
} else {
propertiesMap = arguments [ argumentIndex ] ;
options = arguments [ argumentIndex + 1 ] ;
}
/ * T h e l e n g t h o f t h e e l e m e n t s e t ( i n t h e f o r m o f a n o d e L i s t o r a n a r r a y o f e l e m e n t s ) i s d e f a u l t e d t o 1 i n c a s e a
single raw DOM element is passed in ( which doesn ' t contain a length property ) . * /
var elementsLength = elements . length ,
elementsIndex = 0 ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
Argument Overloading
* * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * S u p p o r t i s i n c l u d e d f o r j Q u e r y ' s a r g u m e n t o v e r l o a d i n g : $ . a n i m a t e ( p r o p e r t y M a p [ , d u r a t i o n ] [ , e a s i n g ] [ , c o m p l e t e ] ) .
Overloading is detected by checking for the absence of an object being passed into options . * /
/* Note: The stop and finish actions do not accept animation options, and are therefore excluded from this check. */
2015-09-26 07:51:26 -07:00
if ( ! /^(stop|finish|finishAll)$/i . test ( propertiesMap ) && ! $ . isPlainObject ( options ) ) {
2015-06-26 08:53:49 -07:00
/* The utility function shifts all arguments one position to the right, so we adjust for that offset. */
var startingArgumentPosition = argumentIndex + 1 ;
options = { } ;
/* Iterate through all options arguments */
for ( var i = startingArgumentPosition ; i < arguments . length ; i ++ ) {
/* Treat a number as a duration. Parse it out. */
/ * N o t e : T h e f o l l o w i n g R e g E x w i l l r e t u r n t r u e i f p a s s e d a n a r r a y w i t h a n u m b e r a s i t s f i r s t i t e m .
Thus , arrays are skipped from this check . * /
if ( ! Type . isArray ( arguments [ i ] ) && ( /^(fast|normal|slow)$/i . test ( arguments [ i ] ) || /^\d/ . test ( arguments [ i ] ) ) ) {
options . duration = arguments [ i ] ;
/* Treat strings and arrays as easings. */
} else if ( Type . isString ( arguments [ i ] ) || Type . isArray ( arguments [ i ] ) ) {
options . easing = arguments [ i ] ;
/* Treat a function as a complete callback. */
} else if ( Type . isFunction ( arguments [ i ] ) ) {
options . complete = arguments [ i ] ;
}
}
}
/ * * * * * * * * * * * * * * *
Promises
* * * * * * * * * * * * * * * /
var promiseData = {
promise : null ,
resolver : null ,
rejecter : null
} ;
/ * I f t h i s c a l l w a s m a d e v i a t h e u t i l i t y f u n c t i o n ( w h i c h i s t h e d e f a u l t m e t h o d o f i n v o c a t i o n w h e n j Q u e r y / Z e p t o a r e n o t b e i n g u s e d ) , a n d i f
promise support was detected , create a promise object for this call and store references to its resolver and rejecter methods . The resolve
method is used when a call completes naturally or is prematurely stopped by the user . In both cases , completeCall ( ) handles the associated
call cleanup and promise resolving logic . The reject method is used when an invalid set of arguments is passed into a Velocity call . * /
/ * N o t e : V e l o c i t y e m p l o y s a c a l l - b a s e d q u e u e i n g a r c h i t e c t u r e , w h i c h m e a n s t h a t s t o p p i n g a n a n i m a t i n g e l e m e n t a c t u a l l y s t o p s t h e f u l l c a l l t h a t
triggered it -- not that one element exclusively . Similarly , there is one promise per call , and all elements targeted by a Velocity call are
grouped together for the purposes of resolving and rejecting a promise . * /
if ( isUtility && Velocity . Promise ) {
promiseData . promise = new Velocity . Promise ( function ( resolve , reject ) {
promiseData . resolver = resolve ;
promiseData . rejecter = reject ;
} ) ;
}
/ * * * * * * * * * * * * * * * * * * * * *
Action Detection
* * * * * * * * * * * * * * * * * * * * * /
/ * V e l o c i t y ' s b e h a v i o r i s c a t e g o r i z e d i n t o " a c t i o n s " : E l e m e n t s c a n e i t h e r b e s p e c i a l l y s c r o l l e d i n t o v i e w ,
or they can be started , stopped , or reversed . If a literal or referenced properties map is passed in as Velocity ' s
first argument , the associated action is "start" . Alternatively , "scroll" , "reverse" , or "stop" can be passed in instead of a properties map . * /
var action ;
switch ( propertiesMap ) {
case "scroll" :
action = "scroll" ;
break ;
case "reverse" :
action = "reverse" ;
break ;
case "finish" :
2015-09-26 07:51:26 -07:00
case "finishAll" :
2015-06-26 08:53:49 -07:00
case "stop" :
/ * * * * * * * * * * * * * * * * * * *
Action : Stop
* * * * * * * * * * * * * * * * * * * /
/* Clear the currently-active delay on each targeted element. */
$ . each ( elements , function ( i , element ) {
if ( Data ( element ) && Data ( element ) . delayTimer ) {
/* Stop the timer from triggering its cached next() function. */
clearTimeout ( Data ( element ) . delayTimer . setTimeout ) ;
/* Manually call the next() function so that the subsequent queue items can progress. */
if ( Data ( element ) . delayTimer . next ) {
Data ( element ) . delayTimer . next ( ) ;
}
delete Data ( element ) . delayTimer ;
}
2015-09-26 07:51:26 -07:00
/ * I f w e w a n t t o f i n i s h e v e r y t h i n g i n t h e q u e u e , w e h a v e t o i t e r a t e t h r o u g h i t
and call each function . This will make them active calls below , which will
cause them to be applied via the duration setting . * /
if ( propertiesMap === "finishAll" && ( options === true || Type . isString ( options ) ) ) {
/* Iterate through the items in the element's queue. */
$ . each ( $ . queue ( element , Type . isString ( options ) ? options : "" ) , function ( _ , item ) {
/* The queue array can contain an "inprogress" string, which we skip. */
if ( Type . isFunction ( item ) ) {
item ( ) ;
}
} ) ;
/* Clearing the $.queue() array is achieved by resetting it to []. */
$ . queue ( element , Type . isString ( options ) ? options : "" , [ ] ) ;
}
2015-06-26 08:53:49 -07:00
} ) ;
var callsToStop = [ ] ;
/ * W h e n t h e s t o p a c t i o n i s t r i g g e r e d , t h e e l e m e n t s ' c u r r e n t l y a c t i v e c a l l i s i m m e d i a t e l y s t o p p e d . T h e a c t i v e c a l l m i g h t h a v e
been applied to multiple elements , in which case all of the call ' s elements will be stopped . When an element
is stopped , the next item in its animation queue is immediately triggered . * /
/ * A n a d d i t i o n a l a r g u m e n t m a y b e p a s s e d i n t o c l e a r a n e l e m e n t ' s r e m a i n i n g q u e u e d c a l l s . E i t h e r t r u e ( w h i c h d e f a u l t s t o t h e " f x " q u e u e )
or a custom queue string can be passed in . * /
/ * N o t e : T h e s t o p c o m m a n d r u n s p r i o r t o V e l o c i t y ' s Q u e u e i n g p h a s e s i n c e i t s b e h a v i o r i s i n t e n d e d t o t a k e e f f e c t * i m m e d i a t e l y * ,
regardless of the element ' s current queue state . * /
/* Iterate through every active call. */
$ . each ( Velocity . State . calls , function ( i , activeCall ) {
/* Inactive calls are set to false by the logic inside completeCall(). Skip them. */
if ( activeCall ) {
/* Iterate through the active call's targeted elements. */
$ . each ( activeCall [ 1 ] , function ( k , activeElement ) {
/ * I f t r u e w a s p a s s e d i n a s a s e c o n d a r y a r g u m e n t , c l e a r a b s o l u t e l y a l l c a l l s o n t h i s e l e m e n t . O t h e r w i s e , o n l y
clear calls associated with the relevant queue . * /
/ * C a l l s t o p p i n g l o g i c w o r k s a s f o l l o w s :
- options === true -- > stop current default queue calls ( and queue : false calls ) , including remaining queued ones .
- options === undefined -- > stop current queue : "" call and all queue : false calls .
- options === false -- > stop only queue : false calls .
- options === "custom" -- > stop current queue : "custom" call , including remaining queued ones ( there is no functionality to only clear the currently - running queue : "custom" call ) . * /
var queueName = ( options === undefined ) ? "" : options ;
if ( queueName !== true && ( activeCall [ 2 ] . queue !== queueName ) && ! ( options === undefined && activeCall [ 2 ] . queue === false ) ) {
return true ;
}
/* Iterate through the calls targeted by the stop command. */
2015-09-26 07:51:26 -07:00
$ . each ( elements , function ( l , element ) {
2015-06-26 08:53:49 -07:00
/* Check that this call was applied to the target element. */
if ( element === activeElement ) {
2015-09-26 07:51:26 -07:00
/ * O p t i o n a l l y c l e a r t h e r e m a i n i n g q u e u e d c a l l s . I f w e ' r e d o i n g " f i n i s h A l l " t h i s w o n ' t f i n d a n y t h i n g ,
due to the queue - clearing above . * /
2015-06-26 08:53:49 -07:00
if ( options === true || Type . isString ( options ) ) {
/* Iterate through the items in the element's queue. */
$ . each ( $ . queue ( element , Type . isString ( options ) ? options : "" ) , function ( _ , item ) {
/* The queue array can contain an "inprogress" string, which we skip. */
if ( Type . isFunction ( item ) ) {
/ * P a s s t h e i t e m ' s c a l l b a c k a f l a g i n d i c a t i n g t h a t w e w a n t t o a b o r t f r o m t h e q u e u e c a l l .
( Specifically , the queue will resolve the call ' s associated promise then abort . ) * /
item ( null , true ) ;
}
} ) ;
/* Clearing the $.queue() array is achieved by resetting it to []. */
$ . queue ( element , Type . isString ( options ) ? options : "" , [ ] ) ;
}
if ( propertiesMap === "stop" ) {
/ * S i n c e " r e v e r s e " u s e s c a c h e d s t a r t v a l u e s ( t h e p r e v i o u s c a l l ' s e n d V a l u e s ) , t h e s e v a l u e s m u s t b e
changed to reflect the final value that the elements were actually tweened to . * /
/ * N o t e : I f o n l y q u e u e : f a l s e a n i m a t i o n s a r e c u r r e n t l y r u n n i n g o n a n e l e m e n t , i t w o n ' t h a v e a t w e e n s C o n t a i n e r
object . Also , queue : false animations can ' t be reversed . * /
if ( Data ( element ) && Data ( element ) . tweensContainer && queueName !== false ) {
$ . each ( Data ( element ) . tweensContainer , function ( m , activeTween ) {
activeTween . endValue = activeTween . currentValue ;
} ) ;
}
callsToStop . push ( i ) ;
2015-09-26 07:51:26 -07:00
} else if ( propertiesMap === "finish" || propertiesMap === "finishAll" ) {
2015-06-26 08:53:49 -07:00
/ * T o g e t a c t i v e t w e e n s t o f i n i s h i m m e d i a t e l y , w e f o r c e f u l l y s h o r t e n t h e i r d u r a t i o n s t o 1 m s s o t h a t
they finish upon the next rAf tick then proceed with normal call completion logic . * /
activeCall [ 2 ] . duration = 1 ;
}
}
} ) ;
} ) ;
}
} ) ;
/ * P r e m a t u r e l y c a l l c o m p l e t e C a l l ( ) o n e a c h m a t c h e d a c t i v e c a l l . P a s s a n a d d i t i o n a l f l a g f o r " s t o p " t o i n d i c a t e
that the complete callback and display : none setting should be skipped since we ' re completing prematurely . * /
if ( propertiesMap === "stop" ) {
$ . each ( callsToStop , function ( i , j ) {
completeCall ( j , true ) ;
} ) ;
if ( promiseData . promise ) {
/* Immediately resolve the promise associated with this stop call since stop runs synchronously. */
promiseData . resolver ( elements ) ;
}
}
/* Since we're stopping, and not proceeding with queueing, exit out of Velocity. */
return getChain ( ) ;
default :
/* Treat a non-empty plain object as a literal properties map. */
if ( $ . isPlainObject ( propertiesMap ) && ! Type . isEmptyObject ( propertiesMap ) ) {
action = "start" ;
/ * * * * * * * * * * * * * * * *
Redirects
* * * * * * * * * * * * * * * * /
/* Check if a string matches a registered redirect (see Redirects above). */
} else if ( Type . isString ( propertiesMap ) && Velocity . Redirects [ propertiesMap ] ) {
var opts = $ . extend ( { } , options ) ,
durationOriginal = opts . duration ,
delayOriginal = opts . delay || 0 ;
/* If the backwards option was passed in, reverse the element set so that elements animate from the last to the first. */
if ( opts . backwards === true ) {
elements = $ . extend ( true , [ ] , elements ) . reverse ( ) ;
}
/* Individually trigger the redirect for each element in the set to prevent users from having to handle iteration logic in their redirect. */
$ . each ( elements , function ( elementIndex , element ) {
/* If the stagger option was passed in, successively delay each element by the stagger value (in ms). Retain the original delay value. */
if ( parseFloat ( opts . stagger ) ) {
opts . delay = delayOriginal + ( parseFloat ( opts . stagger ) * elementIndex ) ;
} else if ( Type . isFunction ( opts . stagger ) ) {
opts . delay = delayOriginal + opts . stagger . call ( element , elementIndex , elementsLength ) ;
}
/ * I f t h e d r a g o p t i o n w a s p a s s e d i n , s u c c e s s i v e l y i n c r e a s e / d e c r e a s e ( d e p e n d i n g o n t h e p r e s e n s e o f o p t s . b a c k w a r d s )
the duration of each element ' s animation , using floors to prevent producing very short durations . * /
if ( opts . drag ) {
/* Default the duration of UI pack effects (callouts and transitions) to 1000ms instead of the usual default duration of 400ms. */
opts . duration = parseFloat ( durationOriginal ) || ( /^(callout|transition)/ . test ( propertiesMap ) ? 1000 : DURATION _DEFAULT ) ;
/ * F o r e a c h e l e m e n t , t a k e t h e g r e a t e r d u r a t i o n o f : A ) a n i m a t i o n c o m p l e t i o n p e r c e n t a g e r e l a t i v e t o t h e o r i g i n a l d u r a t i o n ,
B ) 75 % of the original duration , or C ) a 200 ms fallback ( in case duration is already set to a low value ) .
The end result is a baseline of 75 % of the redirect ' s duration that increases / decreases as the end of the element set is approached . * /
opts . duration = Math . max ( opts . duration * ( opts . backwards ? 1 - elementIndex / elementsLength : ( elementIndex + 1 ) / elementsLength ) , opts . duration * 0.75 , 200 ) ;
}
/ * P a s s i n t h e c a l l ' s o p t s o b j e c t s o t h a t t h e r e d i r e c t c a n o p t i o n a l l y e x t e n d i t . I t d e f a u l t s t o a n e m p t y o b j e c t i n s t e a d o f n u l l t o
reduce the opts checking logic required inside the redirect . * /
Velocity . Redirects [ propertiesMap ] . call ( element , element , opts || { } , elementIndex , elementsLength , elements , promiseData . promise ? promiseData : undefined ) ;
} ) ;
/ * S i n c e t h e a n i m a t i o n l o g i c r e s i d e s w i t h i n t h e r e d i r e c t ' s o w n c o d e , a b o r t t h e r e m a i n d e r o f t h i s c a l l .
( The performance overhead up to this point is virtually non - existant . ) * /
/* Note: The jQuery call chain is kept intact by returning the complete element set. */
return getChain ( ) ;
} else {
var abortError = "Velocity: First argument (" + propertiesMap + ") was not a property map, a known action, or a registered redirect. Aborting." ;
if ( promiseData . promise ) {
promiseData . rejecter ( new Error ( abortError ) ) ;
} else {
console . log ( abortError ) ;
}
return getChain ( ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * *
Call - Wide Variables
* * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * A c o n t a i n e r f o r C S S u n i t c o n v e r s i o n r a t i o s ( e . g . % , r e m , a n d e m = = > p x ) t h a t i s u s e d t o c a c h e r a t i o s a c r o s s a l l e l e m e n t s
being animated in a single Velocity call . Calculating unit ratios necessitates DOM querying and updating , and is therefore
avoided ( via caching ) wherever possible . This container is call - wide instead of page - wide to avoid the risk of using stale
conversion metrics across Velocity animations that are not immediately consecutively chained . * /
var callUnitConversionData = {
lastParent : null ,
lastPosition : null ,
lastFontSize : null ,
lastPercentToPxWidth : null ,
lastPercentToPxHeight : null ,
lastEmToPx : null ,
remToPx : null ,
vwToPx : null ,
vhToPx : null
} ;
/ * A c o n t a i n e r f o r a l l t h e e n s u i n g t w e e n d a t a a n d m e t a d a t a a s s o c i a t e d w i t h t h i s c a l l . T h i s c o n t a i n e r g e t s p u s h e d t o t h e p a g e - w i d e
Velocity . State . calls array that is processed during animation ticking . * /
var call = [ ] ;
/ * * * * * * * * * * * * * * * * * * * * * * * *
Element Processing
* * * * * * * * * * * * * * * * * * * * * * * * /
/ * E l e m e n t p r o c e s s i n g c o n s i s t s o f t h r e e p a r t s - - d a t a p r o c e s s i n g t h a t c a n n o t g o s t a l e a n d d a t a p r o c e s s i n g t h a t * c a n * g o s t a l e ( i . e . t h i r d - p a r t y s t y l e m o d i f i c a t i o n s ) :
1 ) Pre - Queueing : Element - wide variables , including the element ' s data storage , are instantiated . Call options are prepared . If triggered , the Stop action is executed .
2 ) Queueing : The logic that runs once this call has reached its point of execution in the element ' s $ . queue ( ) stack . Most logic is placed here to avoid risking it becoming stale .
3 ) Pushing : Consolidation of the tween data followed by its push onto the global in - progress calls container .
* /
function processElement ( ) {
/ * * * * * * * * * * * * * * * * * * * * * * * * *
Part I : Pre - Queueing
* * * * * * * * * * * * * * * * * * * * * * * * * /
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
Element - Wide Variables
* * * * * * * * * * * * * * * * * * * * * * * * * * * /
var element = this ,
/* The runtime opts object is the extension of the current call's options and Velocity's page-wide option defaults. */
opts = $ . extend ( { } , Velocity . defaults , options ) ,
/ * A c o n t a i n e r f o r t h e p r o c e s s e d d a t a a s s o c i a t e d w i t h e a c h p r o p e r t y i n t h e p r o p e r t y M a p .
( Each property in the map produces its own "tween" . ) * /
tweensContainer = { } ,
elementUnitConversionData ;
/ * * * * * * * * * * * * * * * * * *
Element Init
* * * * * * * * * * * * * * * * * * /
if ( Data ( element ) === undefined ) {
Velocity . init ( element ) ;
}
/ * * * * * * * * * * * * * * * * * *
Option : Delay
* * * * * * * * * * * * * * * * * * /
/* Since queue:false doesn't respect the item's existing queue, we avoid injecting its delay here (it's set later on). */
/ * N o t e : V e l o c i t y r o l l s i t s o w n d e l a y f u n c t i o n s i n c e j Q u e r y d o e s n ' t h a v e a u t i l i t y a l i a s f o r $ . f n . d e l a y ( )
( and thus requires jQuery element creation , which we avoid since its overhead includes DOM querying ) . * /
if ( parseFloat ( opts . delay ) && opts . queue !== false ) {
$ . queue ( element , opts . queue , function ( next ) {
/* This is a flag used to indicate to the upcoming completeCall() function that this queue entry was initiated by Velocity. See completeCall() for further details. */
Velocity . velocityQueueEntryFlag = true ;
/ * T h e e n s u i n g q u e u e i t e m ( w h i c h i s a s s i g n e d t o t h e " n e x t " a r g u m e n t t h a t $ . q u e u e ( ) a u t o m a t i c a l l y p a s s e s i n ) w i l l b e t r i g g e r e d a f t e r a s e t T i m e o u t d e l a y .
The setTimeout is stored so that it can be subjected to clearTimeout ( ) if this animation is prematurely stopped via Velocity ' s "stop" command . * /
Data ( element ) . delayTimer = {
setTimeout : setTimeout ( next , parseFloat ( opts . delay ) ) ,
next : next
} ;
} ) ;
}
/ * * * * * * * * * * * * * * * * * * * * *
Option : Duration
* * * * * * * * * * * * * * * * * * * * * /
/* Support for jQuery's named durations. */
switch ( opts . duration . toString ( ) . toLowerCase ( ) ) {
case "fast" :
opts . duration = 200 ;
break ;
case "normal" :
opts . duration = DURATION _DEFAULT ;
break ;
case "slow" :
opts . duration = 600 ;
break ;
default :
/* Remove the potential "ms" suffix and default to 1 if the user is attempting to set a duration of 0 (in order to produce an immediate style change). */
opts . duration = parseFloat ( opts . duration ) || 1 ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * *
Global Option : Mock
* * * * * * * * * * * * * * * * * * * * * * * * /
if ( Velocity . mock !== false ) {
/ * I n m o c k m o d e , a l l a n i m a t i o n s a r e f o r c e d t o 1 m s s o t h a t t h e y o c c u r i m m e d i a t e l y u p o n t h e n e x t r A F t i c k .
Alternatively , a multiplier can be passed in to time remap all delays and durations . * /
if ( Velocity . mock === true ) {
opts . duration = opts . delay = 1 ;
} else {
opts . duration *= parseFloat ( Velocity . mock ) || 1 ;
opts . delay *= parseFloat ( Velocity . mock ) || 1 ;
}
}
/ * * * * * * * * * * * * * * * * * * *
Option : Easing
* * * * * * * * * * * * * * * * * * * /
opts . easing = getEasing ( opts . easing , opts . duration ) ;
/ * * * * * * * * * * * * * * * * * * * * * *
Option : Callbacks
* * * * * * * * * * * * * * * * * * * * * * /
/* Callbacks must functions. Otherwise, default to null. */
if ( opts . begin && ! Type . isFunction ( opts . begin ) ) {
opts . begin = null ;
}
if ( opts . progress && ! Type . isFunction ( opts . progress ) ) {
opts . progress = null ;
}
if ( opts . complete && ! Type . isFunction ( opts . complete ) ) {
opts . complete = null ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Option : Display & Visibility
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Refer to Velocity's documentation (VelocityJS.org/#displayAndVisibility) for a description of the display and visibility options' behavior. */
/* Note: We strictly check for undefined instead of falsiness because display accepts an empty string value. */
if ( opts . display !== undefined && opts . display !== null ) {
opts . display = opts . display . toString ( ) . toLowerCase ( ) ;
/* Users can pass in a special "auto" value to instruct Velocity to set the element to its default display value. */
if ( opts . display === "auto" ) {
opts . display = Velocity . CSS . Values . getDisplayType ( element ) ;
}
}
if ( opts . visibility !== undefined && opts . visibility !== null ) {
opts . visibility = opts . visibility . toString ( ) . toLowerCase ( ) ;
}
/ * * * * * * * * * * * * * * * * * * * * * *
Option : mobileHA
* * * * * * * * * * * * * * * * * * * * * * /
/ * W h e n s e t t o t r u e , a n d i f t h i s i s a m o b i l e d e v i c e , m o b i l e H A a u t o m a t i c a l l y e n a b l e s h a r d w a r e a c c e l e r a t i o n ( v i a a n u l l t r a n s f o r m h a c k )
on animating elements . HA is removed from the element at the completion of its animation . * /
/* Note: Android Gingerbread doesn't support HA. If a null transform hack (mobileHA) is in fact set, it will prevent other tranform subproperties from taking effect. */
/* Note: You can read more about the use of mobileHA in Velocity's documentation: VelocityJS.org/#mobileHA. */
opts . mobileHA = ( opts . mobileHA && Velocity . State . isMobile && ! Velocity . State . isGingerbread ) ;
/ * * * * * * * * * * * * * * * * * * * * * * *
Part II : Queueing
* * * * * * * * * * * * * * * * * * * * * * * /
/ * W h e n a s e t o f e l e m e n t s i s t a r g e t e d b y a V e l o c i t y c a l l , t h e s e t i s b r o k e n u p a n d e a c h e l e m e n t h a s t h e c u r r e n t V e l o c i t y c a l l i n d i v i d u a l l y q u e u e d o n t o i t .
In this way , each element ' s existing queue is respected ; some elements may already be animating and accordingly should not have this current Velocity call triggered immediately . * /
/ * I n e a c h q u e u e , t w e e n d a t a i s p r o c e s s e d f o r e a c h a n i m a t i n g p r o p e r t y t h e n p u s h e d o n t o t h e c a l l - w i d e c a l l s a r r a y . W h e n t h e l a s t e l e m e n t i n t h e s e t h a s h a d i t s t w e e n s p r o c e s s e d ,
the call array is pushed to Velocity . State . calls for live processing by the requestAnimationFrame tick . * /
function buildQueue ( next ) {
/ * * * * * * * * * * * * * * * * * * *
Option : Begin
* * * * * * * * * * * * * * * * * * * /
/* The begin callback is fired once per call -- not once per elemenet -- and is passed the full raw DOM element set as both its context and its first argument. */
if ( opts . begin && elementsIndex === 0 ) {
/* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */
try {
opts . begin . call ( elements , elements ) ;
} catch ( error ) {
setTimeout ( function ( ) { throw error ; } , 1 ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Tween Data Construction ( for Scroll )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Note: In order to be subjected to chaining and animation options, scroll's tweening is routed through Velocity as if it were a standard CSS property animation. */
if ( action === "scroll" ) {
/* The scroll action uniquely takes an optional "offset" option -- specified in pixels -- that offsets the targeted scroll position. */
var scrollDirection = ( /^x$/i . test ( opts . axis ) ? "Left" : "Top" ) ,
scrollOffset = parseFloat ( opts . offset ) || 0 ,
scrollPositionCurrent ,
scrollPositionCurrentAlternate ,
scrollPositionEnd ;
/ * S c r o l l a l s o u n i q u e l y t a k e s a n o p t i o n a l " c o n t a i n e r " o p t i o n , w h i c h i n d i c a t e s t h e p a r e n t e l e m e n t t h a t s h o u l d b e s c r o l l e d - -
as opposed to the browser window itself . This is useful for scrolling toward an element that ' s inside an overflowing parent element . * /
if ( opts . container ) {
/* Ensure that either a jQuery object or a raw DOM element was passed in. */
if ( Type . isWrapped ( opts . container ) || Type . isNode ( opts . container ) ) {
/* Extract the raw DOM element from the jQuery wrapper. */
opts . container = opts . container [ 0 ] || opts . container ;
/ * N o t e : U n l i k e o t h e r p r o p e r t i e s i n V e l o c i t y , t h e b r o w s e r ' s s c r o l l p o s i t i o n i s n e v e r c a c h e d s i n c e i t s o f r e q u e n t l y c h a n g e s
( due to the user ' s natural interaction with the page ) . * /
scrollPositionCurrent = opts . container [ "scroll" + scrollDirection ] ; /* GET */
/ * $ . p o s i t i o n ( ) v a l u e s a r e r e l a t i v e t o t h e c o n t a i n e r ' s c u r r e n t l y v i e w a b l e a r e a ( w i t h o u t t a k i n g i n t o a c c o u n t t h e c o n t a i n e r ' s t r u e d i m e n s i o n s
-- say , for example , if the container was not overflowing ) . Thus , the scroll end value is the sum of the child element ' s position * and *
the scroll container ' s current scroll position . * /
scrollPositionEnd = ( scrollPositionCurrent + $ ( element ) . position ( ) [ scrollDirection . toLowerCase ( ) ] ) + scrollOffset ; /* GET */
/* If a value other than a jQuery object or a raw DOM element was passed in, default to null so that this option is ignored. */
} else {
opts . container = null ;
}
} else {
/ * I f t h e w i n d o w i t s e l f i s b e i n g s c r o l l e d - - n o t a c o n t a i n i n g e l e m e n t - - p e r f o r m a l i v e s c r o l l p o s i t i o n l o o k u p u s i n g
the appropriate cached property names ( which differ based on browser type ) . * /
scrollPositionCurrent = Velocity . State . scrollAnchor [ Velocity . State [ "scrollProperty" + scrollDirection ] ] ; /* GET */
/* When scrolling the browser window, cache the alternate axis's current value since window.scrollTo() doesn't let us change only one value at a time. */
scrollPositionCurrentAlternate = Velocity . State . scrollAnchor [ Velocity . State [ "scrollProperty" + ( scrollDirection === "Left" ? "Top" : "Left" ) ] ] ; /* GET */
/ * U n l i k e $ . p o s i t i o n ( ) , $ . o f f s e t ( ) v a l u e s a r e r e l a t i v e t o t h e b r o w s e r w i n d o w ' s t r u e d i m e n s i o n s - - n o t m e r e l y i t s c u r r e n t l y v i e w a b l e a r e a - -
and therefore end values do not need to be compounded onto current values . * /
scrollPositionEnd = $ ( element ) . offset ( ) [ scrollDirection . toLowerCase ( ) ] + scrollOffset ; /* GET */
}
/* Since there's only one format that scroll's associated tweensContainer can take, we create it manually. */
tweensContainer = {
scroll : {
rootPropertyValue : false ,
startValue : scrollPositionCurrent ,
currentValue : scrollPositionCurrent ,
endValue : scrollPositionEnd ,
unitType : "" ,
easing : opts . easing ,
scrollData : {
container : opts . container ,
direction : scrollDirection ,
alternateValue : scrollPositionCurrentAlternate
}
} ,
element : element
} ;
if ( Velocity . debug ) console . log ( "tweensContainer (scroll): " , tweensContainer . scroll , element ) ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Tween Data Construction ( for Reverse )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * R e v e r s e a c t s l i k e a " s t a r t " a c t i o n i n t h a t a p r o p e r t y m a p i s a n i m a t e d t o w a r d . T h e o n l y d i f f e r e n c e i s
that the property map used for reverse is the inverse of the map used in the previous call . Thus , we manipulate
the previous call to construct our new map : use the previous map 's end values as our new map' s start values . Copy over all other data . * /
/* Note: Reverse can be directly called via the "reverse" parameter, or it can be indirectly triggered via the loop option. (Loops are composed of multiple reverses.) */
/ * N o t e : R e v e r s e c a l l s d o n o t n e e d t o b e c o n s e c u t i v e l y c h a i n e d o n t o a c u r r e n t l y - a n i m a t i n g e l e m e n t i n o r d e r t o o p e r a t e o n c a c h e d v a l u e s ;
there is no harm to reverse being called on a potentially stale data cache since reverse ' s behavior is simply defined
as reverting to the element ' s values as they were prior to the previous * Velocity * call . * /
} else if ( action === "reverse" ) {
/* Abort if there is no prior animation data to reverse to. */
if ( ! Data ( element ) . tweensContainer ) {
/* Dequeue the element so that this queue entry releases itself immediately, allowing subsequent queue entries to run. */
$ . dequeue ( element , opts . queue ) ;
return ;
} else {
/ * * * * * * * * * * * * * * * * * * * * *
Options Parsing
* * * * * * * * * * * * * * * * * * * * * /
/ * I f t h e e l e m e n t w a s h i d d e n v i a t h e d i s p l a y o p t i o n i n t h e p r e v i o u s c a l l ,
revert display to "auto" prior to reversal so that the element is visible again . * /
if ( Data ( element ) . opts . display === "none" ) {
Data ( element ) . opts . display = "auto" ;
}
if ( Data ( element ) . opts . visibility === "hidden" ) {
Data ( element ) . opts . visibility = "visible" ;
}
/ * I f t h e l o o p o p t i o n w a s s e t i n t h e p r e v i o u s c a l l , d i s a b l e i t s o t h a t " r e v e r s e " c a l l s a r e n ' t r e c u r s i v e l y g e n e r a t e d .
Further , remove the previous call ' s callback options ; typically , users do not want these to be refired . * /
Data ( element ) . opts . loop = false ;
Data ( element ) . opts . begin = null ;
Data ( element ) . opts . complete = null ;
/ * S i n c e w e ' r e e x t e n d i n g a n o p t s o b j e c t t h a t h a s a l r e a d y b e e n e x t e n d e d w i t h t h e d e f a u l t s o p t i o n s o b j e c t ,
we remove non - explicitly - defined properties that are auto - assigned values . * /
if ( ! options . easing ) {
delete opts . easing ;
}
if ( ! options . duration ) {
delete opts . duration ;
}
/ * T h e o p t s o b j e c t u s e d f o r r e v e r s a l i s a n e x t e n s i o n o f t h e o p t i o n s o b j e c t o p t i o n a l l y p a s s e d i n t o t h i s
reverse call plus the options used in the previous Velocity call . * /
opts = $ . extend ( { } , Data ( element ) . opts , opts ) ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Tweens Container Reconstruction
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Create a deepy copy (indicated via the true flag) of the previous call's tweensContainer. */
var lastTweensContainer = $ . extend ( true , { } , Data ( element ) . tweensContainer ) ;
/* Manipulate the previous tweensContainer by replacing its end values and currentValues with its start values. */
for ( var lastTween in lastTweensContainer ) {
/* In addition to tween data, tweensContainers contain an element property that we ignore here. */
if ( lastTween !== "element" ) {
var lastStartValue = lastTweensContainer [ lastTween ] . startValue ;
lastTweensContainer [ lastTween ] . startValue = lastTweensContainer [ lastTween ] . currentValue = lastTweensContainer [ lastTween ] . endValue ;
lastTweensContainer [ lastTween ] . endValue = lastStartValue ;
/ * E a s i n g i s t h e o n l y o p t i o n t h a t e m b e d s i n t o t h e i n d i v i d u a l t w e e n d a t a ( s i n c e i t c a n b e d e f i n e d o n a p e r - p r o p e r t y b a s i s ) .
Accordingly , every property ' s easing value must be updated when an options object is passed in with a reverse call .
The side effect of this extensibility is that all per - property easing values are forcefully reset to the new value . * /
if ( ! Type . isEmptyObject ( options ) ) {
lastTweensContainer [ lastTween ] . easing = opts . easing ;
}
if ( Velocity . debug ) console . log ( "reverse tweensContainer (" + lastTween + "): " + JSON . stringify ( lastTweensContainer [ lastTween ] ) , element ) ;
}
}
tweensContainer = lastTweensContainer ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Tween Data Construction ( for Start )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
} else if ( action === "start" ) {
/ * * * * * * * * * * * * * * * * * * * * * * * * *
Value Transferring
* * * * * * * * * * * * * * * * * * * * * * * * * /
/ * I f t h i s q u e u e e n t r y f o l l o w s a p r e v i o u s V e l o c i t y - i n i t i a t e d q u e u e e n t r y * a n d * i f t h i s e n t r y w a s c r e a t e d
while the element was in the process of being animated by Velocity , then this current call is safe to use
the end values from the prior call as its start values . Velocity attempts to perform this value transfer
process whenever possible in order to avoid requerying the DOM . * /
/ * I f v a l u e s a r e n ' t t r a n s f e r r e d f r o m a p r i o r c a l l a n d s t a r t v a l u e s w e r e n o t f o r c e f e d b y t h e u s e r ( m o r e o n t h i s b e l o w ) ,
then the DOM is queried for the element ' s current values as a last resort . * /
/* Note: Conversely, animation reversal (and looping) *always* perform inter-call value transfers; they never requery the DOM. */
var lastTweensContainer ;
/ * T h e p e r - e l e m e n t i s A n i m a t i n g f l a g i s u s e d t o i n d i c a t e w h e t h e r i t ' s s a f e ( i . e . t h e d a t a i s n ' t s t a l e )
to transfer over end values to use as start values . If it ' s set to true and there is a previous
Velocity call to pull values from , do so . * /
if ( Data ( element ) . tweensContainer && Data ( element ) . isAnimating === true ) {
lastTweensContainer = Data ( element ) . tweensContainer ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
Tween Data Calculation
* * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* This function parses property data and defaults endValue, easing, and startValue as appropriate. */
/ * P r o p e r t y m a p v a l u e s c a n e i t h e r t a k e t h e f o r m o f 1 ) a s i n g l e v a l u e r e p r e s e n t i n g t h e e n d v a l u e ,
or 2 ) an array in the form of [ endValue , [ , easing ] [ , startValue ] ] .
The optional third parameter is a forcefed startValue to be used instead of querying the DOM for
the element 's current value. Read Velocity' s docmentation to learn more about forcefeeding : VelocityJS . org / # forcefeeding * /
function parsePropertyValue ( valueData , skipResolvingEasing ) {
var endValue = undefined ,
easing = undefined ,
startValue = undefined ;
/ * H a n d l e t h e a r r a y f o r m a t , w h i c h c a n b e s t r u c t u r e d a s o n e o f t h r e e p o t e n t i a l o v e r l o a d s :
A ) [ endValue , easing , startValue ] , B ) [ endValue , easing ] , or C ) [ endValue , startValue ] * /
if ( Type . isArray ( valueData ) ) {
/ * e n d V a l u e i s a l w a y s t h e f i r s t i t e m i n t h e a r r a y . D o n ' t b o t h e r v a l i d a t i n g e n d V a l u e ' s v a l u e n o w
since the ensuing property cycling logic does that . * /
endValue = valueData [ 0 ] ;
/ * T w o - i t e m a r r a y f o r m a t : I f t h e s e c o n d i t e m i s a n u m b e r , f u n c t i o n , o r h e x s t r i n g , t r e a t i t a s a
start value since easings can only be non - hex strings or arrays . * /
if ( ( ! Type . isArray ( valueData [ 1 ] ) && /^[\d-]/ . test ( valueData [ 1 ] ) ) || Type . isFunction ( valueData [ 1 ] ) || CSS . RegEx . isHex . test ( valueData [ 1 ] ) ) {
startValue = valueData [ 1 ] ;
/* Two or three-item array: If the second item is a non-hex string or an array, treat it as an easing. */
} else if ( ( Type . isString ( valueData [ 1 ] ) && ! CSS . RegEx . isHex . test ( valueData [ 1 ] ) ) || Type . isArray ( valueData [ 1 ] ) ) {
easing = skipResolvingEasing ? valueData [ 1 ] : getEasing ( valueData [ 1 ] , opts . duration ) ;
/* Don't bother validating startValue's value now since the ensuing property cycling logic inherently does that. */
if ( valueData [ 2 ] !== undefined ) {
startValue = valueData [ 2 ] ;
}
}
/* Handle the single-value format. */
} else {
endValue = valueData ;
}
/* Default to the call's easing if a per-property easing type was not defined. */
if ( ! skipResolvingEasing ) {
easing = easing || opts . easing ;
}
/ * I f f u n c t i o n s w e r e p a s s e d i n a s v a l u e s , p a s s t h e f u n c t i o n t h e c u r r e n t e l e m e n t a s i t s c o n t e x t ,
plus the element 's index and the element set' s size as arguments . Then , assign the returned value . * /
if ( Type . isFunction ( endValue ) ) {
endValue = endValue . call ( element , elementsIndex , elementsLength ) ;
}
if ( Type . isFunction ( startValue ) ) {
startValue = startValue . call ( element , elementsIndex , elementsLength ) ;
}
/* Allow startValue to be left as undefined to indicate to the ensuing code that its value was not forcefed. */
return [ endValue || 0 , easing , startValue ] ;
}
/ * C y c l e t h r o u g h e a c h p r o p e r t y i n t h e m a p , l o o k i n g f o r s h o r t h a n d c o l o r p r o p e r t i e s ( e . g . " c o l o r " a s o p p o s e d t o " c o l o r R e d " ) . I n j e c t t h e c o r r e s p o n d i n g
colorRed , colorGreen , and colorBlue RGB component tweens into the propertiesMap ( which Velocity understands ) and remove the shorthand property . * /
$ . each ( propertiesMap , function ( property , value ) {
/* Find shorthand color properties that have been passed a hex string. */
if ( RegExp ( "^" + CSS . Lists . colors . join ( "$|^" ) + "$" ) . test ( property ) ) {
/* Parse the value data for each shorthand. */
var valueData = parsePropertyValue ( value , true ) ,
endValue = valueData [ 0 ] ,
easing = valueData [ 1 ] ,
startValue = valueData [ 2 ] ;
if ( CSS . RegEx . isHex . test ( endValue ) ) {
/* Convert the hex strings into their RGB component arrays. */
var colorComponents = [ "Red" , "Green" , "Blue" ] ,
endValueRGB = CSS . Values . hexToRgb ( endValue ) ,
startValueRGB = startValue ? CSS . Values . hexToRgb ( startValue ) : undefined ;
/* Inject the RGB component tweens into propertiesMap. */
for ( var i = 0 ; i < colorComponents . length ; i ++ ) {
var dataArray = [ endValueRGB [ i ] ] ;
if ( easing ) {
dataArray . push ( easing ) ;
}
if ( startValueRGB !== undefined ) {
dataArray . push ( startValueRGB [ i ] ) ;
}
propertiesMap [ property + colorComponents [ i ] ] = dataArray ;
}
/* Remove the intermediary shorthand property entry now that we've processed it. */
delete propertiesMap [ property ] ;
}
}
} ) ;
/* Create a tween out of each property, and append its associated data to tweensContainer. */
for ( var property in propertiesMap ) {
/ * * * * * * * * * * * * * * * * * * * * * * * * * *
Start Value Sourcing
* * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Parse out endValue, easing, and startValue from the property's data. */
var valueData = parsePropertyValue ( propertiesMap [ property ] ) ,
endValue = valueData [ 0 ] ,
easing = valueData [ 1 ] ,
startValue = valueData [ 2 ] ;
/ * N o w t h a t t h e o r i g i n a l p r o p e r t y n a m e ' s f o r m a t h a s b e e n u s e d f o r t h e p a r s e P r o p e r t y V a l u e ( ) l o o k u p a b o v e ,
we force the property to its camelCase styling to normalize it for manipulation . * /
property = CSS . Names . camelCase ( property ) ;
/* In case this property is a hook, there are circumstances where we will intend to work on the hook's root property and not the hooked subproperty. */
var rootProperty = CSS . Hooks . getRoot ( property ) ,
rootPropertyValue = false ;
/ * O t h e r t h a n f o r t h e d u m m y t w e e n p r o p e r t y , p r o p e r t i e s t h a t a r e n o t s u p p o r t e d b y t h e b r o w s e r ( a n d d o n o t h a v e a n a s s o c i a t e d n o r m a l i z a t i o n ) w i l l
inherently produce no style changes when set , so they are skipped in order to decrease animation tick overhead .
Property support is determined via prefixCheck ( ) , which returns a false flag when no supported is detected . * /
/ * N o t e : S i n c e S V G e l e m e n t s h a v e s o m e o f t h e i r p r o p e r t i e s d i r e c t l y a p p l i e d a s H T M L a t t r i b u t e s ,
there is no way to check for their explicit browser support , and so we skip skip this check for them . * /
if ( ! Data ( element ) . isSVG && rootProperty !== "tween" && CSS . Names . prefixCheck ( rootProperty ) [ 1 ] === false && CSS . Normalizations . registered [ rootProperty ] === undefined ) {
if ( Velocity . debug ) console . log ( "Skipping [" + rootProperty + "] due to a lack of browser support." ) ;
continue ;
}
/ * I f t h e d i s p l a y o p t i o n i s b e i n g s e t t o a n o n - " n o n e " ( e . g . " b l o c k " ) a n d o p a c i t y ( f i l t e r o n I E < = 8 ) i s b e i n g
animated to an endValue of non - zero , the user ' s intention is to fade in from invisible , thus we forcefeed opacity
a startValue of 0 if its startValue hasn ' t already been sourced by value transferring or prior forcefeeding . * /
if ( ( ( opts . display !== undefined && opts . display !== null && opts . display !== "none" ) || ( opts . visibility !== undefined && opts . visibility !== "hidden" ) ) && /opacity|filter/ . test ( property ) && ! startValue && endValue !== 0 ) {
startValue = 0 ;
}
/ * I f v a l u e s h a v e b e e n t r a n s f e r r e d f r o m t h e p r e v i o u s V e l o c i t y c a l l , e x t r a c t t h e e n d V a l u e a n d r o o t P r o p e r t y V a l u e
for all of the current call ' s properties that were * also * animated in the previous call . * /
/* Note: Value transferring can optionally be disabled by the user via the _cacheValues option. */
if ( opts . _cacheValues && lastTweensContainer && lastTweensContainer [ property ] ) {
if ( startValue === undefined ) {
startValue = lastTweensContainer [ property ] . endValue + lastTweensContainer [ property ] . unitType ;
}
/ * T h e p r e v i o u s c a l l ' s r o o t P r o p e r t y V a l u e i s e x t r a c t e d f r o m t h e e l e m e n t ' s d a t a c a c h e s i n c e t h a t ' s t h e
instance of rootPropertyValue that gets freshly updated by the tweening process , whereas the rootPropertyValue
attached to the incoming lastTweensContainer is equal to the root property ' s value prior to any tweening . * /
rootPropertyValue = Data ( element ) . rootPropertyValueCache [ rootProperty ] ;
/* If values were not transferred from a previous Velocity call, query the DOM as needed. */
} else {
/* Handle hooked properties. */
if ( CSS . Hooks . registered [ property ] ) {
if ( startValue === undefined ) {
rootPropertyValue = CSS . getPropertyValue ( element , rootProperty ) ; /* GET */
/ * N o t e : T h e f o l l o w i n g g e t P r o p e r t y V a l u e ( ) c a l l d o e s n o t a c t u a l l y t r i g g e r a D O M q u e r y ;
getPropertyValue ( ) will extract the hook from rootPropertyValue . * /
startValue = CSS . getPropertyValue ( element , property , rootPropertyValue ) ;
/ * I f s t a r t V a l u e i s a l r e a d y d e f i n e d v i a f o r c e f e e d i n g , d o n o t q u e r y t h e D O M f o r t h e r o o t p r o p e r t y ' s v a l u e ;
just grab rootProperty 's zero-value template from CSS.Hooks. This overwrites the element' s actual
root property value ( if one is set ) , but this is acceptable since the primary reason users forcefeed is
to avoid DOM queries , and thus we likewise avoid querying the DOM for the root property ' s value . * /
} else {
/* Grab this hook's zero-value template, e.g. "0px 0px 0px black". */
rootPropertyValue = CSS . Hooks . templates [ rootProperty ] [ 1 ] ;
}
/* Handle non-hooked properties that haven't already been defined via forcefeeding. */
} else if ( startValue === undefined ) {
startValue = CSS . getPropertyValue ( element , property ) ; /* GET */
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * *
Value Data Extraction
* * * * * * * * * * * * * * * * * * * * * * * * * * /
var separatedValue ,
endValueUnitType ,
startValueUnitType ,
operator = false ;
/* Separates a property value into its numeric value and its unit type. */
function separateValue ( property , value ) {
var unitType ,
numericValue ;
numericValue = ( value || "0" )
. toString ( )
. toLowerCase ( )
/* Match the unit type at the end of the value. */
. replace ( /[%A-z]+$/ , function ( match ) {
/* Grab the unit type. */
unitType = match ;
/* Strip the unit type off of value. */
return "" ;
} ) ;
/* If no unit type was supplied, assign one that is appropriate for this property (e.g. "deg" for rotateZ or "px" for width). */
if ( ! unitType ) {
unitType = CSS . Values . getUnitType ( property ) ;
}
return [ numericValue , unitType ] ;
}
/* Separate startValue. */
separatedValue = separateValue ( property , startValue ) ;
startValue = separatedValue [ 0 ] ;
startValueUnitType = separatedValue [ 1 ] ;
/* Separate endValue, and extract a value operator (e.g. "+=", "-=") if one exists. */
separatedValue = separateValue ( property , endValue ) ;
endValue = separatedValue [ 0 ] . replace ( /^([+-\/*])=/ , function ( match , subMatch ) {
operator = subMatch ;
/* Strip the operator off of the value. */
return "" ;
} ) ;
endValueUnitType = separatedValue [ 1 ] ;
/* Parse float values from endValue and startValue. Default to 0 if NaN is returned. */
startValue = parseFloat ( startValue ) || 0 ;
endValue = parseFloat ( endValue ) || 0 ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Property - Specific Value Conversion
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Custom support for properties that don't actually accept the % unit type, but where pollyfilling is trivial and relatively foolproof. */
if ( endValueUnitType === "%" ) {
/ * A % - v a l u e f o n t S i z e / l i n e H e i g h t i s r e l a t i v e t o t h e p a r e n t ' s f o n t S i z e ( a s o p p o s e d t o t h e p a r e n t ' s d i m e n s i o n s ) ,
which is identical to the em unit ' s behavior , so we piggyback off of that . * /
if ( /^(fontSize|lineHeight)$/ . test ( property ) ) {
/* Convert % into an em decimal value. */
endValue = endValue / 100 ;
endValueUnitType = "em" ;
/* For scaleX and scaleY, convert the value into its decimal format and strip off the unit type. */
} else if ( /^scale/ . test ( property ) ) {
endValue = endValue / 100 ;
endValueUnitType = "" ;
/* For RGB components, take the defined percentage of 255 and strip off the unit type. */
} else if ( /(Red|Green|Blue)$/i . test ( property ) ) {
endValue = ( endValue / 100 ) * 255 ;
endValueUnitType = "" ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
Unit Ratio Calculation
* * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * W h e n q u e r i e d , t h e b r o w s e r r e t u r n s ( m o s t ) C S S p r o p e r t y v a l u e s i n p i x e l s . T h e r e f o r e , i f a n e n d V a l u e w i t h a u n i t t y p e o f
% , em , or rem is animated toward , startValue must be converted from pixels into the same unit type as endValue in order
for value manipulation logic ( increment / decrement ) to proceed . Further , if the startValue was forcefed or transferred
from a previous call , startValue may also not be in pixels . Unit conversion logic therefore consists of two steps :
1 ) Calculating the ratio of % / e m / r e m / v h / v w r e l a t i v e t o p i x e l s
2 ) Converting startValue into the same unit of measurement as endValue based on these ratios . * /
/ * U n i t c o n v e r s i o n r a t i o s a r e c a l c u l a t e d b y i n s e r t i n g a s i b l i n g n o d e n e x t t o t h e t a r g e t n o d e , c o p y i n g o v e r i t s p o s i t i o n p r o p e r t y ,
setting values with the target unit type then comparing the returned pixel value . * /
/ * N o t e : E v e n i f o n l y o n e o f t h e s e u n i t t y p e s i s b e i n g a n i m a t e d , a l l u n i t r a t i o s a r e c a l c u l a t e d a t o n c e s i n c e t h e o v e r h e a d
of batching the SETs and GETs together upfront outweights the potential overhead
of layout thrashing caused by re - querying for uncalculated ratios for subsequently - processed properties . * /
/* Todo: Shift this logic into the calls' first tick instance so that it's synced with RAF. */
function calculateUnitRatios ( ) {
/ * * * * * * * * * * * * * * * * * * * * * * * *
Same Ratio Checks
* * * * * * * * * * * * * * * * * * * * * * * * /
/ * T h e p r o p e r t i e s b e l o w a r e u s e d t o d e t e r m i n e w h e t h e r t h e e l e m e n t d i f f e r s s u f f i c i e n t l y f r o m t h i s c a l l ' s
previously iterated element to also differ in its unit conversion ratios . If the properties match up with those
of the prior element , the prior element ' s conversion ratios are used . Like most optimizations in Velocity ,
this is done to minimize DOM querying . * /
var sameRatioIndicators = {
myParent : element . parentNode || document . body , /* GET */
position : CSS . getPropertyValue ( element , "position" ) , /* GET */
fontSize : CSS . getPropertyValue ( element , "fontSize" ) /* GET */
} ,
/* Determine if the same % ratio can be used. % is based on the element's position value and its parent's width and height dimensions. */
samePercentRatio = ( ( sameRatioIndicators . position === callUnitConversionData . lastPosition ) && ( sameRatioIndicators . myParent === callUnitConversionData . lastParent ) ) ,
/* Determine if the same em ratio can be used. em is relative to the element's fontSize. */
sameEmRatio = ( sameRatioIndicators . fontSize === callUnitConversionData . lastFontSize ) ;
/* Store these ratio indicators call-wide for the next element to compare against. */
callUnitConversionData . lastParent = sameRatioIndicators . myParent ;
callUnitConversionData . lastPosition = sameRatioIndicators . position ;
callUnitConversionData . lastFontSize = sameRatioIndicators . fontSize ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
Element - Specific Units
* * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * N o t e : I E 8 r o u n d s t o t h e n e a r e s t p i x e l w h e n r e t u r n i n g C S S v a l u e s , t h u s w e p e r f o r m c o n v e r s i o n s u s i n g a m e a s u r e m e n t
of 100 ( instead of 1 ) to give our ratios a precision of at least 2 decimal values . * /
var measurement = 100 ,
unitRatios = { } ;
if ( ! sameEmRatio || ! samePercentRatio ) {
var dummy = Data ( element ) . isSVG ? document . createElementNS ( "http://www.w3.org/2000/svg" , "rect" ) : document . createElement ( "div" ) ;
Velocity . init ( dummy ) ;
sameRatioIndicators . myParent . appendChild ( dummy ) ;
/ * T o a c c u r a t e l y a n d c o n s i s t e n t l y c a l c u l a t e c o n v e r s i o n r a t i o s , t h e e l e m e n t ' s c a s c a d e d o v e r f l o w a n d b o x - s i z i n g a r e s t r i p p e d .
Similarly , since width / height can be artificially constrained by their min - /max- equivalents, these are controlled for as well. */
/* Note: Overflow must be also be controlled for per-axis since the overflow property overwrites its per-axis values. */
$ . each ( [ "overflow" , "overflowX" , "overflowY" ] , function ( i , property ) {
Velocity . CSS . setPropertyValue ( dummy , property , "hidden" ) ;
} ) ;
Velocity . CSS . setPropertyValue ( dummy , "position" , sameRatioIndicators . position ) ;
Velocity . CSS . setPropertyValue ( dummy , "fontSize" , sameRatioIndicators . fontSize ) ;
Velocity . CSS . setPropertyValue ( dummy , "boxSizing" , "content-box" ) ;
/* width and height act as our proxy properties for measuring the horizontal and vertical % ratios. */
$ . each ( [ "minWidth" , "maxWidth" , "width" , "minHeight" , "maxHeight" , "height" ] , function ( i , property ) {
Velocity . CSS . setPropertyValue ( dummy , property , measurement + "%" ) ;
} ) ;
/* paddingLeft arbitrarily acts as our proxy property for the em ratio. */
Velocity . CSS . setPropertyValue ( dummy , "paddingLeft" , measurement + "em" ) ;
/* Divide the returned value by the measurement to get the ratio between 1% and 1px. Default to 1 since working with 0 can produce Infinite. */
unitRatios . percentToPxWidth = callUnitConversionData . lastPercentToPxWidth = ( parseFloat ( CSS . getPropertyValue ( dummy , "width" , null , true ) ) || 1 ) / measurement ; /* GET */
unitRatios . percentToPxHeight = callUnitConversionData . lastPercentToPxHeight = ( parseFloat ( CSS . getPropertyValue ( dummy , "height" , null , true ) ) || 1 ) / measurement ; /* GET */
unitRatios . emToPx = callUnitConversionData . lastEmToPx = ( parseFloat ( CSS . getPropertyValue ( dummy , "paddingLeft" ) ) || 1 ) / measurement ; /* GET */
sameRatioIndicators . myParent . removeChild ( dummy ) ;
} else {
unitRatios . emToPx = callUnitConversionData . lastEmToPx ;
unitRatios . percentToPxWidth = callUnitConversionData . lastPercentToPxWidth ;
unitRatios . percentToPxHeight = callUnitConversionData . lastPercentToPxHeight ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * *
Element - Agnostic Units
* * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * W h e r e a s % a n d e m r a t i o s a r e d e t e r m i n e d o n a p e r - e l e m e n t b a s i s , t h e r e m u n i t o n l y n e e d s t o b e c h e c k e d
once per call since it 's exclusively dependant upon document.body' s fontSize . If this is the first time
that calculateUnitRatios ( ) is being run during this call , remToPx will still be set to its default value of null ,
so we calculate it now . * /
if ( callUnitConversionData . remToPx === null ) {
/* Default to browsers' default fontSize of 16px in the case of 0. */
callUnitConversionData . remToPx = parseFloat ( CSS . getPropertyValue ( document . body , "fontSize" ) ) || 16 ; /* GET */
}
/* Similarly, viewport units are %-relative to the window's inner dimensions. */
if ( callUnitConversionData . vwToPx === null ) {
callUnitConversionData . vwToPx = parseFloat ( window . innerWidth ) / 100 ; /* GET */
callUnitConversionData . vhToPx = parseFloat ( window . innerHeight ) / 100 ; /* GET */
}
unitRatios . remToPx = callUnitConversionData . remToPx ;
unitRatios . vwToPx = callUnitConversionData . vwToPx ;
unitRatios . vhToPx = callUnitConversionData . vhToPx ;
if ( Velocity . debug >= 1 ) console . log ( "Unit ratios: " + JSON . stringify ( unitRatios ) , element ) ;
return unitRatios ;
}
/ * * * * * * * * * * * * * * * * * * * *
Unit Conversion
* * * * * * * * * * * * * * * * * * * * /
/* The * and / operators, which are not passed in with an associated unit, inherently use startValue's unit. Skip value and unit conversion. */
if ( /[\/*]/ . test ( operator ) ) {
endValueUnitType = startValueUnitType ;
/ * I f s t a r t V a l u e a n d e n d V a l u e d i f f e r i n u n i t t y p e , c o n v e r t s t a r t V a l u e i n t o t h e s a m e u n i t t y p e a s e n d V a l u e s o t h a t i f e n d V a l u e U n i t T y p e
is a relative unit ( % , em , rem ) , the values set during tweening will continue to be accurately relative even if the metrics they depend
on are dynamically changing during the course of the animation . Conversely , if we always normalized into px and used px for setting values , the px ratio
would become stale if the original unit being animated toward was relative and the underlying metrics change during the animation . * /
/* Since 0 is 0 in any unit type, no conversion is necessary when startValue is 0 -- we just start at 0 with endValueUnitType. */
} else if ( ( startValueUnitType !== endValueUnitType ) && startValue !== 0 ) {
/* Unit conversion is also skipped when endValue is 0, but *startValueUnitType* must be used for tween values to remain accurate. */
/ * N o t e : S k i p p i n g u n i t c o n v e r s i o n h e r e m e a n s t h a t i f e n d V a l u e U n i t T y p e w a s o r i g i n a l l y a r e l a t i v e u n i t , t h e a n i m a t i o n w o n ' t r e l a t i v e l y
match the underlying metrics if they change , but this is acceptable since we ' re animating toward invisibility instead of toward visibility ,
which remains past the point of the animation ' s completion . * /
if ( endValue === 0 ) {
endValueUnitType = startValueUnitType ;
} else {
/ * B y t h i s p o i n t , w e c a n n o t a v o i d u n i t c o n v e r s i o n ( i t ' s u n d e s i r a b l e s i n c e i t c a u s e s l a y o u t t h r a s h i n g ) .
If we haven ' t already , we trigger calculateUnitRatios ( ) , which runs once per element per call . * /
elementUnitConversionData = elementUnitConversionData || calculateUnitRatios ( ) ;
/* The following RegEx matches CSS properties that have their % values measured relative to the x-axis. */
/* Note: W3C spec mandates that all of margin and padding's properties (even top and bottom) are %-relative to the *width* of the parent element. */
var axis = ( /margin|padding|left|right|width|text|word|letter/i . test ( property ) || /X$/ . test ( property ) || property === "x" ) ? "x" : "y" ;
/ * I n o r d e r t o a v o i d g e n e r a t i n g n ^ 2 b e s p o k e c o n v e r s i o n f u n c t i o n s , u n i t c o n v e r s i o n i s a t w o - s t e p p r o c e s s :
1 ) Convert startValue into pixels . 2 ) Convert this new pixel value into endValue ' s unit type . * /
switch ( startValueUnitType ) {
case "%" :
/ * N o t e : t r a n s l a t e X a n d t r a n s l a t e Y a r e t h e o n l y p r o p e r t i e s t h a t a r e % - r e l a t i v e t o a n e l e m e n t ' s o w n d i m e n s i o n s - - n o t i t s p a r e n t ' s d i m e n s i o n s .
Velocity does not include a special conversion process to account for this behavior . Therefore , animating translateX / Y from a % value
to a non - % value will produce an incorrect start value . Fortunately , this sort of cross - unit conversion is rarely done by users in practice . * /
startValue *= ( axis === "x" ? elementUnitConversionData . percentToPxWidth : elementUnitConversionData . percentToPxHeight ) ;
break ;
case "px" :
/* px acts as our midpoint in the unit conversion process; do nothing. */
break ;
default :
startValue *= elementUnitConversionData [ startValueUnitType + "ToPx" ] ;
}
/* Invert the px ratios to convert into to the target unit. */
switch ( endValueUnitType ) {
case "%" :
startValue *= 1 / ( axis === "x" ? elementUnitConversionData . percentToPxWidth : elementUnitConversionData . percentToPxHeight ) ;
break ;
case "px" :
/* startValue is already in px, do nothing; we're done. */
break ;
default :
startValue *= 1 / elementUnitConversionData [ endValueUnitType + "ToPx" ] ;
}
}
}
/ * * * * * * * * * * * * * * * * * * * * *
Relative Values
* * * * * * * * * * * * * * * * * * * * * /
/* Operator logic must be performed last since it requires unit-normalized start and end values. */
/ * N o t e : R e l a t i v e * p e r c e n t v a l u e s * d o n o t b e h a v e h o w m o s t p e o p l e t h i n k ; w h i l e o n e w o u l d e x p e c t " + = 5 0 % "
to increase the property 1.5 x its current value , it in fact increases the percent units in absolute terms :
50 points is added on top of the current % value . * /
switch ( operator ) {
case "+" :
endValue = startValue + endValue ;
break ;
case "-" :
endValue = startValue - endValue ;
break ;
case "*" :
endValue = startValue * endValue ;
break ;
case "/" :
endValue = startValue / endValue ;
break ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * *
tweensContainer Push
* * * * * * * * * * * * * * * * * * * * * * * * * * /
/* Construct the per-property tween object, and push it to the element's tweensContainer. */
tweensContainer [ property ] = {
rootPropertyValue : rootPropertyValue ,
startValue : startValue ,
currentValue : startValue ,
endValue : endValue ,
unitType : endValueUnitType ,
easing : easing
} ;
if ( Velocity . debug ) console . log ( "tweensContainer (" + property + "): " + JSON . stringify ( tweensContainer [ property ] ) , element ) ;
}
/* Along with its property data, store a reference to the element itself onto tweensContainer. */
tweensContainer . element = element ;
}
/ * * * * * * * * * * * * * * * * *
Call Push
* * * * * * * * * * * * * * * * * /
/ * N o t e : t w e e n s C o n t a i n e r c a n b e e m p t y i f a l l o f t h e p r o p e r t i e s i n t h i s c a l l ' s p r o p e r t y m a p w e r e s k i p p e d d u e t o n o t
being supported by the browser . The element property is used for checking that the tweensContainer has been appended to . * /
if ( tweensContainer . element ) {
/* Apply the "velocity-animating" indicator class. */
CSS . Values . addClass ( element , "velocity-animating" ) ;
/* The call array houses the tweensContainers for each element being animated in the current call. */
call . push ( tweensContainer ) ;
/* Store the tweensContainer and options if we're working on the default effects queue, so that they can be used by the reverse command. */
if ( opts . queue === "" ) {
Data ( element ) . tweensContainer = tweensContainer ;
Data ( element ) . opts = opts ;
}
/* Switch on the element's animating flag. */
Data ( element ) . isAnimating = true ;
/ * O n c e t h e f i n a l e l e m e n t i n t h i s c a l l ' s e l e m e n t s e t h a s b e e n p r o c e s s e d , p u s h t h e c a l l a r r a y o n t o
Velocity . State . calls for the animation tick to immediately begin processing . * /
if ( elementsIndex === elementsLength - 1 ) {
/ * A d d t h e c u r r e n t c a l l p l u s i t s a s s o c i a t e d m e t a d a t a ( t h e e l e m e n t s e t a n d t h e c a l l ' s o p t i o n s ) o n t o t h e g l o b a l c a l l c o n t a i n e r .
Anything on this call container is subjected to tick ( ) processing . * /
Velocity . State . calls . push ( [ call , elements , opts , null , promiseData . resolver ] ) ;
/* If the animation tick isn't running, start it. (Velocity shuts it off when there are no active calls to process.) */
if ( Velocity . State . isTicking === false ) {
Velocity . State . isTicking = true ;
/* Start the tick loop. */
tick ( ) ;
}
} else {
elementsIndex ++ ;
}
}
}
/* When the queue option is set to false, the call skips the element's queue and fires immediately. */
if ( opts . queue === false ) {
/ * S i n c e t h i s b u i l d Q u e u e c a l l d o e s n ' t r e s p e c t t h e e l e m e n t ' s e x i s t i n g q u e u e ( w h i c h i s w h e r e a d e l a y o p t i o n w o u l d h a v e b e e n a p p e n d e d ) ,
we manually inject the delay property here with an explicit setTimeout . * /
if ( opts . delay ) {
setTimeout ( buildQueue , opts . delay ) ;
} else {
buildQueue ( ) ;
}
/* Otherwise, the call undergoes element queueing as normal. */
/* Note: To interoperate with jQuery, Velocity uses jQuery's own $.queue() stack for queuing logic. */
} else {
$ . queue ( element , opts . queue , function ( next , clearQueue ) {
/ * I f t h e c l e a r Q u e u e f l a g w a s p a s s e d i n b y t h e s t o p c o m m a n d , r e s o l v e t h i s c a l l ' s p r o m i s e . ( P r o m i s e s c a n o n l y b e r e s o l v e d o n c e ,
so it ' s fine if this is repeatedly triggered for each element in the associated call . ) * /
if ( clearQueue === true ) {
if ( promiseData . promise ) {
promiseData . resolver ( elements ) ;
}
/* Do not continue with animation queueing. */
return true ;
}
/ * T h i s f l a g i n d i c a t e s t o t h e u p c o m i n g c o m p l e t e C a l l ( ) f u n c t i o n t h a t t h i s q u e u e e n t r y w a s i n i t i a t e d b y V e l o c i t y .
See completeCall ( ) for further details . * /
Velocity . velocityQueueEntryFlag = true ;
buildQueue ( next ) ;
} ) ;
}
/ * * * * * * * * * * * * * * * * * * * * *
Auto - Dequeuing
* * * * * * * * * * * * * * * * * * * * * /
/ * A s p e r j Q u e r y ' s $ . q u e u e ( ) b e h a v i o r , t o f i r e t h e f i r s t n o n - c u s t o m - q u e u e e n t r y o n a n e l e m e n t , t h e e l e m e n t
must be dequeued if its queue stack consists * solely * of the current call . ( This can be determined by checking
for the "inprogress" item that jQuery prepends to active queue stack arrays . ) Regardless , whenever the element ' s
queue is further appended with additional items -- including $ . delay ( ) 's or even $.animate() calls, the queue' s
first entry is automatically fired . This behavior contrasts that of custom queues , which never auto - fire . * /
/ * N o t e : W h e n a n e l e m e n t s e t i s b e i n g s u b j e c t e d t o a n o n - p a r a l l e l V e l o c i t y c a l l , t h e a n i m a t i o n w i l l n o t b e g i n u n t i l
each one of the elements in the set has reached the end of its individually pre - existing queue chain . * /
/ * N o t e : U n f o r t u n a t e l y , m o s t p e o p l e d o n ' t f u l l y g r a s p j Q u e r y ' s p o w e r f u l , y e t q u i r k y , $ . q u e u e ( ) f u n c t i o n .
Lean more here : http : //stackoverflow.com/questions/1058158/can-somebody-explain-jquery-queue-to-me */
if ( ( opts . queue === "" || opts . queue === "fx" ) && $ . queue ( element ) [ 0 ] !== "inprogress" ) {
$ . dequeue ( element ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * *
Element Set Iteration
* * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * I f t h e " n o d e T y p e " p r o p e r t y e x i s t s o n t h e e l e m e n t s v a r i a b l e , w e ' r e a n i m a t i n g a s i n g l e e l e m e n t .
Place it in an array so that $ . each ( ) can iterate over it . * /
$ . each ( elements , function ( i , element ) {
/* Ensure each element in a set has a nodeType (is a real element) to avoid throwing errors. */
if ( Type . isNode ( element ) ) {
processElement . call ( element ) ;
}
} ) ;
/ * * * * * * * * * * * * * * * * * *
Option : Loop
* * * * * * * * * * * * * * * * * * /
/ * T h e l o o p o p t i o n a c c e p t s a n i n t e g e r i n d i c a t i n g h o w m a n y t i m e s t h e e l e m e n t s h o u l d l o o p b e t w e e n t h e v a l u e s i n t h e
current call 's properties map and the element' s property values prior to this call . * /
/ * N o t e : T h e l o o p o p t i o n ' s l o g i c i s p e r f o r m e d h e r e - - a f t e r e l e m e n t p r o c e s s i n g - - b e c a u s e t h e c u r r e n t c a l l n e e d s
to undergo its queue insertion prior to the loop option generating its series of constituent "reverse" calls ,
which chain after the current call . Two reverse calls ( two "alternations" ) constitute one loop . * /
var opts = $ . extend ( { } , Velocity . defaults , options ) ,
reverseCallsCount ;
opts . loop = parseInt ( opts . loop ) ;
reverseCallsCount = ( opts . loop * 2 ) - 1 ;
if ( opts . loop ) {
/ * D o u b l e t h e l o o p c o u n t t o c o n v e r t i t i n t o i t s a p p r o p r i a t e n u m b e r o f " r e v e r s e " c a l l s .
Subtract 1 from the resulting value since the current call is included in the total alternation count . * /
for ( var x = 0 ; x < reverseCallsCount ; x ++ ) {
/ * S i n c e t h e l o g i c f o r t h e r e v e r s e a c t i o n o c c u r s i n s i d e Q u e u e i n g a n d t h e r e f o r e t h i s c a l l ' s o p t i o n s o b j e c t
isn 't parsed until then as well, the current call' s delay option must be explicitly passed into the reverse
call so that the delay logic that occurs inside * Pre - Queueing * can process it . * /
var reverseOptions = {
delay : opts . delay ,
progress : opts . progress
} ;
/ * I f a c o m p l e t e c a l l b a c k w a s p a s s e d i n t o t h i s c a l l , t r a n s f e r i t t o t h e l o o p r e d i r e c t ' s f i n a l " r e v e r s e " c a l l
so that it ' s triggered when the entire redirect is complete ( and not when the very first animation is complete ) . * /
if ( x === reverseCallsCount - 1 ) {
reverseOptions . display = opts . display ;
reverseOptions . visibility = opts . visibility ;
reverseOptions . complete = opts . complete ;
}
animate ( elements , "reverse" , reverseOptions ) ;
}
}
/ * * * * * * * * * * * * * * *
Chaining
* * * * * * * * * * * * * * * /
/* Return the elements back to the call chain, with wrapped elements taking precedence in case Velocity was called via the $.fn. extension. */
return getChain ( ) ;
} ;
/* Turn Velocity into the animation function, extended with the pre-existing Velocity object. */
Velocity = $ . extend ( animate , Velocity ) ;
/* For legacy support, also expose the literal animate method. */
Velocity . animate = animate ;
/ * * * * * * * * * * * * * *
Timing
* * * * * * * * * * * * * * /
/* Ticker function. */
var ticker = window . requestAnimationFrame || rAFShim ;
/ * I n a c t i v e b r o w s e r t a b s p a u s e r A F , w h i c h r e s u l t s i n a l l a c t i v e a n i m a t i o n s i m m e d i a t e l y s p r i n t i n g t o t h e i r c o m p l e t i o n s t a t e s w h e n t h e t a b r e f o c u s e s .
To get around this , we dynamically switch rAF to setTimeout ( which the browser * doesn ' t * pause ) when the tab loses focus . We skip this for mobile
devices to avoid wasting battery power on inactive tabs . * /
/* Note: Tab focus detection doesn't work on older versions of IE, but that's okay since they don't support rAF to begin with. */
if ( ! Velocity . State . isMobile && document . hidden !== undefined ) {
document . addEventListener ( "visibilitychange" , function ( ) {
/* Reassign the rAF function (which the global tick() function uses) based on the tab's focus state. */
if ( document . hidden ) {
ticker = function ( callback ) {
/* The tick function needs a truthy first argument in order to pass its internal timestamp check. */
return setTimeout ( function ( ) { callback ( true ) } , 16 ) ;
} ;
/* The rAF loop has been paused by the browser, so we manually restart the tick. */
tick ( ) ;
} else {
ticker = window . requestAnimationFrame || rAFShim ;
}
} ) ;
}
/ * * * * * * * * * * * *
Tick
* * * * * * * * * * * * /
/* Note: All calls to Velocity are pushed to the Velocity.State.calls array, which is fully iterated through upon each tick. */
function tick ( timestamp ) {
/ * A n e m p t y t i m e s t a m p a r g u m e n t i n d i c a t e s t h a t t h i s i s t h e f i r s t t i c k o c c u r e n c e s i n c e t i c k i n g w a s t u r n e d o n .
We leverage this metadata to fully ignore the first tick pass since RAF ' s initial pass is fired whenever
the browser ' s next tick sync time occurs , which results in the first elements subjected to Velocity
calls being animated out of sync with any elements animated immediately thereafter . In short , we ignore
the first RAF tick pass so that elements being immediately consecutively animated -- instead of simultaneously animated
by the same Velocity call -- are properly batched into the same initial RAF tick and consequently remain in sync thereafter . * /
if ( timestamp ) {
/ * W e i g n o r e R A F ' s h i g h r e s o l u t i o n t i m e s t a m p s i n c e i t c a n b e s i g n i f i c a n t l y o f f s e t w h e n t h e b r o w s e r i s
under high stress ; we opt for choppiness over allowing the browser to drop huge chunks of frames . * /
var timeCurrent = ( new Date ) . getTime ( ) ;
/ * * * * * * * * * * * * * * * * * * * *
Call Iteration
* * * * * * * * * * * * * * * * * * * * /
var callsLength = Velocity . State . calls . length ;
/ * T o s p e e d u p i t e r a t i n g o v e r t h i s a r r a y , i t i s c o m p a c t e d ( f a l s e y i t e m s - - c a l l s t h a t h a v e c o m p l e t e d - - a r e r e m o v e d )
when its length has ballooned to a point that can impact tick performance . This only becomes necessary when animation
has been continuous with many elements over a long period of time ; whenever all active calls are completed , completeCall ( ) clears Velocity . State . calls . * /
if ( callsLength > 10000 ) {
Velocity . State . calls = compactSparseArray ( Velocity . State . calls ) ;
}
/* Iterate through each active call. */
for ( var i = 0 ; i < callsLength ; i ++ ) {
/* When a Velocity call is completed, its Velocity.State.calls entry is set to false. Continue on to the next call. */
if ( ! Velocity . State . calls [ i ] ) {
continue ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * *
Call - Wide Variables
* * * * * * * * * * * * * * * * * * * * * * * * /
var callContainer = Velocity . State . calls [ i ] ,
call = callContainer [ 0 ] ,
opts = callContainer [ 2 ] ,
timeStart = callContainer [ 3 ] ,
firstTick = ! ! timeStart ,
tweenDummyValue = null ;
/ * I f t i m e S t a r t i s u n d e f i n e d , t h e n t h i s i s t h e f i r s t t i m e t h a t t h i s c a l l h a s b e e n p r o c e s s e d b y t i c k ( ) .
We assign timeStart now so that its value is as close to the real animation start time as possible .
( Conversely , had timeStart been defined when this call was added to Velocity . State . calls , the delay
between that time and now would cause the first few frames of the tween to be skipped since
percentComplete is calculated relative to timeStart . ) * /
/ * F u r t h e r , s u b t r a c t 1 6 m s ( t h e a p p r o x i m a t e r e s o l u t i o n o f R A F ) f r o m t h e c u r r e n t t i m e v a l u e s o t h a t t h e
first tick iteration isn ' t wasted by animating at 0 % tween completion , which would produce the
same style value as the element ' s current value . * /
if ( ! timeStart ) {
timeStart = Velocity . State . calls [ i ] [ 3 ] = timeCurrent - 16 ;
}
/ * T h e t w e e n ' s c o m p l e t i o n p e r c e n t a g e i s r e l a t i v e t o t h e t w e e n ' s s t a r t t i m e , n o t t h e t w e e n ' s s t a r t v a l u e
( which would result in unpredictable tween durations since JavaScript ' s timers are not particularly accurate ) .
Accordingly , we ensure that percentComplete does not exceed 1. * /
var percentComplete = Math . min ( ( timeCurrent - timeStart ) / opts . duration , 1 ) ;
/ * * * * * * * * * * * * * * * * * * * * * *
Element Iteration
* * * * * * * * * * * * * * * * * * * * * * /
/* For every call, iterate through each of the elements in its set. */
for ( var j = 0 , callLength = call . length ; j < callLength ; j ++ ) {
var tweensContainer = call [ j ] ,
element = tweensContainer . element ;
/ * C h e c k t o s e e i f t h i s e l e m e n t h a s b e e n d e l e t e d m i d w a y t h r o u g h t h e a n i m a t i o n b y c h e c k i n g f o r t h e
continued existence of its data cache . If it ' s gone , skip animating this element . * /
if ( ! Data ( element ) ) {
continue ;
}
var transformPropertyExists = false ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Display & Visibility Toggling
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * I f t h e d i s p l a y o p t i o n i s s e t t o n o n - " n o n e " , s e t i t u p f r o n t s o t h a t t h e e l e m e n t c a n b e c o m e v i s i b l e b e f o r e t w e e n i n g b e g i n s .
( Otherwise , display ' s "none" value is set in completeCall ( ) once the animation has completed . ) * /
if ( opts . display !== undefined && opts . display !== null && opts . display !== "none" ) {
if ( opts . display === "flex" ) {
var flexValues = [ "-webkit-box" , "-moz-box" , "-ms-flexbox" , "-webkit-flex" ] ;
$ . each ( flexValues , function ( i , flexValue ) {
CSS . setPropertyValue ( element , "display" , flexValue ) ;
} ) ;
}
CSS . setPropertyValue ( element , "display" , opts . display ) ;
}
/* Same goes with the visibility option, but its "none" equivalent is "hidden". */
if ( opts . visibility !== undefined && opts . visibility !== "hidden" ) {
CSS . setPropertyValue ( element , "visibility" , opts . visibility ) ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * *
Property Iteration
* * * * * * * * * * * * * * * * * * * * * * * * /
/* For every element, iterate through each property. */
for ( var property in tweensContainer ) {
/* Note: In addition to property tween data, tweensContainer contains a reference to its associated element. */
if ( property !== "element" ) {
var tween = tweensContainer [ property ] ,
currentValue ,
/ * E a s i n g c a n e i t h e r b e a p r e - g e n e r e a t e d f u n c t i o n o r a s t r i n g t h a t r e f e r e n c e s a p r e - r e g i s t e r e d e a s i n g
on the Velocity . Easings object . In either case , return the appropriate easing * function * . * /
easing = Type . isString ( tween . easing ) ? Velocity . Easings [ tween . easing ] : tween . easing ;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Current Value Calculation
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ * I f t h i s i s t h e l a s t t i c k p a s s ( i f w e ' v e r e a c h e d 1 0 0 % c o m p l e t i o n f o r t h i s t w e e n ) ,
ensure that currentValue is explicitly set to its target endValue so that it ' s not subjected to any rounding . * /
if ( percentComplete === 1 ) {
currentValue = tween . endValue ;
/* Otherwise, calculate currentValue based on the current delta from startValue. */
} else {
var tweenDelta = tween . endValue - tween . startValue ;
currentValue = tween . startValue + ( tweenDelta * easing ( percentComplete , opts , tweenDelta ) ) ;
/* If no value change is occurring, don't proceed with DOM updating. */
if ( ! firstTick && ( currentValue === tween . currentValue ) ) {
continue ;
}
}
tween . currentValue = currentValue ;
/ * I f w e ' r e t w e e n i n g a f a k e ' t w e e n ' p r o p e r t y i n o r d e r t o l o g t r a n s i t i o n v a l u e s , u p d a t e t h e o n e - p e r - c a l l v a r i a b l e s o t h a t
2015-09-26 07:51:26 -07:00
it can be passed into the progress callback . * /
2015-06-26 08:53:49 -07:00
if ( property === "tween" ) {
tweenDummyValue = currentValue ;
} else {
/ * * * * * * * * * * * * * * * * * *
Hooks : Part I
* * * * * * * * * * * * * * * * * * /
/ * F o r h o o k e d p r o p e r t i e s , t h e n e w l y - u p d a t e d r o o t P r o p e r t y V a l u e C a c h e i s c a c h e d o n t o t h e e l e m e n t s o t h a t i t c a n b e u s e d
for subsequent hooks in this call that are associated with the same root property . If we didn ' t cache the updated
rootPropertyValue , each subsequent update to the root property in this tick pass would reset the previous hook ' s
updates to rootPropertyValue prior to injection . A nice performance byproduct of rootPropertyValue caching is that
subsequently chained animations using the same hookRoot but a different hook can use this cached rootPropertyValue . * /
if ( CSS . Hooks . registered [ property ] ) {
var hookRoot = CSS . Hooks . getRoot ( property ) ,
rootPropertyValueCache = Data ( element ) . rootPropertyValueCache [ hookRoot ] ;
if ( rootPropertyValueCache ) {
tween . rootPropertyValue = rootPropertyValueCache ;
}
}
/ * * * * * * * * * * * * * * * * *
DOM Update
* * * * * * * * * * * * * * * * * /
/* setPropertyValue() returns an array of the property name and property value post any normalization that may have been performed. */
/* Note: To solve an IE<=8 positioning bug, the unit type is dropped when setting a property value of 0. */
var adjustedSetData = CSS . setPropertyValue ( element , /* SET */
property ,
tween . currentValue + ( parseFloat ( currentValue ) === 0 ? "" : tween . unitType ) ,
tween . rootPropertyValue ,
tween . scrollData ) ;
/ * * * * * * * * * * * * * * * * * * *
Hooks : Part II
* * * * * * * * * * * * * * * * * * * /
/* Now that we have the hook's updated rootPropertyValue (the post-processed value provided by adjustedSetData), cache it onto the element. */
if ( CSS . Hooks . registered [ property ] ) {
/* Since adjustedSetData contains normalized data ready for DOM updating, the rootPropertyValue needs to be re-extracted from its normalized form. ?? */
if ( CSS . Normalizations . registered [ hookRoot ] ) {
Data ( element ) . rootPropertyValueCache [ hookRoot ] = CSS . Normalizations . registered [ hookRoot ] ( "extract" , null , adjustedSetData [ 1 ] ) ;
} else {
Data ( element ) . rootPropertyValueCache [ hookRoot ] = adjustedSetData [ 1 ] ;
}
}
/ * * * * * * * * * * * * * * *
Transforms
* * * * * * * * * * * * * * * /
/* Flag whether a transform property is being animated so that flushTransformCache() can be triggered once this tick pass is complete. */
if ( adjustedSetData [ 0 ] === "transform" ) {
transformPropertyExists = true ;
}
}
}
}
/ * * * * * * * * * * * * * * * *
mobileHA
* * * * * * * * * * * * * * * * /
/ * I f m o b i l e H A i s e n a b l e d , s e t t h e t r a n s l a t e 3 d t r a n s f o r m t o n u l l t o f o r c e h a r d w a r e a c c e l e r a t i o n .
It 's safe to override this property since Velocity doesn' t actually support its animation ( hooks are used in its place ) . * /
if ( opts . mobileHA ) {
/* Don't set the null transform hack if we've already done so. */
if ( Data ( element ) . transformCache . translate3d === undefined ) {
/* All entries on the transformCache object are later concatenated into a single transform string via flushTransformCache(). */
Data ( element ) . transformCache . translate3d = "(0px, 0px, 0px)" ;
transformPropertyExists = true ;
}
}
if ( transformPropertyExists ) {
CSS . flushTransformCache ( element ) ;
}
}
/ * T h e n o n - " n o n e " d i s p l a y v a l u e i s o n l y a p p l i e d t o a n e l e m e n t o n c e - - w h e n i t s a s s o c i a t e d c a l l i s f i r s t t i c k e d t h r o u g h .
Accordingly , it 's set to false so that it isn' t re - processed by this call in the next tick . * /
if ( opts . display !== undefined && opts . display !== "none" ) {
Velocity . State . calls [ i ] [ 2 ] . display = false ;
}
if ( opts . visibility !== undefined && opts . visibility !== "hidden" ) {
Velocity . State . calls [ i ] [ 2 ] . visibility = false ;
}
/* Pass the elements and the timing data (percentComplete, msRemaining, timeStart, tweenDummyValue) into the progress callback. */
if ( opts . progress ) {
opts . progress . call ( callContainer [ 1 ] ,
callContainer [ 1 ] ,
percentComplete ,
Math . max ( 0 , ( timeStart + opts . duration ) - timeCurrent ) ,
timeStart ,
tweenDummyValue ) ;
}
/* If this call has finished tweening, pass its index to completeCall() to handle call cleanup. */
if ( percentComplete === 1 ) {
completeCall ( i ) ;
}
}
}
/* Note: completeCall() sets the isTicking flag to false when the last call on Velocity.State.calls has completed. */
if ( Velocity . State . isTicking ) {
ticker ( tick ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * *
Call Completion
* * * * * * * * * * * * * * * * * * * * * * /
/* Note: Unlike tick(), which processes all active calls at once, call completion is handled on a per-call basis. */
function completeCall ( callIndex , isStopped ) {
/* Ensure the call exists. */
if ( ! Velocity . State . calls [ callIndex ] ) {
return false ;
}
/* Pull the metadata from the call. */
var call = Velocity . State . calls [ callIndex ] [ 0 ] ,
elements = Velocity . State . calls [ callIndex ] [ 1 ] ,
opts = Velocity . State . calls [ callIndex ] [ 2 ] ,
resolver = Velocity . State . calls [ callIndex ] [ 4 ] ;
var remainingCallsExist = false ;
/ * * * * * * * * * * * * * * * * * * * * * * * * *
Element Finalization
* * * * * * * * * * * * * * * * * * * * * * * * * /
for ( var i = 0 , callLength = call . length ; i < callLength ; i ++ ) {
var element = call [ i ] . element ;
/* If the user set display to "none" (intending to hide the element), set it now that the animation has completed. */
/* Note: display:none isn't set when calls are manually stopped (via Velocity("stop"). */
/* Note: Display gets ignored with "reverse" calls and infinite loops, since this behavior would be undesirable. */
if ( ! isStopped && ! opts . loop ) {
if ( opts . display === "none" ) {
CSS . setPropertyValue ( element , "display" , opts . display ) ;
}
if ( opts . visibility === "hidden" ) {
CSS . setPropertyValue ( element , "visibility" , opts . visibility ) ;
}
}
/ * I f t h e e l e m e n t ' s q u e u e i s e m p t y ( i f o n l y t h e " i n p r o g r e s s " i t e m i s l e f t a t p o s i t i o n 0 ) o r i f i t s q u e u e i s a b o u t t o r u n
a non - Velocity - initiated entry , turn off the isAnimating flag . A non - Velocity - initiatied queue entry ' s logic might alter
an element 's CSS values and thereby cause Velocity' s cached value data to go stale . To detect if a queue entry was initiated by Velocity ,
we check for the existence of our special Velocity . queueEntryFlag declaration , which minifiers won ' t rename since the flag
is assigned to jQuery 's global $ object and thus exists out of Velocity' s own scope . * /
if ( opts . loop !== true && ( $ . queue ( element ) [ 1 ] === undefined || ! /\.velocityQueueEntryFlag/i . test ( $ . queue ( element ) [ 1 ] ) ) ) {
/* The element may have been deleted. Ensure that its data cache still exists before acting on it. */
if ( Data ( element ) ) {
Data ( element ) . isAnimating = false ;
/* Clear the element's rootPropertyValueCache, which will become stale. */
Data ( element ) . rootPropertyValueCache = { } ;
var transformHAPropertyExists = false ;
/* If any 3D transform subproperty is at its default value (regardless of unit type), remove it. */
$ . each ( CSS . Lists . transforms3D , function ( i , transformName ) {
var defaultValue = /^scale/ . test ( transformName ) ? 1 : 0 ,
currentValue = Data ( element ) . transformCache [ transformName ] ;
if ( Data ( element ) . transformCache [ transformName ] !== undefined && new RegExp ( "^\\(" + defaultValue + "[^.]" ) . test ( currentValue ) ) {
transformHAPropertyExists = true ;
delete Data ( element ) . transformCache [ transformName ] ;
}
} ) ;
/* Mobile devices have hardware acceleration removed at the end of the animation in order to avoid hogging the GPU's memory. */
if ( opts . mobileHA ) {
transformHAPropertyExists = true ;
delete Data ( element ) . transformCache . translate3d ;
}
/* Flush the subproperty removals to the DOM. */
if ( transformHAPropertyExists ) {
CSS . flushTransformCache ( element ) ;
}
/* Remove the "velocity-animating" indicator class. */
CSS . Values . removeClass ( element , "velocity-animating" ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * *
Option : Complete
* * * * * * * * * * * * * * * * * * * * * /
/* Complete is fired once per call (not once per element) and is passed the full raw DOM element set as both its context and its first argument. */
/* Note: Callbacks aren't fired when calls are manually stopped (via Velocity("stop"). */
if ( ! isStopped && opts . complete && ! opts . loop && ( i === callLength - 1 ) ) {
/* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */
try {
opts . complete . call ( elements , elements ) ;
} catch ( error ) {
setTimeout ( function ( ) { throw error ; } , 1 ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * *
Promise Resolving
* * * * * * * * * * * * * * * * * * * * * * /
/* Note: Infinite loops don't return promises. */
if ( resolver && opts . loop !== true ) {
resolver ( elements ) ;
}
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Option : Loop ( Infinite )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * /
if ( Data ( element ) && opts . loop === true && ! isStopped ) {
/ * I f a r o t a t e X / Y / Z p r o p e r t y i s b e i n g a n i m a t e d t o 3 6 0 d e g w i t h l o o p : t r u e , s w a p t w e e n s t a r t / e n d v a l u e s t o e n a b l e
continuous iterative rotation looping . ( Otherise , the element would just rotate back and forth . ) * /
$ . each ( Data ( element ) . tweensContainer , function ( propertyName , tweenContainer ) {
if ( /^rotate/ . test ( propertyName ) && parseFloat ( tweenContainer . endValue ) === 360 ) {
tweenContainer . endValue = 0 ;
tweenContainer . startValue = 360 ;
}
if ( /^backgroundPosition/ . test ( propertyName ) && parseFloat ( tweenContainer . endValue ) === 100 && tweenContainer . unitType === "%" ) {
tweenContainer . endValue = 0 ;
tweenContainer . startValue = 100 ;
}
} ) ;
Velocity ( element , "reverse" , { loop : true , delay : opts . delay } ) ;
}
/ * * * * * * * * * * * * * * *
Dequeueing
* * * * * * * * * * * * * * * /
/ * F i r e t h e n e x t c a l l i n t h e q u e u e s o l o n g a s t h i s c a l l ' s q u e u e w a s n ' t s e t t o f a l s e ( t o t r i g g e r a p a r a l l e l a n i m a t i o n ) ,
which would have already caused the next call to fire . Note : Even if the end of the animation queue has been reached ,
$ . dequeue ( ) must still be called in order to completely clear jQuery ' s animation queue . * /
if ( opts . queue !== false ) {
$ . dequeue ( element , opts . queue ) ;
}
}
/ * * * * * * * * * * * * * * * * * * * * * * * *
Calls Array Cleanup
* * * * * * * * * * * * * * * * * * * * * * * * /
/ * S i n c e t h i s c a l l i s c o m p l e t e , s e t i t t o f a l s e s o t h a t t h e r A F t i c k s k i p s i t . T h i s a r r a y i s l a t e r c o m p a c t e d v i a c o m p a c t S p a r s e A r r a y ( ) .
( For performance reasons , the call is set to false instead of being deleted from the array : http : //www.html5rocks.com/en/tutorials/speed/v8/) */
Velocity . State . calls [ callIndex ] = false ;
/ * I t e r a t e t h r o u g h t h e c a l l s a r r a y t o d e t e r m i n e i f t h i s w a s t h e f i n a l i n - p r o g r e s s a n i m a t i o n .
If so , set a flag to end ticking and clear the calls array . * /
for ( var j = 0 , callsLength = Velocity . State . calls . length ; j < callsLength ; j ++ ) {
if ( Velocity . State . calls [ j ] !== false ) {
remainingCallsExist = true ;
break ;
}
}
if ( remainingCallsExist === false ) {
/* tick() will detect this flag upon its next iteration and subsequently turn itself off. */
Velocity . State . isTicking = false ;
/* Clear the calls array so that its length is reset. */
delete Velocity . State . calls ;
Velocity . State . calls = [ ] ;
}
}
/ * * * * * * * * * * * * * * * * * *
Frameworks
* * * * * * * * * * * * * * * * * * /
/ * B o t h j Q u e r y a n d Z e p t o a l l o w t h e i r $ . f n o b j e c t t o b e e x t e n d e d t o a l l o w w r a p p e d e l e m e n t s t o b e s u b j e c t e d t o p l u g i n c a l l s .
If either framework is loaded , register a "velocity" extension pointing to Velocity ' s core animate ( ) method . Velocity
also registers itself onto a global container ( window . jQuery || window . Zepto || window ) so that certain features are
accessible beyond just a per - element scope . This master object contains an . animate ( ) method , which is later assigned to $ . fn
( if jQuery or Zepto are present ) . Accordingly , Velocity can both act on wrapped DOM elements and stand alone for targeting raw DOM elements . * /
global . Velocity = Velocity ;
if ( global !== window ) {
/* Assign the element function to Velocity's core animate() method. */
global . fn . velocity = animate ;
/* Assign the object function's defaults to Velocity's global defaults object. */
global . fn . velocity . defaults = Velocity . defaults ;
}
/ * * * * * * * * * * * * * * * * * * * * * * *
Packaged Redirects
* * * * * * * * * * * * * * * * * * * * * * * /
/* slideUp, slideDown */
$ . each ( [ "Down" , "Up" ] , function ( i , direction ) {
Velocity . Redirects [ "slide" + direction ] = function ( element , options , elementsIndex , elementsSize , elements , promiseData ) {
var opts = $ . extend ( { } , options ) ,
begin = opts . begin ,
complete = opts . complete ,
computedValues = { height : "" , marginTop : "" , marginBottom : "" , paddingTop : "" , paddingBottom : "" } ,
inlineValues = { } ;
if ( opts . display === undefined ) {
/* Show the element before slideDown begins and hide the element after slideUp completes. */
/* Note: Inline elements cannot have dimensions animated, so they're reverted to inline-block. */
opts . display = ( direction === "Down" ? ( Velocity . CSS . Values . getDisplayType ( element ) === "inline" ? "inline-block" : "block" ) : "none" ) ;
}
opts . begin = function ( ) {
/* If the user passed in a begin callback, fire it now. */
begin && begin . call ( elements , elements ) ;
/* Cache the elements' original vertical dimensional property values so that we can animate back to them. */
for ( var property in computedValues ) {
inlineValues [ property ] = element . style [ property ] ;
/ * F o r s l i d e D o w n , u s e f o r c e f e e d i n g t o a n i m a t e a l l v e r t i c a l p r o p e r t i e s f r o m 0 . F o r s l i d e U p ,
use forcefeeding to start from computed values and animate down to 0. * /
var propertyValue = Velocity . CSS . getPropertyValue ( element , property ) ;
computedValues [ property ] = ( direction === "Down" ) ? [ propertyValue , 0 ] : [ 0 , propertyValue ] ;
}
/* Force vertical overflow content to clip so that sliding works as expected. */
inlineValues . overflow = element . style . overflow ;
element . style . overflow = "hidden" ;
}
opts . complete = function ( ) {
/* Reset element to its pre-slide inline values once its slide animation is complete. */
for ( var property in inlineValues ) {
element . style [ property ] = inlineValues [ property ] ;
}
/* If the user passed in a complete callback, fire it now. */
complete && complete . call ( elements , elements ) ;
promiseData && promiseData . resolver ( elements ) ;
} ;
Velocity ( element , computedValues , opts ) ;
} ;
} ) ;
/* fadeIn, fadeOut */
$ . each ( [ "In" , "Out" ] , function ( i , direction ) {
Velocity . Redirects [ "fade" + direction ] = function ( element , options , elementsIndex , elementsSize , elements , promiseData ) {
var opts = $ . extend ( { } , options ) ,
propertiesMap = { opacity : ( direction === "In" ) ? 1 : 0 } ,
originalComplete = opts . complete ;
/ * S i n c e r e d i r e c t s a r e t r i g g e r e d i n d i v i d u a l l y f o r e a c h e l e m e n t i n t h e a n i m a t e d s e t , a v o i d r e p e a t e d l y t r i g g e r i n g
callbacks by firing them only when the final element has been reached . * /
if ( elementsIndex !== elementsSize - 1 ) {
opts . complete = opts . begin = null ;
} else {
opts . complete = function ( ) {
if ( originalComplete ) {
originalComplete . call ( elements , elements ) ;
}
promiseData && promiseData . resolver ( elements ) ;
}
}
/* If a display was passed in, use it. Otherwise, default to "none" for fadeOut or the element-specific default for fadeIn. */
/* Note: We allow users to pass in "null" to skip display setting altogether. */
if ( opts . display === undefined ) {
opts . display = ( direction === "In" ? "auto" : "none" ) ;
}
Velocity ( this , propertiesMap , opts ) ;
} ;
} ) ;
return Velocity ;
} ( ( window . jQuery || window . Zepto || window ) , window , document ) ;
} ) ) ;
/ * * * * * * * * * * * * * * * * * *
Known Issues
* * * * * * * * * * * * * * * * * * /
/ * T h e C S S s p e c m a n d a t e s t h a t t h e t r a n s l a t e X / Y / Z t r a n s f o r m s a r e % - r e l a t i v e t o t h e e l e m e n t i t s e l f - - n o t i t s p a r e n t .
Velocity , however , doesn ' t make this distinction . Thus , converting to or from the % unit with these subproperties
will produce an inaccurate conversion value . The same issue exists with the cx / cy attributes of SVG circles and ellipses . * /