UNPKG

209 kBJavaScriptView Raw
1/*! VelocityJS.org (1.0.0). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */
2
3/*************************
4 Velocity jQuery Shim
5*************************/
6
7/*! VelocityJS.org jQuery Shim (1.0.0-rc1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */
8
9/* 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. */
10/* These shimmed functions are only used if jQuery isn't present. If both this shim and jQuery are loaded, Velocity defaults to jQuery proper. */
11/* Browser support: Using this shim instead of jQuery proper removes support for IE8. */
12
13;(function (window) {
14 /***************
15 Setup
16 ***************/
17
18 /* If jQuery is already loaded, there's no point in loading this shim. */
19 if (window.jQuery) {
20 return;
21 }
22
23 /* jQuery base. */
24 var $ = function (selector, context) {
25 return new $.fn.init(selector, context);
26 };
27
28 /********************
29 Private Methods
30 ********************/
31
32 /* jQuery */
33 $.isWindow = function (obj) {
34 /* jshint eqeqeq: false */
35 return obj != null && obj == obj.window;
36 };
37
38 /* jQuery */
39 $.type = function (obj) {
40 if (obj == null) {
41 return obj + "";
42 }
43
44 return typeof obj === "object" || typeof obj === "function" ?
45 class2type[toString.call(obj)] || "object" :
46 typeof obj;
47 };
48
49 /* jQuery */
50 $.isArray = Array.isArray || function (obj) {
51 return $.type(obj) === "array";
52 };
53
54 /* jQuery */
55 function isArraylike (obj) {
56 var length = obj.length,
57 type = $.type(obj);
58
59 if (type === "function" || $.isWindow(obj)) {
60 return false;
61 }
62
63 if (obj.nodeType === 1 && length) {
64 return true;
65 }
66
67 return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj;
68 }
69
70 /***************
71 $ Methods
72 ***************/
73
74 /* jQuery: Support removed for IE<9. */
75 $.isPlainObject = function (obj) {
76 var key;
77
78 if (!obj || $.type(obj) !== "object" || obj.nodeType || $.isWindow(obj)) {
79 return false;
80 }
81
82 try {
83 if (obj.constructor &&
84 !hasOwn.call(obj, "constructor") &&
85 !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
86 return false;
87 }
88 } catch (e) {
89 return false;
90 }
91
92 for (key in obj) {}
93
94 return key === undefined || hasOwn.call(obj, key);
95 };
96
97 /* jQuery */
98 $.each = function(obj, callback, args) {
99 var value,
100 i = 0,
101 length = obj.length,
102 isArray = isArraylike(obj);
103
104 if (args) {
105 if (isArray) {
106 for (; i < length; i++) {
107 value = callback.apply(obj[i], args);
108
109 if (value === false) {
110 break;
111 }
112 }
113 } else {
114 for (i in obj) {
115 value = callback.apply(obj[i], args);
116
117 if (value === false) {
118 break;
119 }
120 }
121 }
122
123 } else {
124 if (isArray) {
125 for (; i < length; i++) {
126 value = callback.call(obj[i], i, obj[i]);
127
128 if (value === false) {
129 break;
130 }
131 }
132 } else {
133 for (i in obj) {
134 value = callback.call(obj[i], i, obj[i]);
135
136 if (value === false) {
137 break;
138 }
139 }
140 }
141 }
142
143 return obj;
144 };
145
146 /* Custom */
147 $.data = function (node, key, value) {
148 /* $.getData() */
149 if (value === undefined) {
150 var id = node[$.expando],
151 store = id && cache[id];
152
153 if (key === undefined) {
154 return store;
155 } else if (store) {
156 if (key in store) {
157 return store[key];
158 }
159 }
160 /* $.setData() */
161 } else if (key !== undefined) {
162 var id = node[$.expando] || (node[$.expando] = ++$.uuid);
163
164 cache[id] = cache[id] || {};
165 cache[id][key] = value;
166
167 return value;
168 }
169 };
170
171 /* Custom */
172 $.removeData = function (node, keys) {
173 var id = node[$.expando],
174 store = id && cache[id];
175
176 if (store) {
177 $.each(keys, function(_, key) {
178 delete store[key];
179 });
180 }
181 };
182
183 /* jQuery */
184 $.extend = function () {
185 var src, copyIsArray, copy, name, options, clone,
186 target = arguments[0] || {},
187 i = 1,
188 length = arguments.length,
189 deep = false;
190
191 if (typeof target === "boolean") {
192 deep = target;
193
194 target = arguments[i] || {};
195 i++;
196 }
197
198 if (typeof target !== "object" && $.type(target) !== "function") {
199 target = {};
200 }
201
202 if (i === length) {
203 target = this;
204 i--;
205 }
206
207 for (; i < length; i++) {
208 if ((options = arguments[i]) != null) {
209 for (name in options) {
210 src = target[name];
211 copy = options[name];
212
213 if (target === copy) {
214 continue;
215 }
216
217 if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = $.isArray(copy)))) {
218 if (copyIsArray) {
219 copyIsArray = false;
220 clone = src && $.isArray(src) ? src : [];
221
222 } else {
223 clone = src && $.isPlainObject(src) ? src : {};
224 }
225
226 target[name] = $.extend(deep, clone, copy);
227
228 } else if (copy !== undefined) {
229 target[name] = copy;
230 }
231 }
232 }
233 }
234
235 return target;
236 };
237
238 /* jQuery 1.4.3 */
239 $.queue = function (elem, type, data) {
240 function $makeArray (arr, results) {
241 var ret = results || [];
242
243 if (arr != null) {
244 if (isArraylike(Object(arr))) {
245 /* $.merge */
246 (function(first, second) {
247 var len = +second.length,
248 j = 0,
249 i = first.length;
250
251 while (j < len) {
252 first[i++] = second[j++];
253 }
254
255 if (len !== len) {
256 while (second[j] !== undefined) {
257 first[i++] = second[j++];
258 }
259 }
260
261 first.length = i;
262
263 return first;
264 })(ret, typeof arr === "string" ? [arr] : arr);
265 } else {
266 [].push.call(ret, arr);
267 }
268 }
269
270 return ret;
271 }
272
273 if (!elem) {
274 return;
275 }
276
277 type = (type || "fx") + "queue";
278
279 var q = $.data(elem, type);
280
281 if (!data) {
282 return q || [];
283 }
284
285 if (!q || $.isArray(data)) {
286 q = $.data(elem, type, $makeArray(data));
287 } else {
288 q.push(data);
289 }
290
291 return q;
292 };
293
294 /* jQuery 1.4.3 */
295 $.dequeue = function (elems, type) {
296 /* Custom: Embed element iteration. */
297 $.each(elems.nodeType ? [ elems ] : elems, function(i, elem) {
298 type = type || "fx";
299
300 var queue = $.queue(elem, type),
301 fn = queue.shift();
302
303 if (fn === "inprogress") {
304 fn = queue.shift();
305 }
306
307 if (fn) {
308 if (type === "fx") {
309 queue.unshift("inprogress");
310 }
311
312 fn.call(elem, function() {
313 $.dequeue(elem, type);
314 });
315 }
316 });
317 };
318
319 /******************
320 $.fn Methods
321 ******************/
322
323 /* jQuery */
324 $.fn = $.prototype = {
325 init: function(selector) {
326 /* Just return the element wrapped inside an array; don't proceed with the actual jQuery node wrapping process. */
327 if (selector.nodeType) {
328 this[0] = selector;
329
330 return this;
331 } else {
332 throw new Error("Not a DOM node.");
333 }
334 },
335
336 offset: function () {
337 /* jQuery altered code: Dropped disconnected DOM node checking and iOS3+BlackBerry support. */
338 var box = this[0].getBoundingClientRect();
339
340 return {
341 top: box.top + (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || 0),
342 left: box.left + (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || 0)
343 };
344 },
345
346 position: function () {
347 /* jQuery */
348 function offsetParent() {
349 var offsetParent = this.offsetParent || document;
350
351 while (offsetParent && (!offsetParent.nodeType.toLowerCase === "html" && offsetParent.style.position === "static")) {
352 offsetParent = offsetParent.offsetParent;
353 }
354
355 return offsetParent || document;
356 }
357
358 /* Zepto */
359 var elem = this[0],
360 offsetParent = offsetParent.apply(elem),
361 offset = this.offset(),
362 parentOffset = /^(?:body|html)$/i.test(offsetParent.nodeName) ? { top: 0, left: 0 } : $(offsetParent).offset()
363
364 offset.top -= parseFloat(elem.style.marginTop) || 0;
365 offset.left -= parseFloat(elem.style.marginLeft) || 0;
366
367 if (offsetParent.style) {
368 parentOffset.top += parseFloat(offsetParent.style.borderTopWidth) || 0
369 parentOffset.left += parseFloat(offsetParent.style.borderLeftWidth) || 0
370 }
371
372 return {
373 top: offset.top - parentOffset.top,
374 left: offset.left - parentOffset.left
375 };
376 }
377 };
378
379 /**********************
380 Private Variables
381 **********************/
382
383 /* For $.data() */
384 var cache = {};
385 $.expando = "velocity" + (new Date().getTime());
386 $.uuid = 0;
387
388 /* For $.queue() */
389 var class2type = {},
390 hasOwn = class2type.hasOwnProperty,
391 toString = class2type.toString;
392
393 var types = "Boolean Number String Function Array Date RegExp Object Error".split(" ");
394 for (var i = 0; i < types.length; i++) {
395 class2type["[object " + types[i] + "]"] = types[i].toLowerCase();
396 }
397
398 /* Makes $(node) possible, without having to call init. */
399 $.fn.init.prototype = $.fn;
400
401 /* Globalize Velocity onto the window, and assign its Utilities property. */
402 window.Velocity = { Utilities: $ };
403})(window);
404
405/******************
406 Velocity.js
407******************/
408
409;(function (factory) {
410 /* CommonJS module. */
411 if (typeof module === "object" && typeof module.exports === "object") {
412 module.exports = factory();
413 /* AMD module. */
414 } else if (typeof define === "function" && define.amd) {
415 define(factory);
416 /* Browser globals. */
417 } else {
418 factory();
419 }
420}(function() {
421return function (global, window, document, undefined) {
422
423 /***************
424 Summary
425 ***************/
426
427 /*
428 - CSS: CSS stack that works independently from the rest of Velocity.
429 - animate(): Core animation method that iterates over the targeted elements and queues the incoming call onto each element individually.
430 - Pre-Queueing: Prepare the element for animation by instantiating its data cache and processing the call's options.
431 - Queueing: The logic that runs once the call has reached its point of execution in the element's $.queue() stack.
432 Most logic is placed here to avoid risking it becoming stale (if the element's properties have changed).
433 - Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container.
434 - tick(): The single requestAnimationFrame loop responsible for tweening all in-progress calls.
435 - completeCall(): Handles the cleanup process for each Velocity call.
436 */
437
438 /*********************
439 Helper Functions
440 *********************/
441
442 /* IE detection. Gist: https://gist.github.com/julianshapiro/9098609 */
443 var IE = (function() {
444 if (document.documentMode) {
445 return document.documentMode;
446 } else {
447 for (var i = 7; i > 4; i--) {
448 var div = document.createElement("div");
449
450 div.innerHTML = "<!--[if IE " + i + "]><span></span><![endif]-->";
451
452 if (div.getElementsByTagName("span").length) {
453 div = null;
454
455 return i;
456 }
457 }
458 }
459
460 return undefined;
461 })();
462
463 /* rAF shim. Gist: https://gist.github.com/julianshapiro/9497513 */
464 var rAFShim = (function() {
465 var timeLast = 0;
466
467 return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
468 var timeCurrent = (new Date()).getTime(),
469 timeDelta;
470
471 /* Dynamically set delay on a per-tick basis to match 60fps. */
472 /* Technique by Erik Moller. MIT license: https://gist.github.com/paulirish/1579671 */
473 timeDelta = Math.max(0, 16 - (timeCurrent - timeLast));
474 timeLast = timeCurrent + timeDelta;
475
476 return setTimeout(function() { callback(timeCurrent + timeDelta); }, timeDelta);
477 };
478 })();
479
480 /* Array compacting. Copyright Lo-Dash. MIT License: https://github.com/lodash/lodash/blob/master/LICENSE.txt */
481 function compactSparseArray (array) {
482 var index = -1,
483 length = array ? array.length : 0,
484 result = [];
485
486 while (++index < length) {
487 var value = array[index];
488
489 if (value) {
490 result.push(value);
491 }
492 }
493
494 return result;
495 }
496
497 function sanitizeElements (elements) {
498 /* Unwrap jQuery/Zepto objects. */
499 if (Type.isWrapped(elements)) {
500 elements = [].slice.call(elements);
501 /* Wrap a single element in an array so that $.each() can iterate with the element instead of its node's children. */
502 } else if (Type.isNode(elements)) {
503 elements = [ elements ];
504 }
505
506 return elements;
507 }
508
509 var Type = {
510 isString: function (variable) {
511 return (typeof variable === "string");
512 },
513
514 isArray: Array.isArray || function (variable) {
515 return Object.prototype.toString.call(variable) === "[object Array]";
516 },
517
518 isFunction: function (variable) {
519 return Object.prototype.toString.call(variable) === "[object Function]";
520 },
521
522 isNode: function (variable) {
523 return variable && variable.nodeType;
524 },
525
526 /* Copyright Martin Bohm. MIT License: https://gist.github.com/Tomalak/818a78a226a0738eaade */
527 isNodeList: function (variable) {
528 return typeof variable === "object" &&
529 /^\[object (HTMLCollection|NodeList|Object)\]$/.test(Object.prototype.toString.call(variable)) &&
530 variable.length !== undefined &&
531 (variable.length === 0 || (typeof variable[0] === "object" && variable[0].nodeType > 0));
532 },
533
534 /* Determine if variable is a wrapped jQuery or Zepto element. */
535 isWrapped: function (variable) {
536 return variable && (variable.jquery || (window.Zepto && window.Zepto.zepto.isZ(variable)));
537 },
538
539 isSVG: function (variable) {
540 return window.SVGElement && (variable instanceof SVGElement);
541 },
542
543 isEmptyObject: function (variable) {
544 var name;
545
546 for (name in variable) {
547 return false;
548 }
549
550 return true;
551 }
552 };
553
554 /*****************
555 Dependencies
556 *****************/
557
558 var $,
559 isJQuery = false;
560
561 if (global.fn && global.fn.jquery) {
562 $ = global;
563 isJQuery = true;
564 } else {
565 $ = window.Velocity.Utilities;
566 }
567
568 if (IE <= 8 && !isJQuery) {
569 throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity.");
570 } else if (IE <= 7) {
571 /* Revert to jQuery's $.animate(), and lose Velocity's extra features. */
572 jQuery.fn.velocity = jQuery.fn.animate;
573
574 /* Now that $.fn.velocity is aliased, abort this Velocity declaration. */
575 return;
576 }
577
578 /* Shorthand alias for jQuery's $.data() utility. */
579 function Data (element) {
580 /* Hardcode a reference to the plugin name. */
581 var response = $.data(element, "velocity");
582
583 /* jQuery <=1.4.2 returns null instead of undefined when no match is found. We normalize this behavior. */
584 return response === null ? undefined : response;
585 };
586
587 /*****************
588 Constants
589 *****************/
590
591 var DURATION_DEFAULT = 400,
592 EASING_DEFAULT = "swing";
593
594 /*************
595 State
596 *************/
597
598 /* Note: The global object also doubles as a publicly-accessible data store for the purposes of unit testing. */
599 /* Note: Alias the lowercase and uppercase variants of "velocity" to minimize user confusion due to the lowercase nature of the $.fn extension. */
600 var Velocity = {
601 /* Container for page-wide Velocity state data. */
602 State: {
603 /* Detect mobile devices to determine if mobileHA should be turned on. */
604 isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
605 /* The mobileHA option's behavior changes on older Android devices (Gingerbread, versions 2.3.3-2.3.7). */
606 isAndroid: /Android/i.test(navigator.userAgent),
607 isGingerbread: /Android 2\.3\.[3-7]/i.test(navigator.userAgent),
608 isChrome: window.chrome,
609 isFirefox: /Firefox/i.test(navigator.userAgent),
610 /* Create a cached element for re-use when checking for CSS property prefixes. */
611 prefixElement: document.createElement("div"),
612 /* Cache every prefix match to avoid repeating lookups. */
613 prefixMatches: {},
614 /* Cache the anchor used for animating window scrolling. */
615 scrollAnchor: null,
616 /* Cache the property names associated with the scroll anchor. */
617 scrollPropertyLeft: null,
618 scrollPropertyTop: null,
619 /* Keep track of whether our RAF tick is running. */
620 isTicking: false,
621 /* Container for every in-progress call to Velocity. */
622 calls: []
623 },
624 /* Velocity's custom CSS stack. Made global for unit testing. */
625 CSS: { /* Defined below. */ },
626 /* Defined by Velocity's optional jQuery shim. */
627 Utilities: $,
628 /* Container for the user's custom animation sequences that are referenced by name in place of a properties map object. */
629 Sequences: {
630 /* Manually registered by the user. Learn more: VelocityJS.org/#sequences */
631 },
632 Easings: {
633 /* Defined below. */
634 },
635 /* Attempt to use ES6 Promises by default. Users can override this with a third-party promises library. */
636 Promise: window.Promise,
637 /* Page-wide option defaults, which can be overriden by the user. */
638 defaults: {
639 queue: "",
640 duration: DURATION_DEFAULT,
641 easing: EASING_DEFAULT,
642 begin: null,
643 complete: null,
644 progress: null,
645 display: undefined,
646 loop: false,
647 delay: false,
648 mobileHA: true,
649 /* Set to false to prevent property values from being cached between consecutive Velocity-initiated chain calls. */
650 _cacheValues: true
651 },
652 /* A design goal of Velocity is to cache data wherever possible in order to avoid DOM requerying.
653 Accordingly, each element has a data cache instantiated on it. */
654 init: function (element) {
655 $.data(element, "velocity", {
656 /* Store whether this is an SVG element, since its properties are retrieved and updated differently than standard HTML elements. */
657 isSVG: Type.isSVG(element),
658 /* Keep track of whether the element is currently being animated by Velocity.
659 This is used to ensure that property values are not transferred between non-consecutive (stale) calls. */
660 isAnimating: false,
661 /* A reference to the element's live computedStyle object. Learn more here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */
662 computedStyle: null,
663 /* Tween data is cached for each animation on the element so that data can be passed across calls --
664 in particular, end values are used as subsequent start values in consecutive Velocity calls. */
665 tweensContainer: null,
666 /* The full root property values of each CSS hook being animated on this element are cached so that:
667 1) Concurrently-animating hooks sharing the same root can have their root values' merged into one while tweening.
668 2) Post-hook-injection root values can be transferred over to consecutively chained Velocity calls as starting root values. */
669 rootPropertyValueCache: {},
670 /* A cache for transform updates, which must be manually flushed via CSS.flushTransformCache(). */
671 transformCache: {}
672 });
673 },
674 /* Velocity's core animation method, later aliased to $.fn if a framework (jQuery or Zepto) is detected. */
675 animate: null, /* Defined below. */
676 /* A reimplementation of jQuery's $.css(), used for getting/setting Velocity's hooked CSS properties. */
677 hook: null, /* Defined below. */
678 /* Set to true to force a duration of 1ms for all animations so that UI testing can be performed without waiting on animations to complete. */
679 mock: false,
680 version: { major: 1, minor: 0, patch: 0 },
681 /* Set to 1 or 2 (most verbose) to output debug info to console. */
682 debug: false
683 };
684
685 /* Retrieve the appropriate scroll anchor and property name for the browser: https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY */
686 if (window.pageYOffset !== undefined) {
687 Velocity.State.scrollAnchor = window;
688 Velocity.State.scrollPropertyLeft = "pageXOffset";
689 Velocity.State.scrollPropertyTop = "pageYOffset";
690 } else {
691 Velocity.State.scrollAnchor = document.documentElement || document.body.parentNode || document.body;
692 Velocity.State.scrollPropertyLeft = "scrollLeft";
693 Velocity.State.scrollPropertyTop = "scrollTop";
694 }
695
696 /**************
697 Easing
698 **************/
699
700 /* Step easing generator. */
701 function generateStep (steps) {
702 return function (p) {
703 return Math.round(p * steps) * (1 / steps);
704 };
705 }
706
707 /* Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
708 function generateBezier (mX1, mY1, mX2, mY2) {
709 var NEWTON_ITERATIONS = 4,
710 NEWTON_MIN_SLOPE = 0.001,
711 SUBDIVISION_PRECISION = 0.0000001,
712 SUBDIVISION_MAX_ITERATIONS = 10,
713 kSplineTableSize = 11,
714 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
715 float32ArraySupported = "Float32Array" in window;
716
717 /* Must contain four arguments. */
718 if (arguments.length !== 4) {
719 return false;
720 }
721
722 /* Arguments must be numbers. */
723 for (var i = 0; i < 4; ++i) {
724 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
725 return false;
726 }
727 }
728
729 /* X values must be in the [0, 1] range. */
730 mX1 = Math.min(mX1, 1);
731 mX2 = Math.min(mX2, 1);
732 mX1 = Math.max(mX1, 0);
733 mX2 = Math.max(mX2, 0);
734
735 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
736
737 function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
738 function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
739 function C (aA1) { return 3.0 * aA1; }
740
741 function calcBezier (aT, aA1, aA2) {
742 return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
743 }
744
745 function getSlope (aT, aA1, aA2) {
746 return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
747 }
748
749 function newtonRaphsonIterate (aX, aGuessT) {
750 for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
751 var currentSlope = getSlope(aGuessT, mX1, mX2);
752
753 if (currentSlope === 0.0) return aGuessT;
754
755 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
756 aGuessT -= currentX / currentSlope;
757 }
758
759 return aGuessT;
760 }
761
762 function calcSampleValues () {
763 for (var i = 0; i < kSplineTableSize; ++i) {
764 mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
765 }
766 }
767
768 function binarySubdivide (aX, aA, aB) {
769 var currentX, currentT, i = 0;
770
771 do {
772 currentT = aA + (aB - aA) / 2.0;
773 currentX = calcBezier(currentT, mX1, mX2) - aX;
774 if (currentX > 0.0) {
775 aB = currentT;
776 } else {
777 aA = currentT;
778 }
779 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
780
781 return currentT;
782 }
783
784 function getTForX (aX) {
785 var intervalStart = 0.0,
786 currentSample = 1,
787 lastSample = kSplineTableSize - 1;
788
789 for (; currentSample != lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
790 intervalStart += kSampleStepSize;
791 }
792
793 --currentSample;
794
795 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample+1] - mSampleValues[currentSample]),
796 guessForT = intervalStart + dist * kSampleStepSize,
797 initialSlope = getSlope(guessForT, mX1, mX2);
798
799 if (initialSlope >= NEWTON_MIN_SLOPE) {
800 return newtonRaphsonIterate(aX, guessForT);
801 } else if (initialSlope == 0.0) {
802 return guessForT;
803 } else {
804 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
805 }
806 }
807
808 var _precomputed = false;
809
810 function precompute() {
811 _precomputed = true;
812 if (mX1 != mY1 || mX2 != mY2) calcSampleValues();
813 }
814
815 var f = function (aX) {
816 if (!_precomputed) precompute();
817 if (mX1 === mY1 && mX2 === mY2) return aX;
818 if (aX === 0) return 0;
819 if (aX === 1) return 1;
820
821 return calcBezier(getTForX(aX), mY1, mY2);
822 };
823
824 f.getControlPoints = function() { return [{ x: mX1, y: mY1 }, { x: mX2, y: mY2 }]; };
825
826 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
827 f.toString = function () { return str; };
828
829 return f;
830 }
831
832 /* Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
833 /* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass
834 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
835 var generateSpringRK4 = (function () {
836 function springAccelerationForState (state) {
837 return (-state.tension * state.x) - (state.friction * state.v);
838 }
839
840 function springEvaluateStateWithDerivative (initialState, dt, derivative) {
841 var state = {
842 x: initialState.x + derivative.dx * dt,
843 v: initialState.v + derivative.dv * dt,
844 tension: initialState.tension,
845 friction: initialState.friction
846 };
847
848 return { dx: state.v, dv: springAccelerationForState(state) };
849 }
850
851 function springIntegrateState (state, dt) {
852 var a = {
853 dx: state.v,
854 dv: springAccelerationForState(state)
855 },
856 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
857 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
858 d = springEvaluateStateWithDerivative(state, dt, c),
859 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
860 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
861
862 state.x = state.x + dxdt * dt;
863 state.v = state.v + dvdt * dt;
864
865 return state;
866 }
867
868 return function springRK4Factory (tension, friction, duration) {
869
870 var initState = {
871 x: -1,
872 v: 0,
873 tension: null,
874 friction: null
875 },
876 path = [0],
877 time_lapsed = 0,
878 tolerance = 1 / 10000,
879 DT = 16 / 1000,
880 have_duration, dt, last_state;
881
882 tension = parseFloat(tension) || 500;
883 friction = parseFloat(friction) || 20;
884 duration = duration || null;
885
886 initState.tension = tension;
887 initState.friction = friction;
888
889 have_duration = duration !== null;
890
891 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
892 if (have_duration) {
893 /* Run the simulation without a duration. */
894 time_lapsed = springRK4Factory(tension, friction);
895 /* Compute the adjusted time delta. */
896 dt = time_lapsed / duration * DT;
897 } else {
898 dt = DT;
899 }
900
901 while (true) {
902 /* Next/step function .*/
903 last_state = springIntegrateState(last_state || initState, dt);
904 /* Store the position. */
905 path.push(1 + last_state.x);
906 time_lapsed += 16;
907 /* If the change threshold is reached, break. */
908 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
909 break;
910 }
911 }
912
913 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
914 computed path and returns a snapshot of the position according to a given percentComplete. */
915 return !have_duration ? time_lapsed : function(percentComplete) { return path[ (percentComplete * (path.length - 1)) | 0 ]; };
916 };
917 }());
918
919 /* Easings from jQuery, jQuery UI, and CSS3. */
920 Velocity.Easings = {
921 /* jQuery's default named easing types. */
922 linear: function(p) { return p; },
923 swing: function(p) { return 0.5 - Math.cos( p * Math.PI ) / 2 },
924 /* Bonus "spring" easing, which is a less exaggerated version of easeInOutElastic. */
925 spring: function(p) { return 1 - (Math.cos(p * 4.5 * Math.PI) * Math.exp(-p * 6)); }
926 };
927
928 $.each(
929 [
930 /* CSS3's named easing types. */
931 [ "ease", [ 0.25, 0.1, 0.25, 1.0 ] ],
932 [ "ease-in", [ 0.42, 0.0, 1.00, 1.0 ] ],
933 [ "ease-out", [ 0.00, 0.0, 0.58, 1.0 ] ],
934 [ "ease-in-out", [ 0.42, 0.0, 0.58, 1.0 ] ],
935 /* Robert Penner easing equations. */
936 [ "easeInSine", [ 0.47, 0, 0.745, 0.715 ] ],
937 [ "easeOutSine", [ 0.39, 0.575, 0.565, 1 ] ],
938 [ "easeInOutSine", [ 0.445, 0.05, 0.55, 0.95 ] ],
939 [ "easeInQuad", [ 0.55, 0.085, 0.68, 0.53 ] ],
940 [ "easeOutQuad", [ 0.25, 0.46, 0.45, 0.94 ] ],
941 [ "easeInOutQuad", [ 0.455, 0.03, 0.515, 0.955 ] ],
942 [ "easeInCubic", [ 0.55, 0.055, 0.675, 0.19 ] ],
943 [ "easeOutCubic", [ 0.215, 0.61, 0.355, 1 ] ],
944 [ "easeInOutCubic", [ 0.645, 0.045, 0.355, 1 ] ],
945 [ "easeInQuart", [ 0.895, 0.03, 0.685, 0.22 ] ],
946 [ "easeOutQuart", [ 0.165, 0.84, 0.44, 1 ] ],
947 [ "easeInOutQuart", [ 0.77, 0, 0.175, 1 ] ],
948 [ "easeInQuint", [ 0.755, 0.05, 0.855, 0.06 ] ],
949 [ "easeOutQuint", [ 0.23, 1, 0.32, 1 ] ],
950 [ "easeInOutQuint", [ 0.86, 0, 0.07, 1 ] ],
951 [ "easeInExpo", [ 0.95, 0.05, 0.795, 0.035 ] ],
952 [ "easeOutExpo", [ 0.19, 1, 0.22, 1 ] ],
953 [ "easeInOutExpo", [ 1, 0, 0, 1 ] ],
954 [ "easeInCirc", [ 0.6, 0.04, 0.98, 0.335 ] ],
955 [ "easeOutCirc", [ 0.075, 0.82, 0.165, 1 ] ],
956 [ "easeInOutCirc", [ 0.785, 0.135, 0.15, 0.86 ] ]
957 ], function(i, easingArray) {
958 Velocity.Easings[easingArray[0]] = generateBezier.apply(null, easingArray[1]);
959 });
960
961 /* Determine the appropriate easing type given an easing input. */
962 function getEasing(value, duration) {
963 var easing = value;
964
965 /* The easing option can either be a string that references a pre-registered easing,
966 or it can be a two-/four-item array of integers to be converted into a bezier/spring function. */
967 if (Type.isString(value)) {
968 /* Ensure that the easing has been assigned to jQuery's Velocity.Easings object. */
969 if (!Velocity.Easings[value]) {
970 easing = false;
971 }
972 } else if (Type.isArray(value) && value.length === 1) {
973 easing = generateStep.apply(null, value);
974 } else if (Type.isArray(value) && value.length === 2) {
975 /* springRK4 must be passed the animation's duration. */
976 /* Note: If the springRK4 array contains non-numbers, generateSpringRK4() returns an easing
977 function generated with default tension and friction values. */
978 easing = generateSpringRK4.apply(null, value.concat([ duration ]));
979 } else if (Type.isArray(value) && value.length === 4) {
980 /* Note: If the bezier array contains non-numbers, generateBezier() returns false. */
981 easing = generateBezier.apply(null, value);
982 } else {
983 easing = false;
984 }
985
986 /* Revert to the Velocity-wide default easing type, or fall back to "swing" (which is also jQuery's default)
987 if the Velocity-wide default has been incorrectly modified. */
988 if (easing === false) {
989 if (Velocity.Easings[Velocity.defaults.easing]) {
990 easing = Velocity.defaults.easing;
991 } else {
992 easing = EASING_DEFAULT;
993 }
994 }
995
996 return easing;
997 }
998
999 /*****************
1000 CSS Stack
1001 *****************/
1002
1003 /* The CSS object is a highly condensed and performant CSS stack that fully replaces jQuery's.
1004 It handles the validation, getting, and setting of both standard CSS properties and CSS property hooks. */
1005 /* Note: A "CSS" shorthand is aliased so that our code is easier to read. */
1006 var CSS = Velocity.CSS = {
1007
1008 /*************
1009 RegEx
1010 *************/
1011
1012 RegEx: {
1013 isHex: /^#([A-f\d]{3}){1,2}$/i,
1014 /* 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". */
1015 valueUnwrap: /^[A-z]+\((.*)\)$/i,
1016 wrappedValueAlreadyExtracted: /[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,
1017 /* 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" ]. */
1018 valueSplit: /([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/ig
1019 },
1020
1021 /************
1022 Lists
1023 ************/
1024
1025 Lists: {
1026 colors: [ "fill", "stroke", "stopColor", "color", "backgroundColor", "borderColor", "borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor", "outlineColor" ],
1027 transformsBase: [ "translateX", "translateY", "scale", "scaleX", "scaleY", "skewX", "skewY", "rotateZ" ],
1028 transforms3D: [ "transformPerspective", "translateZ", "scaleZ", "rotateX", "rotateY" ]
1029 },
1030
1031 /************
1032 Hooks
1033 ************/
1034
1035 /* Hooks allow a subproperty (e.g. "boxShadowBlur") of a compound-value CSS property
1036 (e.g. "boxShadow: X Y Blur Spread Color") to be animated as if it were a discrete property. */
1037 /* Note: Beyond enabling fine-grained property animation, hooking is necessary since Velocity only
1038 tweens properties with single numeric values; unlike CSS transitions, Velocity does not interpolate compound-values. */
1039 Hooks: {
1040 /********************
1041 Registration
1042 ********************/
1043
1044 /* Templates are a concise way of indicating which subproperties must be individually registered for each compound-value CSS property. */
1045 /* Each template consists of the compound-value's base name, its constituent subproperty names, and those subproperties' default values. */
1046 templates: {
1047 "textShadow": [ "Color X Y Blur", "black 0px 0px 0px" ],
1048 /* Todo: Add support for inset boxShadows. (webkit places it last whereas IE places it first.) */
1049 "boxShadow": [ "Color X Y Blur Spread", "black 0px 0px 0px 0px" ],
1050 "clip": [ "Top Right Bottom Left", "0px 0px 0px 0px" ],
1051 "backgroundPosition": [ "X Y", "0% 0%" ],
1052 "transformOrigin": [ "X Y Z", "50% 50% 0px" ],
1053 "perspectiveOrigin": [ "X Y", "50% 50%" ]
1054 },
1055
1056 /* A "registered" hook is one that has been converted from its template form into a live,
1057 tweenable property. It contains data to associate it with its root property. */
1058 registered: {
1059 /* Note: A registered hook looks like this ==> textShadowBlur: [ "textShadow", 3 ],
1060 which consists of the subproperty's name, the associated root property's name,
1061 and the subproperty's position in the root's value. */
1062 },
1063 /* Convert the templates into individual hooks then append them to the registered object above. */
1064 register: function () {
1065 /* Color hooks registration. */
1066 /* Note: Colors are defaulted to white -- as opposed to black -- since colors that are
1067 currently set to "transparent" default to their respective template below when color-animated,
1068 and white is typically a closer match to transparent than black is. */
1069 for (var i = 0; i < CSS.Lists.colors.length; i++) {
1070 CSS.Hooks.templates[CSS.Lists.colors[i]] = [ "Red Green Blue Alpha", "255 255 255 1" ];
1071 }
1072
1073 var rootProperty,
1074 hookTemplate,
1075 hookNames;
1076
1077 /* In IE, color values inside compound-value properties are positioned at the end the value instead of at the beginning.
1078 Thus, we re-arrange the templates accordingly. */
1079 if (IE) {
1080 for (rootProperty in CSS.Hooks.templates) {
1081 hookTemplate = CSS.Hooks.templates[rootProperty];
1082 hookNames = hookTemplate[0].split(" ");
1083
1084 var defaultValues = hookTemplate[1].match(CSS.RegEx.valueSplit);
1085
1086 if (hookNames[0] === "Color") {
1087 /* Reposition both the hook's name and its default value to the end of their respective strings. */
1088 hookNames.push(hookNames.shift());
1089 defaultValues.push(defaultValues.shift());
1090
1091 /* Replace the existing template for the hook's root property. */
1092 CSS.Hooks.templates[rootProperty] = [ hookNames.join(" "), defaultValues.join(" ") ];
1093 }
1094 }
1095 }
1096
1097 /* Hook registration. */
1098 for (rootProperty in CSS.Hooks.templates) {
1099 hookTemplate = CSS.Hooks.templates[rootProperty];
1100 hookNames = hookTemplate[0].split(" ");
1101
1102 for (var i in hookNames) {
1103 var fullHookName = rootProperty + hookNames[i],
1104 hookPosition = i;
1105
1106 /* For each hook, register its full name (e.g. textShadowBlur) with its root property (e.g. textShadow)
1107 and the hook's position in its template's default value string. */
1108 CSS.Hooks.registered[fullHookName] = [ rootProperty, hookPosition ];
1109 }
1110 }
1111 },
1112
1113 /*****************************
1114 Injection and Extraction
1115 *****************************/
1116
1117 /* Look up the root property associated with the hook (e.g. return "textShadow" for "textShadowBlur"). */
1118 /* 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. */
1119 getRoot: function (property) {
1120 var hookData = CSS.Hooks.registered[property];
1121
1122 if (hookData) {
1123 return hookData[0];
1124 } else {
1125 /* If there was no hook match, return the property name untouched. */
1126 return property;
1127 }
1128 },
1129 /* Convert any rootPropertyValue, null or otherwise, into a space-delimited list of hook values so that
1130 the targeted hook can be injected or extracted at its standard position. */
1131 cleanRootPropertyValue: function(rootProperty, rootPropertyValue) {
1132 /* If the rootPropertyValue is wrapped with "rgb()", "clip()", etc., remove the wrapping to normalize the value before manipulation. */
1133 if (CSS.RegEx.valueUnwrap.test(rootPropertyValue)) {
1134 rootPropertyValue = rootPropertyValue.match(CSS.Hooks.RegEx.valueUnwrap)[1];
1135 }
1136
1137 /* If rootPropertyValue is a CSS null-value (from which there's inherently no hook value to extract),
1138 default to the root's default value as defined in CSS.Hooks.templates. */
1139 /* Note: CSS null-values include "none", "auto", and "transparent". They must be converted into their
1140 zero-values (e.g. textShadow: "none" ==> textShadow: "0px 0px 0px black") for hook manipulation to proceed. */
1141 if (CSS.Values.isCSSNullValue(rootPropertyValue)) {
1142 rootPropertyValue = CSS.Hooks.templates[rootProperty][1];
1143 }
1144
1145 return rootPropertyValue;
1146 },
1147 /* Extracted the hook's value from its root property's value. This is used to get the starting value of an animating hook. */
1148 extractValue: function (fullHookName, rootPropertyValue) {
1149 var hookData = CSS.Hooks.registered[fullHookName];
1150
1151 if (hookData) {
1152 var hookRoot = hookData[0],
1153 hookPosition = hookData[1];
1154
1155 rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue);
1156
1157 /* Split rootPropertyValue into its constituent hook values then grab the desired hook at its standard position. */
1158 return rootPropertyValue.toString().match(CSS.RegEx.valueSplit)[hookPosition];
1159 } else {
1160 /* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */
1161 return rootPropertyValue;
1162 }
1163 },
1164 /* Inject the hook's value into its root property's value. This is used to piece back together the root property
1165 once Velocity has updated one of its individually hooked values through tweening. */
1166 injectValue: function (fullHookName, hookValue, rootPropertyValue) {
1167 var hookData = CSS.Hooks.registered[fullHookName];
1168
1169 if (hookData) {
1170 var hookRoot = hookData[0],
1171 hookPosition = hookData[1],
1172 rootPropertyValueParts,
1173 rootPropertyValueUpdated;
1174
1175 rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue);
1176
1177 /* Split rootPropertyValue into its individual hook values, replace the targeted value with hookValue,
1178 then reconstruct the rootPropertyValue string. */
1179 rootPropertyValueParts = rootPropertyValue.toString().match(CSS.RegEx.valueSplit);
1180 rootPropertyValueParts[hookPosition] = hookValue;
1181 rootPropertyValueUpdated = rootPropertyValueParts.join(" ");
1182
1183 return rootPropertyValueUpdated;
1184 } else {
1185 /* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */
1186 return rootPropertyValue;
1187 }
1188 }
1189 },
1190
1191 /*******************
1192 Normalizations
1193 *******************/
1194
1195 /* Normalizations standardize CSS property manipulation by pollyfilling browser-specific implementations (e.g. opacity)
1196 and reformatting special properties (e.g. clip, rgba) to look like standard ones. */
1197 Normalizations: {
1198 /* Normalizations are passed a normalization target (either the property's name, its extracted value, or its injected value),
1199 the targeted element (which may need to be queried), and the targeted property value. */
1200 registered: {
1201 clip: function(type, element, propertyValue) {
1202 switch (type) {
1203 case "name":
1204 return "clip";
1205 /* Clip needs to be unwrapped and stripped of its commas during extraction. */
1206 case "extract":
1207 var extracted;
1208
1209 /* If Velocity also extracted this value, skip extraction. */
1210 if (CSS.RegEx.wrappedValueAlreadyExtracted.test(propertyValue)) {
1211 extracted = propertyValue;
1212 } else {
1213 /* Remove the "rect()" wrapper. */
1214 extracted = propertyValue.toString().match(CSS.RegEx.valueUnwrap);
1215
1216 /* Strip off commas. */
1217 extracted = extracted ? extracted[1].replace(/,(\s+)?/g, " ") : propertyValue;
1218 }
1219
1220 return extracted;
1221 /* Clip needs to be re-wrapped during injection. */
1222 case "inject":
1223 return "rect(" + propertyValue + ")";
1224 }
1225 },
1226
1227 /* <=IE8 do not support the standard opacity property. They use filter:alpha(opacity=INT) instead. */
1228 opacity: function (type, element, propertyValue) {
1229 if (IE <= 8) {
1230 switch (type) {
1231 case "name":
1232 return "filter";
1233 case "extract":
1234 /* <=IE8 return a "filter" value of "alpha(opacity=\d{1,3})".
1235 Extract the value and convert it to a decimal value to match the standard CSS opacity property's formatting. */
1236 var extracted = propertyValue.toString().match(/alpha\(opacity=(.*)\)/i);
1237
1238 if (extracted) {
1239 /* Convert to decimal value. */
1240 propertyValue = extracted[1] / 100;
1241 } else {
1242 /* When extracting opacity, default to 1 since a null value means opacity hasn't been set. */
1243 propertyValue = 1;
1244 }
1245
1246 return propertyValue;
1247 case "inject":
1248 /* Opacified elements are required to have their zoom property set to a non-zero value. */
1249 element.style.zoom = 1;
1250
1251 /* Setting the filter property on elements with certain font property combinations can result in a
1252 highly unappealing ultra-bolding effect. There's no way to remedy this throughout a tween, but dropping the
1253 value altogether (when opacity hits 1) at leasts ensures that the glitch is gone post-tweening. */
1254 if (parseFloat(propertyValue) >= 1) {
1255 return "";
1256 } else {
1257 /* As per the filter property's spec, convert the decimal value to a whole number and wrap the value. */
1258 return "alpha(opacity=" + parseInt(parseFloat(propertyValue) * 100, 10) + ")";
1259 }
1260 }
1261 /* With all other browsers, normalization is not required; return the same values that were passed in. */
1262 } else {
1263 switch (type) {
1264 case "name":
1265 return "opacity";
1266 case "extract":
1267 return propertyValue;
1268 case "inject":
1269 return propertyValue;
1270 }
1271 }
1272 }
1273 },
1274
1275 /*****************************
1276 Batched Registrations
1277 *****************************/
1278
1279 /* Note: Batched normalizations extend the CSS.Normalizations.registered object. */
1280 register: function () {
1281
1282 /*****************
1283 Transforms
1284 *****************/
1285
1286 /* Transforms are the subproperties contained by the CSS "transform" property. Transforms must undergo normalization
1287 so that they can be referenced in a properties map by their individual names. */
1288 /* Note: When transforms are "set", they are actually assigned to a per-element transformCache. When all transform
1289 setting is complete complete, CSS.flushTransformCache() must be manually called to flush the values to the DOM.
1290 Transform setting is batched in this way to improve performance: the transform style only needs to be updated
1291 once when multiple transform subproperties are being animated simultaneously. */
1292 /* Note: IE9 and Android Gingerbread have support for 2D -- but not 3D -- transforms. Since animating unsupported
1293 transform properties results in the browser ignoring the *entire* transform string, we prevent these 3D values
1294 from being normalized for these browsers so that tweening skips these properties altogether
1295 (since it will ignore them as being unsupported by the browser.) */
1296 if (!(IE <= 9) && !Velocity.State.isGingerbread) {
1297 /* Note: Since the standalone CSS "perspective" property and the CSS transform "perspective" subproperty
1298 share the same name, the latter is given a unique token within Velocity: "transformPerspective". */
1299 CSS.Lists.transformsBase = CSS.Lists.transformsBase.concat(CSS.Lists.transforms3D);
1300 }
1301
1302 for (var i = 0; i < CSS.Lists.transformsBase.length; i++) {
1303 /* Wrap the dynamically generated normalization function in a new scope so that transformName's value is
1304 paired with its respective function. (Otherwise, all functions would take the final for loop's transformName.) */
1305 (function() {
1306 var transformName = CSS.Lists.transformsBase[i];
1307
1308 CSS.Normalizations.registered[transformName] = function (type, element, propertyValue) {
1309 switch (type) {
1310 /* The normalized property name is the parent "transform" property -- the property that is actually set in CSS. */
1311 case "name":
1312 return "transform";
1313 /* Transform values are cached onto a per-element transformCache object. */
1314 case "extract":
1315 /* If this transform has yet to be assigned a value, return its null value. */
1316 if (Data(element) === undefined || Data(element).transformCache[transformName] === undefined) {
1317 /* Scale CSS.Lists.transformsBase default to 1 whereas all other transform properties default to 0. */
1318 return /^scale/i.test(transformName) ? 1 : 0;
1319 /* When transform values are set, they are wrapped in parentheses as per the CSS spec.
1320 Thus, when extracting their values (for tween calculations), we strip off the parentheses. */
1321 } else {
1322 return Data(element).transformCache[transformName].replace(/[()]/g, "");
1323 }
1324 case "inject":
1325 var invalid = false;
1326
1327 /* If an individual transform property contains an unsupported unit type, the browser ignores the *entire* transform property.
1328 Thus, protect users from themselves by skipping setting for transform values supplied with invalid unit types. */
1329 /* Switch on the base transform type; ignore the axis by removing the last letter from the transform's name. */
1330 switch (transformName.substr(0, transformName.length - 1)) {
1331 /* Whitelist unit types for each transform. */
1332 case "translate":
1333 invalid = !/(%|px|em|rem|vw|vh|\d)$/i.test(propertyValue);
1334 break;
1335 /* 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. */
1336 case "scal":
1337 case "scale":
1338 /* Chrome on Android has a bug in which scaled elements blur if their initial scale
1339 value is below 1 (which can happen with forcefeeding). Thus, we detect a yet-unset scale property
1340 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 */
1341 if (Velocity.State.isAndroid && Data(element).transformCache[transformName] === undefined && propertyValue < 1) {
1342 propertyValue = 1;
1343 }
1344
1345 invalid = !/(\d)$/i.test(propertyValue);
1346 break;
1347 case "skew":
1348 invalid = !/(deg|\d)$/i.test(propertyValue);
1349 break;
1350 case "rotate":
1351 invalid = !/(deg|\d)$/i.test(propertyValue);
1352 break;
1353 }
1354
1355 if (!invalid) {
1356 /* As per the CSS spec, wrap the value in parentheses. */
1357 Data(element).transformCache[transformName] = "(" + propertyValue + ")";
1358 }
1359
1360 /* Although the value is set on the transformCache object, return the newly-updated value for the calling code to process as normal. */
1361 return Data(element).transformCache[transformName];
1362 }
1363 };
1364 })();
1365 }
1366
1367 /*************
1368 Colors
1369 *************/
1370
1371 /* Since Velocity only animates a single numeric value per property, color animation is achieved by hooking the individual RGBA components of CSS color properties.
1372 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. */
1373 for (var i = 0; i < CSS.Lists.colors.length; i++) {
1374 /* Wrap the dynamically generated normalization function in a new scope so that colorName's value is paired with its respective function.
1375 (Otherwise, all functions would take the final for loop's colorName.) */
1376 (function () {
1377 var colorName = CSS.Lists.colors[i];
1378
1379 /* Note: In IE<=8, which support rgb but not rgba, color properties are reverted to rgb by stripping off the alpha component. */
1380 CSS.Normalizations.registered[colorName] = function(type, element, propertyValue) {
1381 switch (type) {
1382 case "name":
1383 return colorName;
1384 /* Convert all color values into the rgb format. (Old IE can return hex values and color names instead of rgb/rgba.) */
1385 case "extract":
1386 var extracted;
1387
1388 /* If the color is already in its hookable form (e.g. "255 255 255 1") due to having been previously extracted, skip extraction. */
1389 if (CSS.RegEx.wrappedValueAlreadyExtracted.test(propertyValue)) {
1390 extracted = propertyValue;
1391 } else {
1392 var converted,
1393 colorNames = {
1394 black: "rgb(0, 0, 0)",
1395 blue: "rgb(0, 0, 255)",
1396 gray: "rgb(128, 128, 128)",
1397 green: "rgb(0, 128, 0)",
1398 red: "rgb(255, 0, 0)",
1399 white: "rgb(255, 255, 255)"
1400 };
1401
1402 /* Convert color names to rgb. */
1403 if (/^[A-z]+$/i.test(propertyValue)) {
1404 if (colorNames[propertyValue] !== undefined) {
1405 converted = colorNames[propertyValue]
1406 } else {
1407 /* If an unmatched color name is provided, default to black. */
1408 converted = colorNames.black;
1409 }
1410 /* Convert hex values to rgb. */
1411 } else if (CSS.RegEx.isHex.test(propertyValue)) {
1412 converted = "rgb(" + CSS.Values.hexToRgb(propertyValue).join(" ") + ")";
1413 /* If the provided color doesn't match any of the accepted color formats, default to black. */
1414 } else if (!(/^rgba?\(/i.test(propertyValue))) {
1415 converted = colorNames.black;
1416 }
1417
1418 /* Remove the surrounding "rgb/rgba()" string then replace commas with spaces and strip
1419 repeated spaces (in case the value included spaces to begin with). */
1420 extracted = (converted || propertyValue).toString().match(CSS.RegEx.valueUnwrap)[1].replace(/,(\s+)?/g, " ");
1421 }
1422
1423 /* So long as this isn't <=IE8, add a fourth (alpha) component if it's missing and default it to 1 (visible). */
1424 if (!(IE <= 8) && extracted.split(" ").length === 3) {
1425 extracted += " 1";
1426 }
1427
1428 return extracted;
1429 case "inject":
1430 /* If this is IE<=8 and an alpha component exists, strip it off. */
1431 if (IE <= 8) {
1432 if (propertyValue.split(" ").length === 4) {
1433 propertyValue = propertyValue.split(/\s+/).slice(0, 3).join(" ");
1434 }
1435 /* Otherwise, add a fourth (alpha) component if it's missing and default it to 1 (visible). */
1436 } else if (propertyValue.split(" ").length === 3) {
1437 propertyValue += " 1";
1438 }
1439
1440 /* Re-insert the browser-appropriate wrapper("rgb/rgba()"), insert commas, and strip off decimal units
1441 on all values but the fourth (R, G, and B only accept whole numbers). */
1442 return (IE <= 8 ? "rgb" : "rgba") + "(" + propertyValue.replace(/\s+/g, ",").replace(/\.(\d)+(?=,)/g, "") + ")";
1443 }
1444 };
1445 })();
1446 }
1447 }
1448 },
1449
1450 /************************
1451 CSS Property Names
1452 ************************/
1453
1454 Names: {
1455 /* Camelcase a property name into its JavaScript notation (e.g. "background-color" ==> "backgroundColor").
1456 Camelcasing is used to normalize property names between and across calls. */
1457 camelCase: function (property) {
1458 return property.replace(/-(\w)/g, function (match, subMatch) {
1459 return subMatch.toUpperCase();
1460 });
1461 },
1462
1463 /* For SVG elements, some properties (namely, dimensional ones) are GET/SET via the element's HTML attributes (instead of via CSS styles). */
1464 SVGAttribute: function (property) {
1465 var SVGAttributes = "width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2";
1466
1467 /* Certain browsers require an SVG transform to be applied as an attribute. (Otherwise, application via CSS is preferable due to 3D support.) */
1468 if (IE || (Velocity.State.isAndroid && !Velocity.State.isChrome)) {
1469 SVGAttributes += "|transform";
1470 }
1471
1472 return new RegExp("^(" + SVGAttributes + ")$", "i").test(property);
1473 },
1474
1475 /* Determine whether a property should be set with a vendor prefix. */
1476 /* If a prefixed version of the property exists, return it. Otherwise, return the original property name.
1477 If the property is not at all supported by the browser, return a false flag. */
1478 prefixCheck: function (property) {
1479 /* If this property has already been checked, return the cached value. */
1480 if (Velocity.State.prefixMatches[property]) {
1481 return [ Velocity.State.prefixMatches[property], true ];
1482 } else {
1483 var vendors = [ "", "Webkit", "Moz", "ms", "O" ];
1484
1485 for (var i = 0, vendorsLength = vendors.length; i < vendorsLength; i++) {
1486 var propertyPrefixed;
1487
1488 if (i === 0) {
1489 propertyPrefixed = property;
1490 } else {
1491 /* Capitalize the first letter of the property to conform to JavaScript vendor prefix notation (e.g. webkitFilter). */
1492 propertyPrefixed = vendors[i] + property.replace(/^\w/, function(match) { return match.toUpperCase(); });
1493 }
1494
1495 /* Check if the browser supports this property as prefixed. */
1496 if (Type.isString(Velocity.State.prefixElement.style[propertyPrefixed])) {
1497 /* Cache the match. */
1498 Velocity.State.prefixMatches[property] = propertyPrefixed;
1499
1500 return [ propertyPrefixed, true ];
1501 }
1502 }
1503
1504 /* If the browser doesn't support this property in any form, include a false flag so that the caller can decide how to proceed. */
1505 return [ property, false ];
1506 }
1507 }
1508 },
1509
1510 /************************
1511 CSS Property Values
1512 ************************/
1513
1514 Values: {
1515 /* Hex to RGB conversion. Copyright Tim Down: http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb */
1516 hexToRgb: function (hex) {
1517 var shortformRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i,
1518 longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,
1519 rgbParts;
1520
1521 hex = hex.replace(shortformRegex, function (m, r, g, b) {
1522 return r + r + g + g + b + b;
1523 });
1524
1525 rgbParts = longformRegex.exec(hex);
1526
1527 return rgbParts ? [ parseInt(rgbParts[1], 16), parseInt(rgbParts[2], 16), parseInt(rgbParts[3], 16) ] : [ 0, 0, 0 ];
1528 },
1529 isCSSNullValue: function (value) {
1530 /* The browser defaults CSS values that have not been set to either 0 or one of several possible null-value strings.
1531 Thus, we check for both falsiness and these special strings. */
1532 /* Null-value checking is performed to default the special strings to 0 (for the sake of tweening) or their hook
1533 templates as defined as CSS.Hooks (for the sake of hook injection/extraction). */
1534 /* Note: Chrome returns "rgba(0, 0, 0, 0)" for an undefined color whereas IE returns "transparent". */
1535 return (value == 0 || /^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(value));
1536 },
1537 /* Retrieve a property's default unit type. Used for assigning a unit type when one is not supplied by the user. */
1538 getUnitType: function (property) {
1539 if (/^(rotate|skew)/i.test(property)) {
1540 return "deg";
1541 } else if (/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(property)) {
1542 /* The above properties are unitless. */
1543 return "";
1544 } else {
1545 /* Default to px for all other properties. */
1546 return "px";
1547 }
1548 },
1549 /* HTML elements default to an associated display type when they're not set to display:none. */
1550 /* Note: This function is used for correctly setting the non-"none" display value in certain Velocity sequences, such as fadeIn/Out. */
1551 getDisplayType: function (element) {
1552 var tagName = element.tagName.toString().toLowerCase();
1553
1554 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)) {
1555 return "inline";
1556 } else if (/^(li)$/i.test(tagName)) {
1557 return "list-item";
1558 } else if (/^(tr)$/i.test(tagName)) {
1559 return "table-row";
1560 /* Default to "block" when no match is found. */
1561 } else {
1562 return "block";
1563 }
1564 },
1565 /* The class add/remove functions are used to temporarily apply a "velocity-animating" class to elements while they're animating. */
1566 addClass: function (element, className) {
1567 if (element.classList) {
1568 element.classList.add(className);
1569 } else {
1570 element.className += (element.className.length ? " " : "") + className;
1571 }
1572 },
1573 removeClass: function (element, className) {
1574 if (element.classList) {
1575 element.classList.remove(className);
1576 } else {
1577 element.className = element.className.toString().replace(new RegExp("(^|\\s)" + className.split(" ").join("|") + "(\\s|$)", "gi"), " ");
1578 }
1579 }
1580 },
1581
1582 /****************************
1583 Style Getting & Setting
1584 ****************************/
1585
1586 /* The singular getPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */
1587 getPropertyValue: function (element, property, rootPropertyValue, forceStyleLookup) {
1588 /* Get an element's computed property value. */
1589 /* Note: Retrieving the value of a CSS property cannot simply be performed by checking an element's
1590 style attribute (which only reflects user-defined values). Instead, the browser must be queried for a property's
1591 *computed* value. You can read more about getComputedStyle here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */
1592 function computePropertyValue (element, property) {
1593 /* When box-sizing isn't set to border-box, height and width style values are incorrectly computed when an
1594 element's scrollbars are visible (which expands the element's dimensions). Thus, we defer to the more accurate
1595 offsetHeight/Width property, which includes the total dimensions for interior, border, padding, and scrollbar.
1596 We subtract border and padding to get the sum of interior + scrollbar. */
1597 var computedValue = 0;
1598
1599 /* IE<=8 doesn't support window.getComputedStyle, thus we defer to jQuery, which has an extensive array
1600 of hacks to accurately retrieve IE8 property values. Re-implementing that logic here is not worth bloating the
1601 codebase for a dying browser. The performance repercussions of using jQuery here are minimal since
1602 Velocity is optimized to rarely (and sometimes never) query the DOM. Further, the $.css() codepath isn't that slow. */
1603 if (IE <= 8) {
1604 computedValue = $.css(element, property); /* GET */
1605 /* All other browsers support getComputedStyle. The returned live object reference is cached onto its
1606 associated element so that it does not need to be refetched upon every GET. */
1607 } else {
1608 /* Browsers do not return height and width values for elements that are set to display:"none". Thus, we temporarily
1609 toggle display to the element type's default value. */
1610 var toggleDisplay = false;
1611
1612 if (/^(width|height)$/.test(property) && CSS.getPropertyValue(element, "display") === 0) {
1613 toggleDisplay = true;
1614 CSS.setPropertyValue(element, "display", CSS.Values.getDisplayType(element));
1615 }
1616
1617 function revertDisplay () {
1618 if (toggleDisplay) {
1619 CSS.setPropertyValue(element, "display", "none");
1620 }
1621 }
1622
1623 if (!forceStyleLookup) {
1624 if (property === "height" && CSS.getPropertyValue(element, "boxSizing").toString().toLowerCase() !== "border-box") {
1625 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);
1626 revertDisplay();
1627
1628 return contentBoxHeight;
1629 } else if (property === "width" && CSS.getPropertyValue(element, "boxSizing").toString().toLowerCase() !== "border-box") {
1630 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);
1631 revertDisplay();
1632
1633 return contentBoxWidth;
1634 }
1635 }
1636
1637 var computedStyle;
1638
1639 /* For elements that Velocity hasn't been called on directly (e.g. when Velocity queries the DOM on behalf
1640 of a parent of an element its animating), perform a direct getComputedStyle lookup since the object isn't cached. */
1641 if (Data(element) === undefined) {
1642 computedStyle = window.getComputedStyle(element, null); /* GET */
1643 /* If the computedStyle object has yet to be cached, do so now. */
1644 } else if (!Data(element).computedStyle) {
1645 computedStyle = Data(element).computedStyle = window.getComputedStyle(element, null); /* GET */
1646 /* If computedStyle is cached, use it. */
1647 } else {
1648 computedStyle = Data(element).computedStyle;
1649 }
1650
1651 /* IE and Firefox do not return a value for the generic borderColor -- they only return individual values for each border side's color.
1652 As a polyfill for querying individual border side colors, just return the top border's color. */
1653 if ((IE || Velocity.State.isFirefox) && property === "borderColor") {
1654 property = "borderTopColor";
1655 }
1656
1657 /* IE9 has a bug in which the "filter" property must be accessed from computedStyle using the getPropertyValue method
1658 instead of a direct property lookup. The getPropertyValue method is slower than a direct lookup, which is why we avoid it by default. */
1659 if (IE === 9 && property === "filter") {
1660 computedValue = computedStyle.getPropertyValue(property); /* GET */
1661 } else {
1662 computedValue = computedStyle[property];
1663 }
1664
1665 /* Fall back to the property's style value (if defined) when computedValue returns nothing,
1666 which can happen when the element hasn't been painted. */
1667 if (computedValue === "" || computedValue === null) {
1668 computedValue = element.style[property];
1669 }
1670
1671 revertDisplay();
1672 }
1673
1674 /* For top, right, bottom, and left (TRBL) values that are set to "auto" on elements of "fixed" or "absolute" position,
1675 defer to jQuery for converting "auto" to a numeric value. (For elements with a "static" or "relative" position, "auto" has the same
1676 effect as being set to 0, so no conversion is necessary.) */
1677 /* An example of why numeric conversion is necessary: When an element with "position:absolute" has an untouched "left"
1678 property, which reverts to "auto", left's value is 0 relative to its parent element, but is often non-zero relative
1679 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"). */
1680 if (computedValue === "auto" && /^(top|right|bottom|left)$/i.test(property)) {
1681 var position = computePropertyValue(element, "position"); /* GET */
1682
1683 /* For absolute positioning, jQuery's $.position() only returns values for top and left;
1684 right and bottom will have their "auto" value reverted to 0. */
1685 /* Note: A jQuery object must be created here since jQuery doesn't have a low-level alias for $.position().
1686 Not a big deal since we're currently in a GET batch anyway. */
1687 if (position === "fixed" || (position === "absolute" && /top|left/i.test(property))) {
1688 /* Note: jQuery strips the pixel unit from its returned values; we re-add it here to conform with computePropertyValue's behavior. */
1689 computedValue = $(element).position()[property] + "px"; /* GET */
1690 }
1691 }
1692
1693 return computedValue;
1694 }
1695
1696 var propertyValue;
1697
1698 /* If this is a hooked property (e.g. "clipLeft" instead of the root property of "clip"),
1699 extract the hook's value from a normalized rootPropertyValue using CSS.Hooks.extractValue(). */
1700 if (CSS.Hooks.registered[property]) {
1701 var hook = property,
1702 hookRoot = CSS.Hooks.getRoot(hook);
1703
1704 /* If a cached rootPropertyValue wasn't passed in (which Velocity always attempts to do in order to avoid requerying the DOM),
1705 query the DOM for the root property's value. */
1706 if (rootPropertyValue === undefined) {
1707 /* Since the browser is now being directly queried, use the official post-prefixing property name for this lookup. */
1708 rootPropertyValue = CSS.getPropertyValue(element, CSS.Names.prefixCheck(hookRoot)[0]); /* GET */
1709 }
1710
1711 /* If this root has a normalization registered, peform the associated normalization extraction. */
1712 if (CSS.Normalizations.registered[hookRoot]) {
1713 rootPropertyValue = CSS.Normalizations.registered[hookRoot]("extract", element, rootPropertyValue);
1714 }
1715
1716 /* Extract the hook's value. */
1717 propertyValue = CSS.Hooks.extractValue(hook, rootPropertyValue);
1718
1719 /* If this is a normalized property (e.g. "opacity" becomes "filter" in <=IE8) or "translateX" becomes "transform"),
1720 normalize the property's name and value, and handle the special case of transforms. */
1721 /* Note: Normalizing a property is mutually exclusive from hooking a property since hook-extracted values are strictly
1722 numerical and therefore do not require normalization extraction. */
1723 } else if (CSS.Normalizations.registered[property]) {
1724 var normalizedPropertyName,
1725 normalizedPropertyValue;
1726
1727 normalizedPropertyName = CSS.Normalizations.registered[property]("name", element);
1728
1729 /* Transform values are calculated via normalization extraction (see below), which checks against the element's transformCache.
1730 At no point do transform GETs ever actually query the DOM; initial stylesheet values are never processed.
1731 This is because parsing 3D transform matrices is not always accurate and would bloat our codebase;
1732 thus, normalization extraction defaults initial transform values to their zero-values (e.g. 1 for scaleX and 0 for translateX). */
1733 if (normalizedPropertyName !== "transform") {
1734 normalizedPropertyValue = computePropertyValue(element, CSS.Names.prefixCheck(normalizedPropertyName)[0]); /* GET */
1735
1736 /* 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. */
1737 if (CSS.Values.isCSSNullValue(normalizedPropertyValue) && CSS.Hooks.templates[property]) {
1738 normalizedPropertyValue = CSS.Hooks.templates[property][1];
1739 }
1740 }
1741
1742 propertyValue = CSS.Normalizations.registered[property]("extract", element, normalizedPropertyValue);
1743 }
1744
1745 /* If a (numeric) value wasn't produced via hook extraction or normalization, query the DOM. */
1746 if (!/^[\d-]/.test(propertyValue)) {
1747 /* For SVG elements, dimensional properties (which SVGAttribute() detects) are tweened via
1748 their HTML attribute values instead of their CSS style values. */
1749 if (Data(element) && Data(element).isSVG && CSS.Names.SVGAttribute(property)) {
1750 /* Since the height/width attribute values must be set manually, they don't reflect computed values.
1751 Thus, we use use getBBox() to ensure we always get values for elements with undefined height/width attributes. */
1752 if (/^(height|width)$/i.test(property)) {
1753 propertyValue = element.getBBox()[property];
1754 /* Otherwise, access the attribute value directly. */
1755 } else {
1756 propertyValue = element.getAttribute(property);
1757 }
1758 } else {
1759 propertyValue = computePropertyValue(element, CSS.Names.prefixCheck(property)[0]); /* GET */
1760 }
1761 }
1762
1763 /* Since property lookups are for animation purposes (which entails computing the numeric delta between start and end values),
1764 convert CSS null-values to an integer of value 0. */
1765 if (CSS.Values.isCSSNullValue(propertyValue)) {
1766 propertyValue = 0;
1767 }
1768
1769 if (Velocity.debug >= 2) console.log("Get " + property + ": " + propertyValue);
1770
1771 return propertyValue;
1772 },
1773
1774 /* The singular setPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */
1775 setPropertyValue: function(element, property, propertyValue, rootPropertyValue, scrollData) {
1776 var propertyName = property;
1777
1778 /* 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. */
1779 if (property === "scroll") {
1780 /* If a container option is present, scroll the container instead of the browser window. */
1781 if (scrollData.container) {
1782 scrollData.container["scroll" + scrollData.direction] = propertyValue;
1783 /* Otherwise, Velocity defaults to scrolling the browser window. */
1784 } else {
1785 if (scrollData.direction === "Left") {
1786 window.scrollTo(propertyValue, scrollData.alternateValue);
1787 } else {
1788 window.scrollTo(scrollData.alternateValue, propertyValue);
1789 }
1790 }
1791 } else {
1792 /* Transforms (translateX, rotateZ, etc.) are applied to a per-element transformCache object, which is manually flushed via flushTransformCache().
1793 Thus, for now, we merely cache transforms being SET. */
1794 if (CSS.Normalizations.registered[property] && CSS.Normalizations.registered[property]("name", element) === "transform") {
1795 /* Perform a normalization injection. */
1796 /* Note: The normalization logic handles the transformCache updating. */
1797 CSS.Normalizations.registered[property]("inject", element, propertyValue);
1798
1799 propertyName = "transform";
1800 propertyValue = Data(element).transformCache[property];
1801 } else {
1802 /* Inject hooks. */
1803 if (CSS.Hooks.registered[property]) {
1804 var hookName = property,
1805 hookRoot = CSS.Hooks.getRoot(property);
1806
1807 /* If a cached rootPropertyValue was not provided, query the DOM for the hookRoot's current value. */
1808 rootPropertyValue = rootPropertyValue || CSS.getPropertyValue(element, hookRoot); /* GET */
1809
1810 propertyValue = CSS.Hooks.injectValue(hookName, propertyValue, rootPropertyValue);
1811 property = hookRoot;
1812 }
1813
1814 /* Normalize names and values. */
1815 if (CSS.Normalizations.registered[property]) {
1816 propertyValue = CSS.Normalizations.registered[property]("inject", element, propertyValue);
1817 property = CSS.Normalizations.registered[property]("name", element);
1818 }
1819
1820 /* Assign the appropriate vendor prefix before performing an official style update. */
1821 propertyName = CSS.Names.prefixCheck(property)[0];
1822
1823 /* A try/catch is used for IE<=8, which throws an error when "invalid" CSS values are set, e.g. a negative width.
1824 Try/catch is avoided for other browsers since it incurs a performance overhead. */
1825 if (IE <= 8) {
1826 try {
1827 element.style[propertyName] = propertyValue;
1828 } catch (error) { if (Velocity.debug) console.log("Browser does not support [" + propertyValue + "] for [" + propertyName + "]"); }
1829 /* SVG elements have their dimensional properties (width, height, x, y, cx, etc.) applied directly as attributes instead of as styles. */
1830 /* Note: IE8 does not support SVG elements, so it's okay that we skip it for SVG animation. */
1831 } else if (Data(element) && Data(element).isSVG && CSS.Names.SVGAttribute(property)) {
1832 /* Note: For SVG attributes, vendor-prefixed property names are never used. */
1833 /* Note: Not all CSS properties can be animated via attributes, but the browser won't throw an error for unsupported properties. */
1834 element.setAttribute(property, propertyValue);
1835 } else {
1836 element.style[propertyName] = propertyValue;
1837 }
1838
1839 if (Velocity.debug >= 2) console.log("Set " + property + " (" + propertyName + "): " + propertyValue);
1840 }
1841 }
1842
1843 /* 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. */
1844 return [ propertyName, propertyValue ];
1845 },
1846
1847 /* To increase performance by batching transform updates into a single SET, transforms are not directly applied to an element until flushTransformCache() is called. */
1848 /* Note: Velocity applies transform properties in the same order that they are chronogically introduced to the element's CSS styles. */
1849 flushTransformCache: function(element) {
1850 var transformString = "";
1851
1852 /* Certain browsers require that SVG transforms be applied as an attribute. However, the SVG transform attribute takes a modified version of CSS's transform string
1853 (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). */
1854 if ((IE || (Velocity.State.isAndroid && !Velocity.State.isChrome)) && Data(element).isSVG) {
1855 /* Since transform values are stored in their parentheses-wrapped form, we use a helper function to strip out their numeric values.
1856 Further, SVG transform properties only take unitless (representing pixels) values, so it's okay that parseFloat() strips the unit suffixed to the float value. */
1857 function getTransformFloat (transformProperty) {
1858 return parseFloat(CSS.getPropertyValue(element, transformProperty));
1859 }
1860
1861 /* Create an object to organize all the transforms that we'll apply to the SVG element. To keep the logic simple,
1862 we process *all* transform properties -- even those that may not be explicitly applied (since they default to their zero-values anyway). */
1863 var SVGTransforms = {
1864 translate: [ getTransformFloat("translateX"), getTransformFloat("translateY") ],
1865 skewX: [ getTransformFloat("skewX") ], skewY: [ getTransformFloat("skewY") ],
1866 /* If the scale property is set (non-1), use that value for the scaleX and scaleY values
1867 (this behavior mimics the result of animating all these properties at once on HTML elements). */
1868 scale: getTransformFloat("scale") !== 1 ? [ getTransformFloat("scale"), getTransformFloat("scale") ] : [ getTransformFloat("scaleX"), getTransformFloat("scaleY") ],
1869 /* Note: SVG's rotate transform takes three values: rotation degrees followed by the X and Y values
1870 defining the rotation's origin point. We ignore the origin values (default them to 0). */
1871 rotate: [ getTransformFloat("rotateZ"), 0, 0 ]
1872 };
1873
1874 /* Iterate through the transform properties in the user-defined property map order.
1875 (This mimics the behavior of non-SVG transform animation.) */
1876 $.each(Data(element).transformCache, function(transformName) {
1877 /* Except for with skewX/Y, revert the axis-specific transform subproperties to their axis-free master
1878 properties so that they match up with SVG's accepted transform properties. */
1879 if (/^translate/i.test(transformName)) {
1880 transformName = "translate";
1881 } else if (/^scale/i.test(transformName)) {
1882 transformName = "scale";
1883 } else if (/^rotate/i.test(transformName)) {
1884 transformName = "rotate";
1885 }
1886
1887 /* Check that we haven't yet deleted the property from the SVGTransforms container. */
1888 if (SVGTransforms[transformName]) {
1889 /* Append the transform property in the SVG-supported transform format. As per the spec, surround the space-delimited values in parentheses. */
1890 transformString += transformName + "(" + SVGTransforms[transformName].join(" ") + ")" + " ";
1891
1892 /* After processing an SVG transform property, delete it from the SVGTransforms container so we don't
1893 re-insert the same master property if we encounter another one of its axis-specific properties. */
1894 delete SVGTransforms[transformName];
1895 }
1896 });
1897 } else {
1898 var transformValue,
1899 perspective;
1900
1901 /* Transform properties are stored as members of the transformCache object. Concatenate all the members into a string. */
1902 $.each(Data(element).transformCache, function(transformName) {
1903 transformValue = Data(element).transformCache[transformName];
1904
1905 /* Transform's perspective subproperty must be set first in order to take effect. Store it temporarily. */
1906 if (transformName === "transformPerspective") {
1907 perspective = transformValue;
1908 return true;
1909 }
1910
1911 /* IE9 only supports one rotation type, rotateZ, which it refers to as "rotate". */
1912 if (IE === 9 && transformName === "rotateZ") {
1913 transformName = "rotate";
1914 }
1915
1916 transformString += transformName + transformValue + " ";
1917 });
1918
1919 /* If present, set the perspective subproperty first. */
1920 if (perspective) {
1921 transformString = "perspective" + perspective + " " + transformString;
1922 }
1923 }
1924
1925 CSS.setPropertyValue(element, "transform", transformString);
1926 }
1927 };
1928
1929 /* Register hooks and normalizations. */
1930 CSS.Hooks.register();
1931 CSS.Normalizations.register();
1932
1933 /* Allow hook setting in the same fashion as jQuery's $.css(). */
1934 Velocity.hook = function (elements, arg2, arg3) {
1935 var value = undefined;
1936
1937 elements = sanitizeElements(elements);
1938
1939 $.each(elements, function(i, element) {
1940 /* Initialize Velocity's per-element data cache if this element hasn't previously been animated. */
1941 if (Data(element) === undefined) {
1942 Velocity.init(element);
1943 }
1944
1945 /* Get property value. If an element set was passed in, only return the value for the first element. */
1946 if (arg3 === undefined) {
1947 if (value === undefined) {
1948 value = Velocity.CSS.getPropertyValue(element, arg2);
1949 }
1950 /* Set property value. */
1951 } else {
1952 /* sPV returns an array of the normalized propertyName/propertyValue pair used to update the DOM. */
1953 var adjustedSet = Velocity.CSS.setPropertyValue(element, arg2, arg3);
1954
1955 /* Transform properties don't automatically set. They have to be flushed to the DOM. */
1956 if (adjustedSet[0] === "transform") {
1957 Velocity.CSS.flushTransformCache(element);
1958 }
1959
1960 value = adjustedSet;
1961 }
1962 });
1963
1964 return value;
1965 };
1966
1967 /*****************
1968 Animation
1969 *****************/
1970
1971 var animate = function() {
1972
1973 /******************
1974 Call Chain
1975 ******************/
1976
1977 /* Logic for determining what to return to the call stack when exiting out of Velocity. */
1978 function getChain () {
1979 /* If we are using the utility function, attempt to return this call's promise. If no promise library was detected,
1980 default to null instead of returning the targeted elements so that utility function's return value is standardized. */
1981 if (isUtility) {
1982 return promiseData.promise || null;
1983 /* Otherwise, if we're using $.fn, return the jQuery-/Zepto-wrapped element set. */
1984 } else {
1985 return elementsWrapped;
1986 }
1987 }
1988
1989 /*************************
1990 Arguments Assignment
1991 *************************/
1992
1993 /* To allow for expressive CoffeeScript code, Velocity supports an alternative syntax in which "properties" and "options"
1994 objects are defined on a container object that's passed in as Velocity's sole argument. */
1995 /* Note: Some browsers automatically populate arguments with a "properties" object. We detect it by checking for its default "names" property. */
1996 var syntacticSugar = (arguments[0] && (($.isPlainObject(arguments[0].properties) && !arguments[0].properties.names) || Type.isString(arguments[0].properties))),
1997 /* Whether Velocity was called via the utility function (as opposed to on a jQuery/Zepto object). */
1998 isUtility,
1999 /* When Velocity is called via the utility function ($.Velocity()/Velocity()), elements are explicitly
2000 passed in as the first parameter. Thus, argument positioning varies. We normalize them here. */
2001 elementsWrapped,
2002 argumentIndex;
2003
2004 var elements,
2005 propertiesMap,
2006 options;
2007
2008 /* Detect jQuery/Zepto elements being animated via the $.fn method. */
2009 if (Type.isWrapped(this)) {
2010 isUtility = false;
2011
2012 argumentIndex = 0;
2013 elements = this;
2014 elementsWrapped = this;
2015 /* Otherwise, raw elements are being animated via the utility function. */
2016 } else {
2017 isUtility = true;
2018
2019 argumentIndex = 1;
2020 elements = syntacticSugar ? arguments[0].elements : arguments[0];
2021 }
2022
2023 elements = sanitizeElements(elements);
2024
2025 if (!elements) {
2026 return;
2027 }
2028
2029 if (syntacticSugar) {
2030 propertiesMap = arguments[0].properties;
2031 options = arguments[0].options;
2032 } else {
2033 propertiesMap = arguments[argumentIndex];
2034 options = arguments[argumentIndex + 1];
2035 }
2036
2037 /* The length of the element set (in the form of a nodeList or an array of elements) is defaulted to 1 in case a
2038 single raw DOM element is passed in (which doesn't contain a length property). */
2039 var elementsLength = elements.length,
2040 elementsIndex = 0;
2041
2042 /***************************
2043 Argument Overloading
2044 ***************************/
2045
2046 /* Support is included for jQuery's argument overloading: $.animate(propertyMap [, duration] [, easing] [, complete]).
2047 Overloading is detected by checking for the absence of an object being passed into options. */
2048 /* Note: The stop action does not accept animation options, and is therefore excluded from this check. */
2049 if (propertiesMap !== "stop" && !$.isPlainObject(options)) {
2050 /* The utility function shifts all arguments one position to the right, so we adjust for that offset. */
2051 var startingArgumentPosition = argumentIndex + 1;
2052
2053 options = {};
2054
2055 /* Iterate through all options arguments */
2056 for (var i = startingArgumentPosition; i < arguments.length; i++) {
2057 /* Treat a number as a duration. Parse it out. */
2058 /* Note: The following RegEx will return true if passed an array with a number as its first item.
2059 Thus, arrays are skipped from this check. */
2060 if (!Type.isArray(arguments[i]) && (/fast|normal|slow/i.test(arguments[i].toString()) || /^\d/.test(arguments[i]))) {
2061 options.duration = arguments[i];
2062 /* Treat strings and arrays as easings. */
2063 } else if (Type.isString(arguments[i]) || Type.isArray(arguments[i])) {
2064 options.easing = arguments[i];
2065 /* Treat a function as a complete callback. */
2066 } else if (Type.isFunction(arguments[i])) {
2067 options.complete = arguments[i];
2068 }
2069 }
2070 }
2071
2072 /***************
2073 Promises
2074 ***************/
2075
2076 var promiseData = {
2077 promise: null,
2078 resolver: null,
2079 rejecter: null
2080 };
2081
2082 /* If this call was made via the utility function (which is the default method of invocation when jQuery/Zepto are not being used), and if
2083 promise support was detected, create a promise object for this call and store references to its resolver and rejecter methods. The resolve
2084 method is used when a call completes naturally or is prematurely stopped by the user. In both cases, completeCall() handles the associated
2085 call cleanup and promise resolving logic. The reject method is used when an invalid set of arguments is passed into a Velocity call. */
2086 /* Note: Velocity employs a call-based queueing architecture, which means that stopping an animating element actually stops the full call that
2087 triggered it -- not that one element exclusively. Similarly, there is one promise per call, and all elements targeted by a Velocity call are
2088 grouped together for the purposes of resolving and rejecting a promise. */
2089 if (isUtility && Velocity.Promise) {
2090 promiseData.promise = new Velocity.Promise(function (resolve, reject) {
2091 promiseData.resolver = resolve;
2092 promiseData.rejecter = reject;
2093 });
2094 }
2095
2096 /*********************
2097 Action Detection
2098 *********************/
2099
2100 /* Velocity's behavior is categorized into "actions": Elements can either be specially scrolled into view,
2101 or they can be started, stopped, or reversed. If a literal or referenced properties map is passed in as Velocity's
2102 first argument, the associated action is "start". Alternatively, "scroll", "reverse", or "stop" can be passed in instead of a properties map. */
2103 var action;
2104
2105 switch (propertiesMap) {
2106 case "scroll":
2107 action = "scroll";
2108 break;
2109
2110 case "reverse":
2111 action = "reverse";
2112 break;
2113
2114 case "stop":
2115 /*******************
2116 Action: Stop
2117 *******************/
2118
2119 /* Clear the currently-active delay on each targeted element. */
2120 $.each(elements, function(i, element) {
2121 if (Data(element) && Data(element).delayTimer) {
2122 /* Stop the timer from triggering its cached next() function. */
2123 clearTimeout(Data(element).delayTimer.setTimeout);
2124
2125 /* Manually call the next() function so that the subsequent queue items can progress. */
2126 if (Data(element).delayTimer.next) {
2127 Data(element).delayTimer.next();
2128 }
2129
2130 delete Data(element).delayTimer;
2131 }
2132 });
2133
2134 var callsToStop = [];
2135
2136 /* When the stop action is triggered, the elements' currently active call is immediately stopped. The active call might have
2137 been applied to multiple elements, in which case all of the call's elements will be subjected to stopping. When an element
2138 is stopped, the next item in its animation queue is immediately triggered. */
2139 /* An additional argument may be passed in to clear an element's remaining queued calls. Either true (which defaults to the "fx" queue)
2140 or a custom queue string can be passed in. */
2141 /* Note: The stop command runs prior to Queueing since its behavior is intended to take effect *immediately*,
2142 regardless of the element's current queue state. */
2143
2144 /* Iterate through every active call. */
2145 $.each(Velocity.State.calls, function(i, activeCall) {
2146 /* Inactive calls are set to false by the logic inside completeCall(). Skip them. */
2147 if (activeCall) {
2148 /* Iterate through the active call's targeted elements. */
2149 $.each(activeCall[1], function(k, activeElement) {
2150 var queueName = Type.isString(options) ? options : "";
2151
2152 if (options !== undefined && activeCall[2].queue !== queueName) {
2153 return true;
2154 }
2155
2156 /* Iterate through the calls targeted by the stop command. */
2157 $.each(elements, function(l, element) {
2158 /* Check that this call was applied to the target element. */
2159 if (element === activeElement) {
2160 /* Optionally clear the remaining queued calls. */
2161 if (options !== undefined) {
2162 /* Iterate through the items in the element's queue. */
2163 $.each($.queue(element, queueName), function(_, item) {
2164 /* The queue array can contain an "inprogress" string, which we skip. */
2165 if (Type.isFunction(item)) {
2166 /* Pass the item's callback a flag indicating that we want to abort from the queue call.
2167 (Specifically, the queue will resolve the call's associated promise then abort.) */
2168 item(null, true);
2169 }
2170 });
2171
2172 /* Clearing the $.queue() array is achieved by resetting it to []. */
2173 $.queue(element, queueName, []);
2174 }
2175
2176 if (Data(element) && queueName === "") {
2177 /* Since "reverse" uses cached start values (the previous call's endValues),
2178 these values must be changed to reflect the final value that the elements were actually tweened to. */
2179 $.each(Data(element).tweensContainer, function(m, activeTween) {
2180 activeTween.endValue = activeTween.currentValue;
2181 });
2182 }
2183
2184 callsToStop.push(i);
2185 }
2186 });
2187 });
2188 }
2189 });
2190
2191 /* Prematurely call completeCall() on each matched active call, passing an additional flag to indicate
2192 that the complete callback and display:none setting should be skipped since we're completing prematurely. */
2193 $.each(callsToStop, function(i, j) {
2194 completeCall(j, true);
2195 });
2196
2197 if (promiseData.promise) {
2198 /* Immediately resolve the promise associated with this stop call since stop runs synchronously. */
2199 promiseData.resolver(elements);
2200 }
2201
2202 /* Since we're stopping, and not proceeding with queueing, exit out of Velocity. */
2203 return getChain();
2204
2205 default:
2206 /* Treat a non-empty plain object as a literal properties map. */
2207 if ($.isPlainObject(propertiesMap) && !Type.isEmptyObject(propertiesMap)) {
2208 action = "start";
2209
2210 /****************
2211 Sequences
2212 ****************/
2213
2214 /* Check if a string matches a registered sequence (see Sequences above). */
2215 } else if (Type.isString(propertiesMap) && Velocity.Sequences[propertiesMap]) {
2216 var opts = $.extend({}, options),
2217 durationOriginal = opts.duration,
2218 delayOriginal = opts.delay || 0;
2219
2220 /* If the backwards option was passed in, reverse the element set so that elements animate from the last to the first. */
2221 if (opts.backwards === true) {
2222 elements = elements.reverse();
2223 }
2224
2225 /* Individually trigger the sequence for each element in the set to prevent users from having to handle iteration logic in their sequence. */
2226 $.each(elements, function(elementIndex, element) {
2227 /* If the stagger option was passed in, successively delay each element by the stagger value (in ms). Retain the original delay value. */
2228 if (parseFloat(opts.stagger)) {
2229 opts.delay = delayOriginal + (parseFloat(opts.stagger) * elementIndex);
2230 } else if (Type.isFunction(opts.stagger)) {
2231 opts.delay = delayOriginal + opts.stagger.call(element, elementIndex, elementsLength);
2232 }
2233
2234 /* If the drag option was passed in, successively increase/decrease (depending on the presense of opts.backwards)
2235 the duration of each element's animation, using floors to prevent producing very short durations. */
2236 if (opts.drag) {
2237 /* Default the duration of UI pack effects (callouts and transitions) to 1000ms instead of the usual default duration of 400ms. */
2238 opts.duration = parseFloat(durationOriginal) || (/^(callout|transition)/.test(propertiesMap) ? 1000 : DURATION_DEFAULT);
2239
2240 /* For each element, take the greater duration of: A) animation completion percentage relative to the original duration,
2241 B) 75% of the original duration, or C) a 200ms fallback (in case duration is already set to a low value).
2242 The end result is a baseline of 75% of the sequence's duration that increases/decreases as the end of the element set is approached. */
2243 opts.duration = Math.max(opts.duration * (opts.backwards ? 1 - elementIndex/elementsLength : (elementIndex + 1) / elementsLength), opts.duration * 0.75, 200);
2244 }
2245
2246 /* Pass in the call's opts object so that the sequence can optionally extend it. It defaults to an empty object instead of null to
2247 reduce the opts checking logic required inside the sequence. */
2248 Velocity.Sequences[propertiesMap].call(element, element, opts || {}, elementIndex, elementsLength, elements, promiseData.promise ? promiseData : undefined);
2249 });
2250
2251 /* Since the animation logic resides within the sequence's own code, abort the remainder of this call.
2252 (The performance overhead up to this point is virtually non-existant.) */
2253 /* Note: The jQuery call chain is kept intact by returning the complete element set. */
2254 return getChain();
2255 } else {
2256 var abortError = "Velocity: First argument (" + propertiesMap + ") was not a property map, a known action, or a registered sequence. Aborting.";
2257
2258 if (promiseData.promise) {
2259 promiseData.rejecter(new Error(abortError));
2260 } else {
2261 console.log(abortError);
2262 }
2263
2264 return getChain();
2265 }
2266 }
2267
2268 /**************************
2269 Call-Wide Variables
2270 **************************/
2271
2272 /* A container for CSS unit conversion ratios (e.g. %, rem, and em ==> px) that is used to cache ratios across all elements
2273 being animated in a single Velocity call. Calculating unit ratios necessitates DOM querying and updating, and is therefore
2274 avoided (via caching) wherever possible. This container is call-wide instead of page-wide to avoid the risk of using stale
2275 conversion metrics across Velocity animations that are not immediately consecutively chained. */
2276 var callUnitConversionData = {
2277 lastParent: null,
2278 lastPosition: null,
2279 lastFontSize: null,
2280 lastPercentToPxWidth: null,
2281 lastPercentToPxHeight: null,
2282 lastEmToPx: null,
2283 remToPx: null,
2284 vwToPx: null,
2285 vhToPx: null
2286 };
2287
2288 /* A container for all the ensuing tween data and metadata associated with this call. This container gets pushed to the page-wide
2289 Velocity.State.calls array that is processed during animation ticking. */
2290 var call = [];
2291
2292 /************************
2293 Element Processing
2294 ************************/
2295
2296 /* Element processing consists of three parts -- data processing that cannot go stale and data processing that *can* go stale (i.e. third-party style modifications):
2297 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.
2298 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.
2299 3) Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container.
2300 */
2301
2302 function processElement () {
2303
2304 /*************************
2305 Part I: Pre-Queueing
2306 *************************/
2307
2308 /***************************
2309 Element-Wide Variables
2310 ***************************/
2311
2312 var element = this,
2313 /* The runtime opts object is the extension of the current call's options and Velocity's page-wide option defaults. */
2314 opts = $.extend({}, Velocity.defaults, options),
2315 /* A container for the processed data associated with each property in the propertyMap.
2316 (Each property in the map produces its own "tween".) */
2317 tweensContainer = {},
2318 elementUnitConversionData;
2319
2320 /******************
2321 Element Init
2322 ******************/
2323
2324 if (Data(element) === undefined) {
2325 Velocity.init(element);
2326 }
2327
2328 /******************
2329 Option: Delay
2330 ******************/
2331
2332 /* Since queue:false doesn't respect the item's existing queue, we avoid injecting its delay here (it's set later on). */
2333 /* Note: Velocity rolls its own delay function since jQuery doesn't have a utility alias for $.fn.delay()
2334 (and thus requires jQuery element creation, which we avoid since its overhead includes DOM querying). */
2335 if (parseFloat(opts.delay) && opts.queue !== false) {
2336 $.queue(element, opts.queue, function(next) {
2337 /* 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. */
2338 Velocity.velocityQueueEntryFlag = true;
2339
2340 /* The ensuing queue item (which is assigned to the "next" argument that $.queue() automatically passes in) will be triggered after a setTimeout delay.
2341 The setTimeout is stored so that it can be subjected to clearTimeout() if this animation is prematurely stopped via Velocity's "stop" command. */
2342 Data(element).delayTimer = {
2343 setTimeout: setTimeout(next, parseFloat(opts.delay)),
2344 next: next
2345 };
2346 });
2347 }
2348
2349 /*********************
2350 Option: Duration
2351 *********************/
2352
2353 /* In mock mode, all animations are forced to 1ms so that they occur immediately upon the next rAF tick. */
2354 if (Velocity.mock === true) {
2355 opts.duration = 1;
2356 } else {
2357 /* Support for jQuery's named durations. */
2358 switch (opts.duration.toString().toLowerCase()) {
2359 case "fast":
2360 opts.duration = 200;
2361 break;
2362
2363 case "normal":
2364 opts.duration = DURATION_DEFAULT;
2365 break;
2366
2367 case "slow":
2368 opts.duration = 600;
2369 break;
2370
2371 default:
2372 /* 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). */
2373 opts.duration = parseFloat(opts.duration) || 1;
2374 }
2375 }
2376
2377 /*******************
2378 Option: Easing
2379 *******************/
2380
2381 opts.easing = getEasing(opts.easing, opts.duration);
2382
2383 /**********************
2384 Option: Callbacks
2385 **********************/
2386
2387 /* Callbacks must functions. Otherwise, default to null. */
2388 if (opts.begin && !Type.isFunction(opts.begin)) {
2389 opts.begin = null;
2390 }
2391
2392 if (opts.progress && !Type.isFunction(opts.progress)) {
2393 opts.progress = null;
2394 }
2395
2396 if (opts.complete && !Type.isFunction(opts.complete)) {
2397 opts.complete = null;
2398 }
2399
2400 /*********************************
2401 Option: Display & Visibility
2402 *********************************/
2403
2404 /* Refer to Velocity's documentation (VelocityJS.org/#displayAndVisibility) for a description of the display and visibility options' behavior. */
2405 /* Note: We strictly check for undefined instead of falsiness because display accepts an empty string value. */
2406 if (opts.display !== undefined && opts.display !== null) {
2407 opts.display = opts.display.toString().toLowerCase();
2408
2409 /* Users can pass in a special "auto" value to instruct Velocity to set the element to its default display value. */
2410 if (opts.display === "auto") {
2411 opts.display = Velocity.CSS.Values.getDisplayType(element);
2412 }
2413 }
2414
2415 if (opts.visibility) {
2416 opts.visibility = opts.visibility.toString().toLowerCase();
2417 }
2418
2419 /**********************
2420 Option: mobileHA
2421 **********************/
2422
2423 /* When set to true, and if this is a mobile device, mobileHA automatically enables hardware acceleration (via a null transform hack)
2424 on animating elements. HA is removed from the element at the completion of its animation. */
2425 /* 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. */
2426 /* Note: You can read more about the use of mobileHA in Velocity's documentation: VelocityJS.org/#mobileHA. */
2427 opts.mobileHA = (opts.mobileHA && Velocity.State.isMobile && !Velocity.State.isGingerbread);
2428
2429 /***********************
2430 Part II: Queueing
2431 ***********************/
2432
2433 /* When a set of elements is targeted by a Velocity call, the set is broken up and each element has the current Velocity call individually queued onto it.
2434 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. */
2435 /* In each queue, tween data is processed for each animating property then pushed onto the call-wide calls array. When the last element in the set has had its tweens processed,
2436 the call array is pushed to Velocity.State.calls for live processing by the requestAnimationFrame tick. */
2437 function buildQueue (next) {
2438
2439 /*******************
2440 Option: Begin
2441 *******************/
2442
2443 /* 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. */
2444 if (opts.begin && elementsIndex === 0) {
2445 /* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */
2446 try {
2447 opts.begin.call(elements, elements);
2448 } catch (error) {
2449 setTimeout(function() { throw error; }, 1);
2450 }
2451 }
2452
2453 /*****************************************
2454 Tween Data Construction (for Scroll)
2455 *****************************************/
2456
2457 /* 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. */
2458 if (action === "scroll") {
2459 /* The scroll action uniquely takes an optional "offset" option -- specified in pixels -- that offsets the targeted scroll position. */
2460 var scrollDirection = (/^x$/i.test(opts.axis) ? "Left" : "Top"),
2461 scrollOffset = parseFloat(opts.offset) || 0,
2462 scrollPositionCurrent,
2463 scrollPositionCurrentAlternate,
2464 scrollPositionEnd;
2465
2466 /* Scroll also uniquely takes an optional "container" option, which indicates the parent element that should be scrolled --
2467 as opposed to the browser window itself. This is useful for scrolling toward an element that's inside an overflowing parent element. */
2468 if (opts.container) {
2469 /* Ensure that either a jQuery object or a raw DOM element was passed in. */
2470 if (Type.isWrapped(opts.container) || Type.isNode(opts.container)) {
2471 /* Extract the raw DOM element from the jQuery wrapper. */
2472 opts.container = opts.container[0] || opts.container;
2473 /* Note: Unlike other properties in Velocity, the browser's scroll position is never cached since it so frequently changes
2474 (due to the user's natural interaction with the page). */
2475 scrollPositionCurrent = opts.container["scroll" + scrollDirection]; /* GET */
2476
2477 /* $.position() values are relative to the container's currently viewable area (without taking into account the container's true dimensions
2478 -- say, for example, if the container was not overflowing). Thus, the scroll end value is the sum of the child element's position *and*
2479 the scroll container's current scroll position. */
2480 /* Note: jQuery does not offer a utility alias for $.position(), so we have to incur jQuery object conversion here.
2481 This syncs up with an ensuing batch of GETs, so it fortunately does not trigger layout thrashing. */
2482 scrollPositionEnd = (scrollPositionCurrent + $(element).position()[scrollDirection.toLowerCase()]) + scrollOffset; /* GET */
2483 /* 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. */
2484 } else {
2485 opts.container = null;
2486 }
2487 } else {
2488 /* If the window itself is being scrolled -- not a containing element -- perform a live scroll position lookup using
2489 the appropriate cached property names (which differ based on browser type). */
2490 scrollPositionCurrent = Velocity.State.scrollAnchor[Velocity.State["scrollProperty" + scrollDirection]]; /* GET */
2491 /* 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. */
2492 scrollPositionCurrentAlternate = Velocity.State.scrollAnchor[Velocity.State["scrollProperty" + (scrollDirection === "Left" ? "Top" : "Left")]]; /* GET */
2493
2494 /* Unlike $.position(), $.offset() values are relative to the browser window's true dimensions -- not merely its currently viewable area --
2495 and therefore end values do not need to be compounded onto current values. */
2496 scrollPositionEnd = $(element).offset()[scrollDirection.toLowerCase()] + scrollOffset; /* GET */
2497 }
2498
2499 /* Since there's only one format that scroll's associated tweensContainer can take, we create it manually. */
2500 tweensContainer = {
2501 scroll: {
2502 rootPropertyValue: false,
2503 startValue: scrollPositionCurrent,
2504 currentValue: scrollPositionCurrent,
2505 endValue: scrollPositionEnd,
2506 unitType: "",
2507 easing: opts.easing,
2508 scrollData: {
2509 container: opts.container,
2510 direction: scrollDirection,
2511 alternateValue: scrollPositionCurrentAlternate
2512 }
2513 },
2514 element: element
2515 };
2516
2517 if (Velocity.debug) console.log("tweensContainer (scroll): ", tweensContainer.scroll, element);
2518
2519 /******************************************
2520 Tween Data Construction (for Reverse)
2521 ******************************************/
2522
2523 /* Reverse acts like a "start" action in that a property map is animated toward. The only difference is
2524 that the property map used for reverse is the inverse of the map used in the previous call. Thus, we manipulate
2525 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. */
2526 /* 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.) */
2527 /* Note: Reverse calls do not need to be consecutively chained onto a currently-animating element in order to operate on cached values;
2528 there is no harm to reverse being called on a potentially stale data cache since reverse's behavior is simply defined
2529 as reverting to the element's values as they were prior to the previous *Velocity* call. */
2530 } else if (action === "reverse") {
2531 /* Abort if there is no prior animation data to reverse to. */
2532 if (!Data(element).tweensContainer) {
2533 /* Dequeue the element so that this queue entry releases itself immediately, allowing subsequent queue entries to run. */
2534 $.dequeue(element, opts.queue);
2535
2536 return;
2537 } else {
2538 /*********************
2539 Options Parsing
2540 *********************/
2541
2542 /* If the element was hidden via the display option in the previous call,
2543 revert display to "auto" prior to reversal so that the element is visible again. */
2544 if (Data(element).opts.display === "none") {
2545 Data(element).opts.display = "auto";
2546 }
2547
2548 if (Data(element).opts.visibility === "hidden") {
2549 Data(element).opts.visibility = "visible";
2550 }
2551
2552 /* If the loop option was set in the previous call, disable it so that "reverse" calls aren't recursively generated.
2553 Further, remove the previous call's callback options; typically, users do not want these to be refired. */
2554 Data(element).opts.loop = false;
2555 Data(element).opts.begin = null;
2556 Data(element).opts.complete = null;
2557
2558 /* Since we're extending an opts object that has already been extended with the defaults options object,
2559 we remove non-explicitly-defined properties that are auto-assigned values. */
2560 if (!options.easing) {
2561 delete opts.easing;
2562 }
2563
2564 if (!options.duration) {
2565 delete opts.duration;
2566 }
2567
2568 /* The opts object used for reversal is an extension of the options object optionally passed into this
2569 reverse call plus the options used in the previous Velocity call. */
2570 opts = $.extend({}, Data(element).opts, opts);
2571
2572 /*************************************
2573 Tweens Container Reconstruction
2574 *************************************/
2575
2576 /* Create a deepy copy (indicated via the true flag) of the previous call's tweensContainer. */
2577 var lastTweensContainer = $.extend(true, {}, Data(element).tweensContainer);
2578
2579 /* Manipulate the previous tweensContainer by replacing its end values and currentValues with its start values. */
2580 for (var lastTween in lastTweensContainer) {
2581 /* In addition to tween data, tweensContainers contain an element property that we ignore here. */
2582 if (lastTween !== "element") {
2583 var lastStartValue = lastTweensContainer[lastTween].startValue;
2584
2585 lastTweensContainer[lastTween].startValue = lastTweensContainer[lastTween].currentValue = lastTweensContainer[lastTween].endValue;
2586 lastTweensContainer[lastTween].endValue = lastStartValue;
2587
2588 /* Easing is the only option that embeds into the individual tween data (since it can be defined on a per-property basis).
2589 Accordingly, every property's easing value must be updated when an options object is passed in with a reverse call.
2590 The side effect of this extensibility is that all per-property easing values are forcefully reset to the new value. */
2591 if (!Type.isEmptyObject(options)) {
2592 lastTweensContainer[lastTween].easing = opts.easing;
2593 }
2594
2595 if (Velocity.debug) console.log("reverse tweensContainer (" + lastTween + "): " + JSON.stringify(lastTweensContainer[lastTween]), element);
2596 }
2597 }
2598
2599 tweensContainer = lastTweensContainer;
2600 }
2601
2602 /*****************************************
2603 Tween Data Construction (for Start)
2604 *****************************************/
2605
2606 } else if (action === "start") {
2607
2608 /*************************
2609 Value Transferring
2610 *************************/
2611
2612 /* If this queue entry follows a previous Velocity-initiated queue entry *and* if this entry was created
2613 while the element was in the process of being animated by Velocity, then this current call is safe to use
2614 the end values from the prior call as its start values. Velocity attempts to perform this value transfer
2615 process whenever possible in order to avoid requerying the DOM. */
2616 /* If values aren't transferred from a prior call and start values were not forcefed by the user (more on this below),
2617 then the DOM is queried for the element's current values as a last resort. */
2618 /* Note: Conversely, animation reversal (and looping) *always* perform inter-call value transfers; they never requery the DOM. */
2619 var lastTweensContainer;
2620
2621 /* The per-element isAnimating flag is used to indicate whether it's safe (i.e. the data isn't stale)
2622 to transfer over end values to use as start values. If it's set to true and there is a previous
2623 Velocity call to pull values from, do so. */
2624 if (Data(element).tweensContainer && Data(element).isAnimating === true) {
2625 lastTweensContainer = Data(element).tweensContainer;
2626 }
2627
2628 /***************************
2629 Tween Data Calculation
2630 ***************************/
2631
2632 /* This function parses property data and defaults endValue, easing, and startValue as appropriate. */
2633 /* Property map values can either take the form of 1) a single value representing the end value,
2634 or 2) an array in the form of [ endValue, [, easing] [, startValue] ].
2635 The optional third parameter is a forcefed startValue to be used instead of querying the DOM for
2636 the element's current value. Read Velocity's docmentation to learn more about forcefeeding: VelocityJS.org/#forcefeeding */
2637 function parsePropertyValue (valueData, skipResolvingEasing) {
2638 var endValue = undefined,
2639 easing = undefined,
2640 startValue = undefined;
2641
2642 /* Handle the array format, which can be structured as one of three potential overloads:
2643 A) [ endValue, easing, startValue ], B) [ endValue, easing ], or C) [ endValue, startValue ] */
2644 if (Type.isArray(valueData)) {
2645 /* endValue is always the first item in the array. Don't bother validating endValue's value now
2646 since the ensuing property cycling logic does that. */
2647 endValue = valueData[0];
2648
2649 /* Two-item array format: If the second item is a number, function, or hex string, treat it as a
2650 start value since easings can only be non-hex strings or arrays. */
2651 if ((!Type.isArray(valueData[1]) && /^[\d-]/.test(valueData[1])) || Type.isFunction(valueData[1]) || CSS.RegEx.isHex.test(valueData[1])) {
2652 startValue = valueData[1];
2653 /* Two or three-item array: If the second item is a non-hex string or an array, treat it as an easing. */
2654 } else if ((Type.isString(valueData[1]) && !CSS.RegEx.isHex.test(valueData[1])) || Type.isArray(valueData[1])) {
2655 easing = skipResolvingEasing ? valueData[1] : getEasing(valueData[1], opts.duration);
2656
2657 /* Don't bother validating startValue's value now since the ensuing property cycling logic inherently does that. */
2658 if (valueData[2] !== undefined) {
2659 startValue = valueData[2];
2660 }
2661 }
2662 /* Handle the single-value format. */
2663 } else {
2664 endValue = valueData;
2665 }
2666
2667 /* Default to the call's easing if a per-property easing type was not defined. */
2668 if (!skipResolvingEasing) {
2669 easing = easing || opts.easing;
2670 }
2671
2672 /* If functions were passed in as values, pass the function the current element as its context,
2673 plus the element's index and the element set's size as arguments. Then, assign the returned value. */
2674 if (Type.isFunction(endValue)) {
2675 endValue = endValue.call(element, elementsIndex, elementsLength);
2676 }
2677
2678 if (Type.isFunction(startValue)) {
2679 startValue = startValue.call(element, elementsIndex, elementsLength);
2680 }
2681
2682 /* Allow startValue to be left as undefined to indicate to the ensuing code that its value was not forcefed. */
2683 return [ endValue || 0, easing, startValue ];
2684 }
2685
2686 /* Cycle through each property in the map, looking for shorthand color properties (e.g. "color" as opposed to "colorRed"). Inject the corresponding
2687 colorRed, colorGreen, and colorBlue RGB component tweens into the propertiesMap (which Velocity understands) and remove the shorthand property. */
2688 $.each(propertiesMap, function(property, value) {
2689 /* Find shorthand color properties that have been passed a hex string. */
2690 if (RegExp("^" + CSS.Lists.colors.join("$|^") + "$").test(property)) {
2691 /* Parse the value data for each shorthand. */
2692 var valueData = parsePropertyValue(value, true),
2693 endValue = valueData[0],
2694 easing = valueData[1],
2695 startValue = valueData[2];
2696
2697 if (CSS.RegEx.isHex.test(endValue)) {
2698 /* Convert the hex strings into their RGB component arrays. */
2699 var colorComponents = [ "Red", "Green", "Blue" ],
2700 endValueRGB = CSS.Values.hexToRgb(endValue),
2701 startValueRGB = startValue ? CSS.Values.hexToRgb(startValue) : undefined;
2702
2703 /* Inject the RGB component tweens into propertiesMap. */
2704 for (var i = 0; i < colorComponents.length; i++) {
2705 propertiesMap[property + colorComponents[i]] = [ endValueRGB[i], easing, startValueRGB ? startValueRGB[i] : startValueRGB ];
2706 }
2707
2708 /* Remove the intermediary shorthand property entry now that we've processed it. */
2709 delete propertiesMap[property];
2710 }
2711 }
2712 });
2713
2714 /* Create a tween out of each property, and append its associated data to tweensContainer. */
2715 for (var property in propertiesMap) {
2716
2717 /**************************
2718 Start Value Sourcing
2719 **************************/
2720
2721 /* Parse out endValue, easing, and startValue from the property's data. */
2722 var valueData = parsePropertyValue(propertiesMap[property]),
2723 endValue = valueData[0],
2724 easing = valueData[1],
2725 startValue = valueData[2];
2726
2727 /* Now that the original property name's format has been used for the parsePropertyValue() lookup above,
2728 we force the property to its camelCase styling to normalize it for manipulation. */
2729 property = CSS.Names.camelCase(property);
2730
2731 /* 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. */
2732 var rootProperty = CSS.Hooks.getRoot(property),
2733 rootPropertyValue = false;
2734
2735 /* Properties that are not supported by the browser (and do not have an associated normalization) will
2736 inherently produce no style changes when set, so they are skipped in order to decrease animation tick overhead.
2737 Property support is determined via prefixCheck(), which returns a false flag when no supported is detected. */
2738 /* Note: Since SVG elements have some of their properties directly applied as HTML attributes,
2739 there is no way to check for their explicit browser support, and so we skip skip this check for them. */
2740 if (!Data(element).isSVG && CSS.Names.prefixCheck(rootProperty)[1] === false && CSS.Normalizations.registered[rootProperty] === undefined) {
2741 if (Velocity.debug) console.log("Skipping [" + rootProperty + "] due to a lack of browser support.");
2742
2743 continue;
2744 }
2745
2746 /* If the display option is being set to a non-"none" (e.g. "block") and opacity (filter on IE<=8) is being
2747 animated to an endValue of non-zero, the user's intention is to fade in from invisible, thus we forcefeed opacity
2748 a startValue of 0 if its startValue hasn't already been sourced by value transferring or prior forcefeeding. */
2749 if (((opts.display !== undefined && opts.display !== null && opts.display !== "none") || (opts.visibility && opts.visibility !== "hidden")) && /opacity|filter/.test(property) && !startValue && endValue !== 0) {
2750 startValue = 0;
2751 }
2752
2753 /* If values have been transferred from the previous Velocity call, extract the endValue and rootPropertyValue
2754 for all of the current call's properties that were *also* animated in the previous call. */
2755 /* Note: Value transferring can optionally be disabled by the user via the _cacheValues option. */
2756 if (opts._cacheValues && lastTweensContainer && lastTweensContainer[property]) {
2757 if (startValue === undefined) {
2758 startValue = lastTweensContainer[property].endValue + lastTweensContainer[property].unitType;
2759 }
2760
2761 /* The previous call's rootPropertyValue is extracted from the element's data cache since that's the
2762 instance of rootPropertyValue that gets freshly updated by the tweening process, whereas the rootPropertyValue
2763 attached to the incoming lastTweensContainer is equal to the root property's value prior to any tweening. */
2764 rootPropertyValue = Data(element).rootPropertyValueCache[rootProperty];
2765 /* If values were not transferred from a previous Velocity call, query the DOM as needed. */
2766 } else {
2767 /* Handle hooked properties. */
2768 if (CSS.Hooks.registered[property]) {
2769 if (startValue === undefined) {
2770 rootPropertyValue = CSS.getPropertyValue(element, rootProperty); /* GET */
2771 /* Note: The following getPropertyValue() call does not actually trigger a DOM query;
2772 getPropertyValue() will extract the hook from rootPropertyValue. */
2773 startValue = CSS.getPropertyValue(element, property, rootPropertyValue);
2774 /* If startValue is already defined via forcefeeding, do not query the DOM for the root property's value;
2775 just grab rootProperty's zero-value template from CSS.Hooks. This overwrites the element's actual
2776 root property value (if one is set), but this is acceptable since the primary reason users forcefeed is
2777 to avoid DOM queries, and thus we likewise avoid querying the DOM for the root property's value. */
2778 } else {
2779 /* Grab this hook's zero-value template, e.g. "0px 0px 0px black". */
2780 rootPropertyValue = CSS.Hooks.templates[rootProperty][1];
2781 }
2782 /* Handle non-hooked properties that haven't already been defined via forcefeeding. */
2783 } else if (startValue === undefined) {
2784 startValue = CSS.getPropertyValue(element, property); /* GET */
2785 }
2786 }
2787
2788 /**************************
2789 Value Data Extraction
2790 **************************/
2791
2792 var separatedValue,
2793 endValueUnitType,
2794 startValueUnitType,
2795 operator = false;
2796
2797 /* Separates a property value into its numeric value and its unit type. */
2798 function separateValue (property, value) {
2799 var unitType,
2800 numericValue;
2801
2802 numericValue = (value || 0)
2803 .toString()
2804 .toLowerCase()
2805 /* Match the unit type at the end of the value. */
2806 .replace(/[%A-z]+$/, function(match) {
2807 /* Grab the unit type. */
2808 unitType = match;
2809
2810 /* Strip the unit type off of value. */
2811 return "";
2812 });
2813
2814 /* If no unit type was supplied, assign one that is appropriate for this property (e.g. "deg" for rotateZ or "px" for width). */
2815 if (!unitType) {
2816 unitType = CSS.Values.getUnitType(property);
2817 }
2818
2819 return [ numericValue, unitType ];
2820 }
2821
2822 /* Separate startValue. */
2823 separatedValue = separateValue(property, startValue);
2824 startValue = separatedValue[0];
2825 startValueUnitType = separatedValue[1];
2826
2827 /* Separate endValue, and extract a value operator (e.g. "+=", "-=") if one exists. */
2828 separatedValue = separateValue(property, endValue);
2829 endValue = separatedValue[0].replace(/^([+-\/*])=/, function(match, subMatch) {
2830 operator = subMatch;
2831
2832 /* Strip the operator off of the value. */
2833 return "";
2834 });
2835 endValueUnitType = separatedValue[1];
2836
2837 /* Parse float values from endValue and startValue. Default to 0 if NaN is returned. */
2838 startValue = parseFloat(startValue) || 0;
2839 endValue = parseFloat(endValue) || 0;
2840
2841 /***************************************
2842 Property-Specific Value Conversion
2843 ***************************************/
2844
2845 /* Custom support for properties that don't actually accept the % unit type, but where pollyfilling is trivial and relatively foolproof. */
2846 if (endValueUnitType === "%") {
2847 /* A %-value fontSize/lineHeight is relative to the parent's fontSize (as opposed to the parent's dimensions),
2848 which is identical to the em unit's behavior, so we piggyback off of that. */
2849 if (/^(fontSize|lineHeight)$/.test(property)) {
2850 /* Convert % into an em decimal value. */
2851 endValue = endValue / 100;
2852 endValueUnitType = "em";
2853 /* For scaleX and scaleY, convert the value into its decimal format and strip off the unit type. */
2854 } else if (/^scale/.test(property)) {
2855 endValue = endValue / 100;
2856 endValueUnitType = "";
2857 /* For RGB components, take the defined percentage of 255 and strip off the unit type. */
2858 } else if (/(Red|Green|Blue)$/i.test(property)) {
2859 endValue = (endValue / 100) * 255;
2860 endValueUnitType = "";
2861 }
2862 }
2863
2864 /***************************
2865 Unit Ratio Calculation
2866 ***************************/
2867
2868 /* When queried, the browser returns (most) CSS property values in pixels. Therefore, if an endValue with a unit type of
2869 %, em, or rem is animated toward, startValue must be converted from pixels into the same unit type as endValue in order
2870 for value manipulation logic (increment/decrement) to proceed. Further, if the startValue was forcefed or transferred
2871 from a previous call, startValue may also not be in pixels. Unit conversion logic therefore consists of two steps:
2872 1) Calculating the ratio of %/em/rem/vh/vw relative to pixels
2873 2) Converting startValue into the same unit of measurement as endValue based on these ratios. */
2874 /* Unit conversion ratios are calculated by inserting a sibling node next to the target node, copying over its position property,
2875 setting values with the target unit type then comparing the returned pixel value. */
2876 /* Note: Even if only one of these unit types is being animated, all unit ratios are calculated at once since the overhead
2877 of batching the SETs and GETs together upfront outweights the potential overhead
2878 of layout thrashing caused by re-querying for uncalculated ratios for subsequently-processed properties. */
2879 /* Todo: Shift this logic into the calls' first tick instance so that it's synced with RAF. */
2880 function calculateUnitRatios () {
2881
2882 /************************
2883 Same Ratio Checks
2884 ************************/
2885
2886 /* The properties below are used to determine whether the element differs sufficiently from this call's
2887 previously iterated element to also differ in its unit conversion ratios. If the properties match up with those
2888 of the prior element, the prior element's conversion ratios are used. Like most optimizations in Velocity,
2889 this is done to minimize DOM querying. */
2890 var sameRatioIndicators = {
2891 myParent: element.parentNode || document.body, /* GET */
2892 position: CSS.getPropertyValue(element, "position"), /* GET */
2893 fontSize: CSS.getPropertyValue(element, "fontSize") /* GET */
2894 },
2895 /* Determine if the same % ratio can be used. % is based on the element's position value and its parent's width and height dimensions. */
2896 samePercentRatio = ((sameRatioIndicators.position === callUnitConversionData.lastPosition) && (sameRatioIndicators.myParent === callUnitConversionData.lastParent)),
2897 /* Determine if the same em ratio can be used. em is relative to the element's fontSize. */
2898 sameEmRatio = (sameRatioIndicators.fontSize === callUnitConversionData.lastFontSize);
2899
2900 /* Store these ratio indicators call-wide for the next element to compare against. */
2901 callUnitConversionData.lastParent = sameRatioIndicators.myParent;
2902 callUnitConversionData.lastPosition = sameRatioIndicators.position;
2903 callUnitConversionData.lastFontSize = sameRatioIndicators.fontSize;
2904
2905 /***************************
2906 Element-Specific Units
2907 ***************************/
2908
2909 /* Note: IE8 rounds to the nearest pixel when returning CSS values, thus we perform conversions using a measurement
2910 of 100 (instead of 1) to give our ratios a precision of at least 2 decimal values. */
2911 var measurement = 100,
2912 unitRatios = {};
2913
2914 if (!sameEmRatio || !samePercentRatio) {
2915 var dummy = Data(element).isSVG ? document.createElementNS("http://www.w3.org/2000/svg", "rect") : document.createElement("div");
2916
2917 Velocity.init(dummy);
2918 sameRatioIndicators.myParent.appendChild(dummy);
2919
2920 /* To accurately and consistently calculate conversion ratios, the element's cascaded overflow and box-sizing are stripped.
2921 Similarly, since width/height can be artificially constrained by their min-/max- equivalents, these are controlled for as well. */
2922 /* Note: Overflow must be also be controlled for per-axis since the overflow property overwrites its per-axis values. */
2923 $.each([ "overflow", "overflowX", "overflowY" ], function(i, property) {
2924 Velocity.CSS.setPropertyValue(dummy, property, "hidden");
2925 });
2926 Velocity.CSS.setPropertyValue(dummy, "position", sameRatioIndicators.position);
2927 Velocity.CSS.setPropertyValue(dummy, "fontSize", sameRatioIndicators.fontSize);
2928 Velocity.CSS.setPropertyValue(dummy, "boxSizing", "content-box");
2929
2930 /* width and height act as our proxy properties for measuring the horizontal and vertical % ratios. */
2931 $.each([ "minWidth", "maxWidth", "width", "minHeight", "maxHeight", "height" ], function(i, property) {
2932 Velocity.CSS.setPropertyValue(dummy, property, measurement + "%");
2933 });
2934 /* paddingLeft arbitrarily acts as our proxy property for the em ratio. */
2935 Velocity.CSS.setPropertyValue(dummy, "paddingLeft", measurement + "em");
2936
2937 /* 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. */
2938 unitRatios.percentToPxWidth = callUnitConversionData.lastPercentToPxWidth = (parseFloat(CSS.getPropertyValue(dummy, "width", null, true)) || 1) / measurement; /* GET */
2939 unitRatios.percentToPxHeight = callUnitConversionData.lastPercentToPxHeight = (parseFloat(CSS.getPropertyValue(dummy, "height", null, true)) || 1) / measurement; /* GET */
2940 unitRatios.emToPx = callUnitConversionData.lastEmToPx = (parseFloat(CSS.getPropertyValue(dummy, "paddingLeft")) || 1) / measurement; /* GET */
2941
2942 sameRatioIndicators.myParent.removeChild(dummy);
2943 } else {
2944 unitRatios.emToPx = callUnitConversionData.lastEmToPx;
2945 unitRatios.percentToPxWidth = callUnitConversionData.lastPercentToPxWidth;
2946 unitRatios.percentToPxHeight = callUnitConversionData.lastPercentToPxHeight;
2947 }
2948
2949 /***************************
2950 Element-Agnostic Units
2951 ***************************/
2952
2953 /* Whereas % and em ratios are determined on a per-element basis, the rem unit only needs to be checked
2954 once per call since it's exclusively dependant upon document.body's fontSize. If this is the first time
2955 that calculateUnitRatios() is being run during this call, remToPx will still be set to its default value of null,
2956 so we calculate it now. */
2957 if (callUnitConversionData.remToPx === null) {
2958 /* Default to browsers' default fontSize of 16px in the case of 0. */
2959 callUnitConversionData.remToPx = parseFloat(CSS.getPropertyValue(document.body, "fontSize")) || 16; /* GET */
2960 }
2961
2962 /* Similarly, viewport units are %-relative to the window's inner dimensions. */
2963 if (callUnitConversionData.vwToPx === null) {
2964 callUnitConversionData.vwToPx = parseFloat(window.innerWidth) / 100; /* GET */
2965 callUnitConversionData.vhToPx = parseFloat(window.innerHeight) / 100; /* GET */
2966 }
2967
2968 unitRatios.remToPx = callUnitConversionData.remToPx;
2969 unitRatios.vwToPx = callUnitConversionData.vwToPx;
2970 unitRatios.vhToPx = callUnitConversionData.vhToPx;
2971
2972 if (Velocity.debug >= 1) console.log("Unit ratios: " + JSON.stringify(unitRatios), element);
2973
2974 return unitRatios;
2975 }
2976
2977 /********************
2978 Unit Conversion
2979 ********************/
2980
2981 /* The * and / operators, which are not passed in with an associated unit, inherently use startValue's unit. Skip value and unit conversion. */
2982 if (/[\/*]/.test(operator)) {
2983 endValueUnitType = startValueUnitType;
2984 /* If startValue and endValue differ in unit type, convert startValue into the same unit type as endValue so that if endValueUnitType
2985 is a relative unit (%, em, rem), the values set during tweening will continue to be accurately relative even if the metrics they depend
2986 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
2987 would become stale if the original unit being animated toward was relative and the underlying metrics change during the animation. */
2988 /* Since 0 is 0 in any unit type, no conversion is necessary when startValue is 0 -- we just start at 0 with endValueUnitType. */
2989 } else if ((startValueUnitType !== endValueUnitType) && startValue !== 0) {
2990 /* Unit conversion is also skipped when endValue is 0, but *startValueUnitType* must be used for tween values to remain accurate. */
2991 /* Note: Skipping unit conversion here means that if endValueUnitType was originally a relative unit, the animation won't relatively
2992 match the underlying metrics if they change, but this is acceptable since we're animating toward invisibility instead of toward visibility,
2993 which remains past the point of the animation's completion. */
2994 if (endValue === 0) {
2995 endValueUnitType = startValueUnitType;
2996 } else {
2997 /* By this point, we cannot avoid unit conversion (it's undesirable since it causes layout thrashing).
2998 If we haven't already, we trigger calculateUnitRatios(), which runs once per element per call. */
2999 elementUnitConversionData = elementUnitConversionData || calculateUnitRatios();
3000
3001 /* The following RegEx matches CSS properties that have their % values measured relative to the x-axis. */
3002 /* 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. */
3003 var axis = (/margin|padding|left|right|width|text|word|letter/i.test(property) || /X$/.test(property) || property === "x") ? "x" : "y";
3004
3005 /* In order to avoid generating n^2 bespoke conversion functions, unit conversion is a two-step process:
3006 1) Convert startValue into pixels. 2) Convert this new pixel value into endValue's unit type. */
3007 switch (startValueUnitType) {
3008 case "%":
3009 /* Note: translateX and translateY are the only properties that are %-relative to an element's own dimensions -- not its parent's dimensions.
3010 Velocity does not include a special conversion process to account for this behavior. Therefore, animating translateX/Y from a % value
3011 to a non-% value will produce an incorrect start value. Fortunately, this sort of cross-unit conversion is rarely done by users in practice. */
3012 startValue *= (axis === "x" ? elementUnitConversionData.percentToPxWidth : elementUnitConversionData.percentToPxHeight);
3013 break;
3014
3015 case "px":
3016 /* px acts as our midpoint in the unit conversion process; do nothing. */
3017 break;
3018
3019 default:
3020 startValue *= elementUnitConversionData[startValueUnitType + "ToPx"];
3021 }
3022
3023 /* Invert the px ratios to convert into to the target unit. */
3024 switch (endValueUnitType) {
3025 case "%":
3026 startValue *= 1 / (axis === "x" ? elementUnitConversionData.percentToPxWidth : elementUnitConversionData.percentToPxHeight);
3027 break;
3028
3029 case "px":
3030 /* startValue is already in px, do nothing; we're done. */
3031 break;
3032
3033 default:
3034 startValue *= 1 / elementUnitConversionData[endValueUnitType + "ToPx"];
3035 }
3036 }
3037 }
3038
3039 /*********************
3040 Relative Values
3041 *********************/
3042
3043 /* Operator logic must be performed last since it requires unit-normalized start and end values. */
3044 /* Note: Relative *percent values* do not behave how most people think; while one would expect "+=50%"
3045 to increase the property 1.5x its current value, it in fact increases the percent units in absolute terms:
3046 50 points is added on top of the current % value. */
3047 switch (operator) {
3048 case "+":
3049 endValue = startValue + endValue;
3050 break;
3051
3052 case "-":
3053 endValue = startValue - endValue;
3054 break;
3055
3056 case "*":
3057 endValue = startValue * endValue;
3058 break;
3059
3060 case "/":
3061 endValue = startValue / endValue;
3062 break;
3063 }
3064
3065 /**************************
3066 tweensContainer Push
3067 **************************/
3068
3069 /* Construct the per-property tween object, and push it to the element's tweensContainer. */
3070 tweensContainer[property] = {
3071 rootPropertyValue: rootPropertyValue,
3072 startValue: startValue,
3073 currentValue: startValue,
3074 endValue: endValue,
3075 unitType: endValueUnitType,
3076 easing: easing
3077 };
3078
3079 if (Velocity.debug) console.log("tweensContainer (" + property + "): " + JSON.stringify(tweensContainer[property]), element);
3080 }
3081
3082 /* Along with its property data, store a reference to the element itself onto tweensContainer. */
3083 tweensContainer.element = element;
3084 }
3085
3086 /*****************
3087 Call Push
3088 *****************/
3089
3090 /* Note: tweensContainer can be empty if all of the properties in this call's property map were skipped due to not
3091 being supported by the browser. The element property is used for checking that the tweensContainer has been appended to. */
3092 if (tweensContainer.element) {
3093 /* Apply the "velocity-animating" indicator class. */
3094 CSS.Values.addClass(element, "velocity-animating");
3095
3096 /* The call array houses the tweensContainers for each element being animated in the current call. */
3097 call.push(tweensContainer);
3098
3099 /* Store the tweensContainer and options if we're working on the default effects queue, so that they can be used by the reverse command. */
3100 if (opts.queue === "") {
3101 Data(element).tweensContainer = tweensContainer;
3102 Data(element).opts = opts;
3103 }
3104
3105 /* Switch on the element's animating flag. */
3106 Data(element).isAnimating = true;
3107
3108 /* Once the final element in this call's element set has been processed, push the call array onto
3109 Velocity.State.calls for the animation tick to immediately begin processing. */
3110 if (elementsIndex === elementsLength - 1) {
3111 /* To speed up iterating over this array, it is compacted (falsey items -- calls that have completed -- are removed)
3112 when its length has ballooned to a point that can impact tick performance. This only becomes necessary when animation
3113 has been continuous with many elements over a long period of time; whenever all active calls are completed, completeCall() clears Velocity.State.calls. */
3114 if (Velocity.State.calls.length > 10000) {
3115 Velocity.State.calls = compactSparseArray(Velocity.State.calls);
3116 }
3117
3118 /* Add the current call plus its associated metadata (the element set and the call's options) onto the global call container.
3119 Anything on this call container is subjected to tick() processing. */
3120 Velocity.State.calls.push([ call, elements, opts, null, promiseData.resolver ]);
3121
3122 /* If the animation tick isn't running, start it. (Velocity shuts it off when there are no active calls to process.) */
3123 if (Velocity.State.isTicking === false) {
3124 Velocity.State.isTicking = true;
3125
3126 /* Start the tick loop. */
3127 tick();
3128 }
3129 } else {
3130 elementsIndex++;
3131 }
3132 }
3133 }
3134
3135 /* When the queue option is set to false, the call skips the element's queue and fires immediately. */
3136 if (opts.queue === false) {
3137 /* Since this buildQueue call doesn't respect the element's existing queue (which is where a delay option would have been appended),
3138 we manually inject the delay property here with an explicit setTimeout. */
3139 if (opts.delay) {
3140 setTimeout(buildQueue, opts.delay);
3141 } else {
3142 buildQueue();
3143 }
3144 /* Otherwise, the call undergoes element queueing as normal. */
3145 /* Note: To interoperate with jQuery, Velocity uses jQuery's own $.queue() stack for queuing logic. */
3146 } else {
3147 $.queue(element, opts.queue, function(next, clearQueue) {
3148 /* If the clearQueue flag was passed in by the stop command, resolve this call's promise. (Promises can only be resolved once,
3149 so it's fine if this is repeatedly triggered for each element in the associated call.) */
3150 if (clearQueue === true) {
3151 if (promiseData.promise) {
3152 promiseData.resolver(elements);
3153 }
3154
3155 /* Do not continue with animation queueing. */
3156 return true;
3157 }
3158
3159 /* This flag indicates to the upcoming completeCall() function that this queue entry was initiated by Velocity.
3160 See completeCall() for further details. */
3161 Velocity.velocityQueueEntryFlag = true;
3162
3163 buildQueue(next);
3164 });
3165 }
3166
3167 /*********************
3168 Auto-Dequeuing
3169 *********************/
3170
3171 /* As per jQuery's $.queue() behavior, to fire the first non-custom-queue entry on an element, the element
3172 must be dequeued if its queue stack consists *solely* of the current call. (This can be determined by checking
3173 for the "inprogress" item that jQuery prepends to active queue stack arrays.) Regardless, whenever the element's
3174 queue is further appended with additional items -- including $.delay()'s or even $.animate() calls, the queue's
3175 first entry is automatically fired. This behavior contrasts that of custom queues, which never auto-fire. */
3176 /* Note: When an element set is being subjected to a non-parallel Velocity call, the animation will not begin until
3177 each one of the elements in the set has reached the end of its individually pre-existing queue chain. */
3178 /* Note: Unfortunately, most people don't fully grasp jQuery's powerful, yet quirky, $.queue() function.
3179 Lean more here: http://stackoverflow.com/questions/1058158/can-somebody-explain-jquery-queue-to-me */
3180 if ((opts.queue === "" || opts.queue === "fx") && $.queue(element)[0] !== "inprogress") {
3181 $.dequeue(element);
3182 }
3183 }
3184
3185 /**************************
3186 Element Set Iteration
3187 **************************/
3188
3189 /* If the "nodeType" property exists on the elements variable, we're animating a single element.
3190 Place it in an array so that $.each() can iterate over it. */
3191 $.each(elements, function(i, element) {
3192 /* Ensure each element in a set has a nodeType (is a real element) to avoid throwing errors. */
3193 if (Type.isNode(element)) {
3194 processElement.call(element);
3195 }
3196 });
3197
3198 /******************
3199 Option: Loop
3200 ******************/
3201
3202 /* The loop option accepts an integer indicating how many times the element should loop between the values in the
3203 current call's properties map and the element's property values prior to this call. */
3204 /* Note: The loop option's logic is performed here -- after element processing -- because the current call needs
3205 to undergo its queue insertion prior to the loop option generating its series of constituent "reverse" calls,
3206 which chain after the current call. Two reverse calls (two "alternations") constitute one loop. */
3207 var opts = $.extend({}, Velocity.defaults, options),
3208 reverseCallsCount;
3209
3210 opts.loop = parseInt(opts.loop);
3211 reverseCallsCount = (opts.loop * 2) - 1;
3212
3213 if (opts.loop) {
3214 /* Double the loop count to convert it into its appropriate number of "reverse" calls.
3215 Subtract 1 from the resulting value since the current call is included in the total alternation count. */
3216 for (var x = 0; x < reverseCallsCount; x++) {
3217 /* Since the logic for the reverse action occurs inside Queueing and therefore this call's options object
3218 isn't parsed until then as well, the current call's delay option must be explicitly passed into the reverse
3219 call so that the delay logic that occurs inside *Pre-Queueing* can process it. */
3220 var reverseOptions = {
3221 delay: opts.delay
3222 };
3223
3224 /* If a complete callback was passed into this call, transfer it to the loop sequence's final "reverse" call
3225 so that it's triggered when the entire sequence is complete (and not when the very first animation is complete). */
3226 if (x === reverseCallsCount - 1) {
3227 reverseOptions.display = opts.display;
3228 reverseOptions.visibility = opts.visibility;
3229 reverseOptions.complete = opts.complete;
3230 }
3231
3232 animate(elements, "reverse", reverseOptions);
3233 }
3234 }
3235
3236 /***************
3237 Chaining
3238 ***************/
3239
3240 /* Return the elements back to the call chain, with wrapped elements taking precedence in case Velocity was called via the $.fn. extension. */
3241 return getChain();
3242 };
3243
3244 /* Turn Velocity into the animation function, extended with the pre-existing Velocity object. */
3245 Velocity = $.extend(animate, Velocity);
3246 /* For legacy support, also expose the literal animate method. */
3247 Velocity.animate = animate;
3248
3249 /**************
3250 Timing
3251 **************/
3252
3253 var ticker = window.requestAnimationFrame || rAFShim;
3254
3255 /* Inactive browser tabs pause rAF, which results in all active animations immediately sprinting to their completion states when the tab refocuses.
3256 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
3257 devices to avoid wasting battery power on inactive tabs. */
3258 /* 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. */
3259 if (!Velocity.State.isMobile && document.hidden !== undefined) {
3260 document.addEventListener("visibilitychange", function() {
3261 /* Reassign the rAF function (which the global tick() function uses) based on the tab's focus state. */
3262 if (document.hidden) {
3263 ticker = function(callback) {
3264 /* The tick function needs a truthy first argument in order to pass its internal timestamp check. */
3265 return setTimeout(function() { callback(true) }, 16);
3266 };
3267
3268 /* The rAF loop has been paused by the browser, so we manually restart the tick. */
3269 tick();
3270 } else {
3271 ticker = window.requestAnimationFrame || rAFShim;
3272 }
3273 });
3274 }
3275
3276 /************
3277 Tick
3278 ************/
3279
3280 /* Note: All calls to Velocity are pushed to the Velocity.State.calls array, which is fully iterated through upon each tick. */
3281 function tick (timestamp) {
3282 /* An empty timestamp argument indicates that this is the first tick occurence since ticking was turned on.
3283 We leverage this metadata to fully ignore the first tick pass since RAF's initial pass is fired whenever
3284 the browser's next tick sync time occurs, which results in the first elements subjected to Velocity
3285 calls being animated out of sync with any elements animated immediately thereafter. In short, we ignore
3286 the first RAF tick pass so that elements being immediately consecutively animated -- instead of simultaneously animated
3287 by the same Velocity call -- are properly batched into the same initial RAF tick and consequently remain in sync thereafter. */
3288 if (timestamp) {
3289 /* We ignore RAF's high resolution timestamp since it can be significantly offset when the browser is
3290 under high stress; we opt for choppiness over allowing the browser to drop huge chunks of frames. */
3291 var timeCurrent = (new Date).getTime();
3292
3293 /********************
3294 Call Iteration
3295 ********************/
3296
3297 /* Iterate through each active call. */
3298 for (var i = 0, callsLength = Velocity.State.calls.length; i < callsLength; i++) {
3299 /* When a Velocity call is completed, its Velocity.State.calls entry is set to false. Continue on to the next call. */
3300 if (!Velocity.State.calls[i]) {
3301 continue;
3302 }
3303
3304 /************************
3305 Call-Wide Variables
3306 ************************/
3307
3308 var callContainer = Velocity.State.calls[i],
3309 call = callContainer[0],
3310 opts = callContainer[2],
3311 timeStart = callContainer[3];
3312
3313 /* If timeStart is undefined, then this is the first time that this call has been processed by tick().
3314 We assign timeStart now so that its value is as close to the real animation start time as possible.
3315 (Conversely, had timeStart been defined when this call was added to Velocity.State.calls, the delay
3316 between that time and now would cause the first few frames of the tween to be skipped since
3317 percentComplete is calculated relative to timeStart.) */
3318 /* Further, subtract 16ms (the approximate resolution of RAF) from the current time value so that the
3319 first tick iteration isn't wasted by animating at 0% tween completion, which would produce the
3320 same style value as the element's current value. */
3321 if (!timeStart) {
3322 timeStart = Velocity.State.calls[i][3] = timeCurrent - 16;
3323 }
3324
3325 /* The tween's completion percentage is relative to the tween's start time, not the tween's start value
3326 (which would result in unpredictable tween durations since JavaScript's timers are not particularly accurate).
3327 Accordingly, we ensure that percentComplete does not exceed 1. */
3328 var percentComplete = Math.min((timeCurrent - timeStart) / opts.duration, 1);
3329
3330 /**********************
3331 Element Iteration
3332 **********************/
3333
3334 /* For every call, iterate through each of the elements in its set. */
3335 for (var j = 0, callLength = call.length; j < callLength; j++) {
3336 var tweensContainer = call[j],
3337 element = tweensContainer.element;
3338
3339 /* Check to see if this element has been deleted midway through the animation by checking for the
3340 continued existence of its data cache. If it's gone, skip animating this element. */
3341 if (!Data(element)) {
3342 continue;
3343 }
3344
3345 var transformPropertyExists = false;
3346
3347 /**********************************
3348 Display & Visibility Toggling
3349 **********************************/
3350
3351 /* If the display option is set to non-"none", set it upfront so that the element can become visible before tweening begins.
3352 (Otherwise, display's "none" value is set in completeCall() once the animation has completed.) */
3353 if (opts.display !== undefined && opts.display !== null && opts.display !== "none") {
3354 if (opts.display === "flex") {
3355 var flexValues = [ "-webkit-box", "-moz-box", "-ms-flexbox", "-webkit-flex" ];
3356
3357 $.each(flexValues, function(i, flexValue) {
3358 CSS.setPropertyValue(element, "display", flexValue);
3359 });
3360 }
3361
3362 CSS.setPropertyValue(element, "display", opts.display);
3363 }
3364
3365 /* Same goes with the visibility option, but its "none" equivalent is "hidden". */
3366 if (opts.visibility && opts.visibility !== "hidden") {
3367 CSS.setPropertyValue(element, "visibility", opts.visibility);
3368 }
3369
3370 /************************
3371 Property Iteration
3372 ************************/
3373
3374 /* For every element, iterate through each property. */
3375 for (var property in tweensContainer) {
3376 /* Note: In addition to property tween data, tweensContainer contains a reference to its associated element. */
3377 if (property !== "element") {
3378 var tween = tweensContainer[property],
3379 currentValue,
3380 /* Easing can either be a pre-genereated function or a string that references a pre-registered easing
3381 on the Velocity.Easings object. In either case, return the appropriate easing *function*. */
3382 easing = Type.isString(tween.easing) ? Velocity.Easings[tween.easing] : tween.easing;
3383
3384 /******************************
3385 Current Value Calculation
3386 ******************************/
3387
3388 /* If this is the last tick pass (if we've reached 100% completion for this tween),
3389 ensure that currentValue is explicitly set to its target endValue so that it's not subjected to any rounding. */
3390 if (percentComplete === 1) {
3391 currentValue = tween.endValue;
3392 /* Otherwise, calculate currentValue based on the current delta from startValue. */
3393 } else {
3394 currentValue = tween.startValue + ((tween.endValue - tween.startValue) * easing(percentComplete));
3395 }
3396
3397 tween.currentValue = currentValue;
3398
3399 /******************
3400 Hooks: Part I
3401 ******************/
3402
3403 /* For hooked properties, the newly-updated rootPropertyValueCache is cached onto the element so that it can be used
3404 for subsequent hooks in this call that are associated with the same root property. If we didn't cache the updated
3405 rootPropertyValue, each subsequent update to the root property in this tick pass would reset the previous hook's
3406 updates to rootPropertyValue prior to injection. A nice performance byproduct of rootPropertyValue caching is that
3407 subsequently chained animations using the same hookRoot but a different hook can use this cached rootPropertyValue. */
3408 if (CSS.Hooks.registered[property]) {
3409 var hookRoot = CSS.Hooks.getRoot(property),
3410 rootPropertyValueCache = Data(element).rootPropertyValueCache[hookRoot];
3411
3412 if (rootPropertyValueCache) {
3413 tween.rootPropertyValue = rootPropertyValueCache;
3414 }
3415 }
3416
3417 /*****************
3418 DOM Update
3419 *****************/
3420
3421 /* setPropertyValue() returns an array of the property name and property value post any normalization that may have been performed. */
3422 /* Note: To solve an IE<=8 positioning bug, the unit type is dropped when setting a property value of 0. */
3423 var adjustedSetData = CSS.setPropertyValue(element, /* SET */
3424 property,
3425 tween.currentValue + (parseFloat(currentValue) === 0 ? "" : tween.unitType),
3426 tween.rootPropertyValue,
3427 tween.scrollData);
3428
3429 /*******************
3430 Hooks: Part II
3431 *******************/
3432
3433 /* Now that we have the hook's updated rootPropertyValue (the post-processed value provided by adjustedSetData), cache it onto the element. */
3434 if (CSS.Hooks.registered[property]) {
3435 /* Since adjustedSetData contains normalized data ready for DOM updating, the rootPropertyValue needs to be re-extracted from its normalized form. ?? */
3436 if (CSS.Normalizations.registered[hookRoot]) {
3437 Data(element).rootPropertyValueCache[hookRoot] = CSS.Normalizations.registered[hookRoot]("extract", null, adjustedSetData[1]);
3438 } else {
3439 Data(element).rootPropertyValueCache[hookRoot] = adjustedSetData[1];
3440 }
3441 }
3442
3443 /***************
3444 Transforms
3445 ***************/
3446
3447 /* Flag whether a transform property is being animated so that flushTransformCache() can be triggered once this tick pass is complete. */
3448 if (adjustedSetData[0] === "transform") {
3449 transformPropertyExists = true;
3450 }
3451 }
3452 }
3453
3454 /****************
3455 mobileHA
3456 ****************/
3457
3458 /* If mobileHA is enabled, set the translate3d transform to null to force hardware acceleration.
3459 It's safe to override this property since Velocity doesn't actually support its animation (hooks are used in its place). */
3460 if (opts.mobileHA) {
3461 /* Don't set the null transform hack if we've already done so. */
3462 if (Data(element).transformCache.translate3d === undefined) {
3463 /* All entries on the transformCache object are later concatenated into a single transform string via flushTransformCache(). */
3464 Data(element).transformCache.translate3d = "(0px, 0px, 0px)";
3465
3466 transformPropertyExists = true;
3467 }
3468 }
3469
3470 if (transformPropertyExists) {
3471 CSS.flushTransformCache(element);
3472 }
3473 }
3474
3475 /* The non-"none" display value is only applied to an element once -- when its associated call is first ticked through.
3476 Accordingly, it's set to false so that it isn't re-processed by this call in the next tick. */
3477 if (opts.display !== undefined && opts.display !== "none") {
3478 Velocity.State.calls[i][2].display = false;
3479 }
3480 if (opts.visibility && opts.visibility !== "hidden") {
3481 Velocity.State.calls[i][2].visibility = false;
3482 }
3483
3484 /* Pass the elements and the timing data (percentComplete, msRemaining, and timeStart) into the progress callback. */
3485 if (opts.progress) {
3486 opts.progress.call(callContainer[1],
3487 callContainer[1],
3488 percentComplete,
3489 Math.max(0, (timeStart + opts.duration) - timeCurrent),
3490 timeStart);
3491 }
3492
3493 /* If this call has finished tweening, pass its index to completeCall() to handle call cleanup. */
3494 if (percentComplete === 1) {
3495 completeCall(i);
3496 }
3497 }
3498 }
3499
3500 /* Note: completeCall() sets the isTicking flag to false when the last call on Velocity.State.calls has completed. */
3501 if (Velocity.State.isTicking) {
3502 ticker(tick);
3503 }
3504 }
3505
3506 /**********************
3507 Call Completion
3508 **********************/
3509
3510 /* Note: Unlike tick(), which processes all active calls at once, call completion is handled on a per-call basis. */
3511 function completeCall (callIndex, isStopped) {
3512 /* Ensure the call exists. */
3513 if (!Velocity.State.calls[callIndex]) {
3514 return false;
3515 }
3516
3517 /* Pull the metadata from the call. */
3518 var call = Velocity.State.calls[callIndex][0],
3519 elements = Velocity.State.calls[callIndex][1],
3520 opts = Velocity.State.calls[callIndex][2],
3521 resolver = Velocity.State.calls[callIndex][4];
3522
3523 var remainingCallsExist = false;
3524
3525 /*************************
3526 Element Finalization
3527 *************************/
3528
3529 for (var i = 0, callLength = call.length; i < callLength; i++) {
3530 var element = call[i].element;
3531
3532 /* If the user set display to "none" (intending to hide the element), set it now that the animation has completed. */
3533 /* Note: display:none isn't set when calls are manually stopped (via Velocity("stop"). */
3534 /* Note: Display gets ignored with "reverse" calls and infinite loops, since this behavior would be undesirable. */
3535 if (!isStopped && !opts.loop) {
3536 if (opts.display === "none") {
3537 CSS.setPropertyValue(element, "display", opts.display);
3538 }
3539
3540 if (opts.visibility === "hidden") {
3541 CSS.setPropertyValue(element, "visibility", opts.visibility);
3542 }
3543 }
3544
3545 /* If the element's queue is empty (if only the "inprogress" item is left at position 0) or if its queue is about to run
3546 a non-Velocity-initiated entry, turn off the isAnimating flag. A non-Velocity-initiatied queue entry's logic might alter
3547 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,
3548 we check for the existence of our special Velocity.queueEntryFlag declaration, which minifiers won't rename since the flag
3549 is assigned to jQuery's global $ object and thus exists out of Velocity's own scope. */
3550 if ($.queue(element)[1] === undefined || !/\.velocityQueueEntryFlag/i.test($.queue(element)[1])) {
3551 /* The element may have been deleted. Ensure that its data cache still exists before acting on it. */
3552 if (Data(element)) {
3553 Data(element).isAnimating = false;
3554 /* Clear the element's rootPropertyValueCache, which will become stale. */
3555 Data(element).rootPropertyValueCache = {};
3556
3557 var transformHAPropertyExists = false;
3558 /* If any 3D transform subproperty is at its default value (regardless of unit type), remove it. */
3559 $.each(CSS.Lists.transforms3D, function(i, transformName) {
3560 var defaultValue = /^scale/.test(transformName) ? 1 : 0,
3561 currentValue = Data(element).transformCache[transformName];
3562
3563 if (Data(element).transformCache[transformName] !== undefined && new RegExp("^\\(" + defaultValue + "[^.]").test(currentValue)) {
3564 transformHAPropertyExists = true;
3565
3566 delete Data(element).transformCache[transformName];
3567 }
3568 });
3569
3570 /* Mobile devices have hardware acceleration removed at the end of the animation in order to avoid hogging the GPU's memory. */
3571 if (opts.mobileHA) {
3572 transformHAPropertyExists = true;
3573 delete Data(element).transformCache.translate3d;
3574 }
3575
3576 /* Flush the subproperty removals to the DOM. */
3577 if (transformHAPropertyExists) {
3578 CSS.flushTransformCache(element);
3579 }
3580
3581 /* Remove the "velocity-animating" indicator class. */
3582 CSS.Values.removeClass(element, "velocity-animating");
3583 }
3584 }
3585
3586 /*********************
3587 Option: Complete
3588 *********************/
3589
3590 /* 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. */
3591 /* Note: Callbacks aren't fired when calls are manually stopped (via Velocity("stop"). */
3592 if (!isStopped && opts.complete && !opts.loop && (i === callLength - 1)) {
3593 /* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Velocity itself. */
3594 try {
3595 opts.complete.call(elements, elements);
3596 } catch (error) {
3597 setTimeout(function() { throw error; }, 1);
3598 }
3599 }
3600
3601 /**********************
3602 Promise Resolving
3603 **********************/
3604
3605 /* Note: Infinite loops don't return promises. */
3606 if (resolver && opts.loop !== true) {
3607 resolver(elements);
3608 }
3609
3610 /****************************
3611 Option: Loop (Infinite)
3612 ****************************/
3613
3614 if (opts.loop === true && !isStopped) {
3615 /* If a rotateX/Y/Z property is being animated to 360 deg with loop:true, swap tween start/end values to enable
3616 continuous iterative rotation looping. (Otherise, the element would just rotate back and forth.) */
3617 $.each(Data(element).tweensContainer, function(propertyName, tweenContainer) {
3618 if (/^rotate/.test(propertyName) && parseFloat(tweenContainer.endValue) === 360) {
3619 tweenContainer.endValue = 0;
3620 tweenContainer.startValue = 360;
3621 }
3622 });
3623
3624 Velocity(element, "reverse", { loop: true, delay: opts.delay });
3625 }
3626
3627 /***************
3628 Dequeueing
3629 ***************/
3630
3631 /* Fire the next call in the queue so long as this call's queue wasn't set to false (to trigger a parallel animation),
3632 which would have already caused the next call to fire. Note: Even if the end of the animation queue has been reached,
3633 $.dequeue() must still be called in order to completely clear jQuery's animation queue. */
3634 if (opts.queue !== false) {
3635 $.dequeue(element, opts.queue);
3636 }
3637 }
3638
3639 /************************
3640 Calls Array Cleanup
3641 ************************/
3642
3643 /* Since this call is complete, set it to false so that the rAF tick skips it. This array is later compacted via compactSparseArray().
3644 (For performance reasons, the call is set to false instead of being deleted from the array: http://www.html5rocks.com/en/tutorials/speed/v8/) */
3645 Velocity.State.calls[callIndex] = false;
3646
3647 /* Iterate through the calls array to determine if this was the final in-progress animation.
3648 If so, set a flag to end ticking and clear the calls array. */
3649 for (var j = 0, callsLength = Velocity.State.calls.length; j < callsLength; j++) {
3650 if (Velocity.State.calls[j] !== false) {
3651 remainingCallsExist = true;
3652
3653 break;
3654 }
3655 }
3656
3657 if (remainingCallsExist === false) {
3658 /* tick() will detect this flag upon its next iteration and subsequently turn itself off. */
3659 Velocity.State.isTicking = false;
3660
3661 /* Clear the calls array so that its length is reset. */
3662 delete Velocity.State.calls;
3663 Velocity.State.calls = [];
3664 }
3665 }
3666
3667 /******************
3668 Frameworks
3669 ******************/
3670
3671 /* Both jQuery and Zepto allow their $.fn object to be extended to allow wrapped elements to be subjected to plugin calls.
3672 If either framework is loaded, register a "velocity" extension pointing to Velocity's core animate() method. Velocity
3673 also registers itself onto a global container (window.jQuery || window.Zepto || window) so that certain features are
3674 accessible beyond just a per-element scope. This master object contains an .animate() method, which is later assigned to $.fn
3675 (if jQuery or Zepto are present). Accordingly, Velocity can both act on wrapped DOM elements and stand alone for targeting raw DOM elements. */
3676 global.Velocity = Velocity;
3677
3678 if (global !== window) {
3679 /* Assign the element function to Velocity's core animate() method. */
3680 global.fn.velocity = animate;
3681 /* Assign the object function's defaults to Velocity's global defaults object. */
3682 global.fn.velocity.defaults = Velocity.defaults;
3683 }
3684
3685 /***********************
3686 Packaged Sequences
3687 ***********************/
3688
3689 /* slideUp, slideDown */
3690 $.each([ "Down", "Up" ], function(i, direction) {
3691 Velocity.Sequences["slide" + direction] = function (element, options, elementsIndex, elementsSize, elements, promiseData) {
3692 var opts = $.extend({}, options),
3693 begin = opts.begin,
3694 complete = opts.complete,
3695 computedValues = { height: "", marginTop: "", marginBottom: "", paddingTop: "", paddingBottom: "" },
3696 inlineValues = {};
3697
3698 if (opts.display === undefined) {
3699 /* Show the element before slideDown begins and hide the element after slideUp completes. */
3700 /* Note: Inline elements cannot have dimensions animated, so they're reverted to inline-block. */
3701 opts.display = (direction === "Down" ? (Velocity.CSS.Values.getDisplayType(element) === "inline" ? "inline-block" : "block") : "none");
3702 }
3703
3704 opts.begin = function () {
3705 /* If the user passed in a begin callback, fire it now. */
3706 begin && begin.call(elements, elements);
3707
3708 /* Force vertical overflow content to clip so that sliding works as expected. */
3709 inlineValues.overflow = element.style.overflow;
3710 element.style.overflow = "hidden";
3711
3712 /* Cache the elements' original vertical dimensional property values so that we can animate back to them. */
3713 for (var property in computedValues) {
3714 /* Cache all inline values, we reset to upon animation completion. */
3715 inlineValues[property] = element.style[property];
3716
3717 /* For slideDown, use forcefeeding to animate all vertical properties from 0. For slideUp,
3718 use forcefeeding to start from computed values and animate down to 0. */
3719 var propertyValue = Velocity.CSS.getPropertyValue(element, property);
3720 computedValues[property] = (direction === "Down") ? [ propertyValue, 0 ] : [ 0, propertyValue ];
3721 }
3722 }
3723
3724 opts.complete = function () {
3725 /* Reset element to its pre-slide inline values once its slide animation is complete. */
3726 for (var property in inlineValues) {
3727 element.style[property] = inlineValues[property];
3728 }
3729
3730 /* If the user passed in a complete callback, fire it now. */
3731 complete && complete.call(elements, elements);
3732 promiseData && promiseData.resolver(elements);
3733 };
3734
3735 Velocity(element, computedValues, opts);
3736 };
3737 });
3738
3739 /* fadeIn, fadeOut */
3740 $.each([ "In", "Out" ], function(i, direction) {
3741 Velocity.Sequences["fade" + direction] = function (element, options, elementsIndex, elementsSize, elements, promiseData) {
3742 var opts = $.extend({}, options),
3743 propertiesMap = { opacity: (direction === "In") ? 1 : 0 },
3744 originalComplete = opts.complete;
3745
3746 /* Since sequences are triggered individually for each element in the animated set, avoid repeatedly triggering
3747 callbacks by firing them only when the final element has been reached. */
3748 if (elementsIndex !== elementsSize - 1) {
3749 opts.complete = opts.begin = null;
3750 } else {
3751 opts.complete = function() {
3752 if (originalComplete) {
3753 originalComplete.call(elements, elements);
3754 }
3755
3756 promiseData && promiseData.resolver(elements);
3757 }
3758 }
3759
3760 /* If a display was passed in, use it. Otherwise, default to "none" for fadeOut or the element-specific default for fadeIn. */
3761 /* Note: We allow users to pass in "null" to skip display setting altogether. */
3762 if (opts.display === undefined) {
3763 opts.display = (direction === "In" ? "auto" : "none");
3764 }
3765
3766 Velocity(this, propertiesMap, opts);
3767 };
3768 });
3769
3770 return Velocity;
3771}((window.jQuery || window.Zepto || window), window, document);
3772}));
3773
3774/******************
3775 Known Issues
3776******************/
3777
3778/* When animating height/width to a % value on an element *without* box-sizing:border-box and *with* visible scrollbars
3779on *both* axes, the opposite axis (e.g. height vs width) will be shortened by the height/width of its scrollbar. */
3780/* The CSS spec mandates that the translateX/Y/Z transforms are %-relative to the element itself -- not its parent.
3781Velocity, however, doesn't make this distinction. Thus, converting to or from the % unit with these subproperties
3782will produce an inaccurate conversion value. The same issue exists with the cx/cy attributes of SVG circles and ellipses. */
\No newline at end of file