UNPKG

react-native-web-headroom

Version:
1,580 lines (1,366 loc) 202 kB
/*! VelocityJS.org (1.5.0). (C) 2014 Julian Shapiro. MIT @license: en.wikipedia.org/wiki/MIT_License */ /************************* Velocity jQuery Shim *************************/ /*! VelocityJS.org jQuery Shim (1.0.1). (C) 2014 The jQuery Foundation. MIT @license: en.wikipedia.org/wiki/MIT_License. */ /* This file contains the jQuery functions that Velocity relies on, thereby removing Velocity's dependency on a full copy of jQuery, and allowing it to work in any environment. */ /* These shimmed functions are only used if jQuery isn't present. If both this shim and jQuery are loaded, Velocity defaults to jQuery proper. */ /* Browser support: Using this shim instead of jQuery proper removes support for IE8. */ (function(window) { "use strict"; /*************** Setup ***************/ /* If jQuery is already loaded, there's no point in loading this shim. */ if (window.jQuery) { return; } /* jQuery base. */ var $ = function(selector, context) { return new $.fn.init(selector, context); }; /******************** Private Methods ********************/ /* jQuery */ $.isWindow = function(obj) { /* jshint eqeqeq: false */ return obj && obj === obj.window; }; /* jQuery */ $.type = function(obj) { if (!obj) { return obj + ""; } return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj; }; /* jQuery */ $.isArray = Array.isArray || function(obj) { return $.type(obj) === "array"; }; /* jQuery */ function isArraylike(obj) { var length = obj.length, type = $.type(obj); if (type === "function" || $.isWindow(obj)) { return false; } if (obj.nodeType === 1 && length) { return true; } return type === "array" || length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj; } /*************** $ Methods ***************/ /* jQuery: Support removed for IE<9. */ $.isPlainObject = function(obj) { var key; if (!obj || $.type(obj) !== "object" || obj.nodeType || $.isWindow(obj)) { return false; } try { if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { return false; } } catch (e) { return false; } for (key in obj) { } return key === undefined || hasOwn.call(obj, key); }; /* jQuery */ $.each = function(obj, callback, args) { var value, i = 0, length = obj.length, isArray = isArraylike(obj); if (args) { if (isArray) { for (; i < length; i++) { value = callback.apply(obj[i], args); if (value === false) { break; } } } else { for (i in obj) { if (!obj.hasOwnProperty(i)) { continue; } value = callback.apply(obj[i], args); if (value === false) { break; } } } } else { if (isArray) { for (; i < length; i++) { value = callback.call(obj[i], i, obj[i]); if (value === false) { break; } } } else { for (i in obj) { if (!obj.hasOwnProperty(i)) { continue; } value = callback.call(obj[i], i, obj[i]); if (value === false) { break; } } } } return obj; }; /* Custom */ $.data = function(node, key, value) { /* $.getData() */ if (value === undefined) { var getId = node[$.expando], store = getId && cache[getId]; if (key === undefined) { return store; } else if (store) { if (key in store) { return store[key]; } } /* $.setData() */ } else if (key !== undefined) { var setId = node[$.expando] || (node[$.expando] = ++$.uuid); cache[setId] = cache[setId] || {}; cache[setId][key] = value; return value; } }; /* Custom */ $.removeData = function(node, keys) { var id = node[$.expando], store = id && cache[id]; if (store) { // Cleanup the entire store if no keys are provided. if (!keys) { delete cache[id]; } else { $.each(keys, function(_, key) { delete store[key]; }); } } }; /* jQuery */ $.extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; if (typeof target === "boolean") { deep = target; target = arguments[i] || {}; i++; } if (typeof target !== "object" && $.type(target) !== "function") { target = {}; } if (i === length) { target = this; i--; } for (; i < length; i++) { if ((options = arguments[i])) { for (name in options) { if (!options.hasOwnProperty(name)) { continue; } src = target[name]; copy = options[name]; if (target === copy) { continue; } if (deep && copy && ($.isPlainObject(copy) || (copyIsArray = $.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && $.isArray(src) ? src : []; } else { clone = src && $.isPlainObject(src) ? src : {}; } target[name] = $.extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; } } } } return target; }; /* jQuery 1.4.3 */ $.queue = function(elem, type, data) { function $makeArray(arr, results) { var ret = results || []; if (arr) { if (isArraylike(Object(arr))) { /* $.merge */ (function(first, second) { var len = +second.length, j = 0, i = first.length; while (j < len) { first[i++] = second[j++]; } if (len !== len) { while (second[j] !== undefined) { first[i++] = second[j++]; } } first.length = i; return first; })(ret, typeof arr === "string" ? [arr] : arr); } else { [].push.call(ret, arr); } } return ret; } if (!elem) { return; } type = (type || "fx") + "queue"; var q = $.data(elem, type); if (!data) { return q || []; } if (!q || $.isArray(data)) { q = $.data(elem, type, $makeArray(data)); } else { q.push(data); } return q; }; /* jQuery 1.4.3 */ $.dequeue = function(elems, type) { /* Custom: Embed element iteration. */ $.each(elems.nodeType ? [elems] : elems, function(i, elem) { type = type || "fx"; var queue = $.queue(elem, type), fn = queue.shift(); if (fn === "inprogress") { fn = queue.shift(); } if (fn) { if (type === "fx") { queue.unshift("inprogress"); } fn.call(elem, function() { $.dequeue(elem, type); }); } }); }; /****************** $.fn Methods ******************/ /* jQuery */ $.fn = $.prototype = { init: function(selector) { /* Just return the element wrapped inside an array; don't proceed with the actual jQuery node wrapping process. */ if (selector.nodeType) { this[0] = selector; return this; } else { throw new Error("Not a DOM node."); } }, offset: function() { /* jQuery altered code: Dropped disconnected DOM node checking. */ var box = this[0].getBoundingClientRect ? this[0].getBoundingClientRect() : {top: 0, left: 0}; return { top: box.top + (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || 0), left: box.left + (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || 0) }; }, position: function() { /* jQuery */ function offsetParentFn(elem) { var offsetParent = elem.offsetParent; while (offsetParent && (offsetParent.nodeName.toLowerCase() !== "html" && offsetParent.style && offsetParent.style.position.toLowerCase() === "static")) { offsetParent = offsetParent.offsetParent; } return offsetParent || document; } /* Zepto */ var elem = this[0], offsetParent = offsetParentFn(elem), offset = this.offset(), parentOffset = /^(?:body|html)$/i.test(offsetParent.nodeName) ? {top: 0, left: 0} : $(offsetParent).offset(); offset.top -= parseFloat(elem.style.marginTop) || 0; offset.left -= parseFloat(elem.style.marginLeft) || 0; if (offsetParent.style) { parentOffset.top += parseFloat(offsetParent.style.borderTopWidth) || 0; parentOffset.left += parseFloat(offsetParent.style.borderLeftWidth) || 0; } return { top: offset.top - parentOffset.top, left: offset.left - parentOffset.left }; } }; /********************** Private Variables **********************/ /* For $.data() */ var cache = {}; $.expando = "velocity" + (new Date().getTime()); $.uuid = 0; /* For $.queue() */ var class2type = {}, hasOwn = class2type.hasOwnProperty, toString = class2type.toString; var types = "Boolean Number String Function Array Date RegExp Object Error".split(" "); for (var i = 0; i < types.length; i++) { class2type["[object " + types[i] + "]"] = types[i].toLowerCase(); } /* Makes $(node) possible, without having to call init. */ $.fn.init.prototype = $.fn; /* Globalize Velocity onto the window, and assign its Utilities property. */ window.Velocity = {Utilities: $}; })(window); /****************** Velocity.js ******************/ (function(factory) { "use strict"; /* CommonJS module. */ if (typeof module === "object" && typeof module.exports === "object") { module.exports = factory(); /* AMD module. */ } else if (typeof define === "function" && define.amd) { define(factory); /* Browser globals. */ } else { factory(); } }(function() { "use strict"; return function(global, window, document, undefined) { /*************** Summary ***************/ /* - CSS: CSS stack that works independently from the rest of Velocity. - animate(): Core animation method that iterates over the targeted elements and queues the incoming call onto each element individually. - Pre-Queueing: Prepare the element for animation by instantiating its data cache and processing the call's options. - Queueing: The logic that runs once the call has reached its point of execution in the element's $.queue() stack. Most logic is placed here to avoid risking it becoming stale (if the element's properties have changed). - Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container. - tick(): The single requestAnimationFrame loop responsible for tweening all in-progress calls. - completeCall(): Handles the cleanup process for each Velocity call. */ /********************* Helper Functions *********************/ /* IE detection. Gist: https://gist.github.com/julianshapiro/9098609 */ var IE = (function() { if (document.documentMode) { return document.documentMode; } else { for (var i = 7; i > 4; i--) { var div = document.createElement("div"); div.innerHTML = "<!--[if IE " + i + "]><span></span><![endif]-->"; if (div.getElementsByTagName("span").length) { div = null; return i; } } } return undefined; })(); /* rAF shim. Gist: https://gist.github.com/julianshapiro/9497513 */ var rAFShim = (function() { var timeLast = 0; return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { var timeCurrent = (new Date()).getTime(), timeDelta; /* Dynamically set delay on a per-tick basis to match 60fps. */ /* Technique by Erik Moller. MIT license: https://gist.github.com/paulirish/1579671 */ timeDelta = Math.max(0, 16 - (timeCurrent - timeLast)); timeLast = timeCurrent + timeDelta; return setTimeout(function() { callback(timeCurrent + timeDelta); }, timeDelta); }; })(); var performance = (function() { var perf = window.performance || {}; if (typeof perf.now !== "function") { var nowOffset = perf.timing && perf.timing.navigationStart ? perf.timing.navigationStart : (new Date()).getTime(); perf.now = function() { return (new Date()).getTime() - nowOffset; }; } return perf; })(); /* Array compacting. Copyright Lo-Dash. MIT License: https://github.com/lodash/lodash/blob/master/LICENSE.txt */ function compactSparseArray(array) { var index = -1, length = array ? array.length : 0, result = []; while (++index < length) { var value = array[index]; if (value) { result.push(value); } } return result; } /** * Shim for "fixing" IE's lack of support (IE < 9) for applying slice * on host objects like NamedNodeMap, NodeList, and HTMLCollection * (technically, since host objects have been implementation-dependent, * at least before ES2015, IE hasn't needed to work this way). * Also works on strings, fixes IE < 9 to allow an explicit undefined * for the 2nd argument (as in Firefox), and prevents errors when * called on other DOM objects. */ var _slice = (function() { var slice = Array.prototype.slice; try { // Can't be used with DOM elements in IE < 9 slice.call(document.documentElement); return slice; } catch (e) { // Fails in IE < 9 // This will work for genuine arrays, array-like objects, // NamedNodeMap (attributes, entities, notations), // NodeList (e.g., getElementsByTagName), HTMLCollection (e.g., childNodes), // and will not fail on other DOM objects (as do DOM elements in IE < 9) return function(begin, end) { var len = this.length; if (typeof begin !== "number") { begin = 0; } // IE < 9 gets unhappy with an undefined end argument if (typeof end !== "number") { end = len; } // For native Array objects, we use the native slice function if (this.slice) { return slice.call(this, begin, end); } // For array like object we handle it ourselves. var i, cloned = [], // Handle negative value for "begin" start = (begin >= 0) ? begin : Math.max(0, len + begin), // Handle negative value for "end" upTo = end < 0 ? len + end : Math.min(end, len), // Actual expected size of the slice size = upTo - start; if (size > 0) { cloned = new Array(size); if (this.charAt) { for (i = 0; i < size; i++) { cloned[i] = this.charAt(start + i); } } else { for (i = 0; i < size; i++) { cloned[i] = this[start + i]; } } } return cloned; }; } })(); /* .indexOf doesn't exist in IE<9 */ var _inArray = (function() { if (Array.prototype.includes) { return function(arr, val) { return arr.includes(val); }; } if (Array.prototype.indexOf) { return function(arr, val) { return arr.indexOf(val) >= 0; }; } return function(arr, val) { for (var i = 0; i < arr.length; i++) { if (arr[i] === val) { return true; } } return false; }; }); function sanitizeElements(elements) { /* Unwrap jQuery/Zepto objects. */ if (Type.isWrapped(elements)) { elements = _slice.call(elements); /* Wrap a single element in an array so that $.each() can iterate with the element instead of its node's children. */ } else if (Type.isNode(elements)) { elements = [elements]; } return elements; } var Type = { isNumber: function(variable) { return (typeof variable === "number"); }, isString: function(variable) { return (typeof variable === "string"); }, isArray: Array.isArray || function(variable) { return Object.prototype.toString.call(variable) === "[object Array]"; }, isFunction: function(variable) { return Object.prototype.toString.call(variable) === "[object Function]"; }, isNode: function(variable) { return variable && variable.nodeType; }, /* Determine if variable is an array-like wrapped jQuery, Zepto or similar element, or even a NodeList etc. */ /* NOTE: HTMLFormElements also have a length. */ isWrapped: function(variable) { return variable && variable !== window && Type.isNumber(variable.length) && !Type.isString(variable) && !Type.isFunction(variable) && !Type.isNode(variable) && (variable.length === 0 || Type.isNode(variable[0])); }, isSVG: function(variable) { return window.SVGElement && (variable instanceof window.SVGElement); }, isEmptyObject: function(variable) { for (var name in variable) { if (variable.hasOwnProperty(name)) { return false; } } return true; } }; /***************** Dependencies *****************/ var $, isJQuery = false; if (global.fn && global.fn.jquery) { $ = global; isJQuery = true; } else { $ = window.Velocity.Utilities; } if (IE <= 8 && !isJQuery) { throw new Error("Velocity: IE8 and below require jQuery to be loaded before Velocity."); } else if (IE <= 7) { /* Revert to jQuery's $.animate(), and lose Velocity's extra features. */ jQuery.fn.velocity = jQuery.fn.animate; /* Now that $.fn.velocity is aliased, abort this Velocity declaration. */ return; } /***************** Constants *****************/ var DURATION_DEFAULT = 400, EASING_DEFAULT = "swing"; /************* State *************/ var Velocity = { /* Container for page-wide Velocity state data. */ State: { /* Detect mobile devices to determine if mobileHA should be turned on. */ isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(window.navigator.userAgent), /* The mobileHA option's behavior changes on older Android devices (Gingerbread, versions 2.3.3-2.3.7). */ isAndroid: /Android/i.test(window.navigator.userAgent), isGingerbread: /Android 2\.3\.[3-7]/i.test(window.navigator.userAgent), isChrome: window.chrome, isFirefox: /Firefox/i.test(window.navigator.userAgent), /* Create a cached element for re-use when checking for CSS property prefixes. */ prefixElement: document.createElement("div"), /* Cache every prefix match to avoid repeating lookups. */ prefixMatches: {}, /* Cache the anchor used for animating window scrolling. */ scrollAnchor: null, /* Cache the browser-specific property names associated with the scroll anchor. */ scrollPropertyLeft: null, scrollPropertyTop: null, /* Keep track of whether our RAF tick is running. */ isTicking: false, /* Container for every in-progress call to Velocity. */ calls: [], delayedElements: { count: 0 } }, /* Velocity's custom CSS stack. Made global for unit testing. */ CSS: {/* Defined below. */}, /* A shim of the jQuery utility functions used by Velocity -- provided by Velocity's optional jQuery shim. */ Utilities: $, /* Container for the user's custom animation redirects that are referenced by name in place of the properties map argument. */ Redirects: {/* Manually registered by the user. */}, Easings: {/* Defined below. */}, /* Attempt to use ES6 Promises by default. Users can override this with a third-party promises library. */ Promise: window.Promise, /* Velocity option defaults, which can be overriden by the user. */ defaults: { queue: "", duration: DURATION_DEFAULT, easing: EASING_DEFAULT, begin: undefined, complete: undefined, progress: undefined, display: undefined, visibility: undefined, loop: false, delay: false, mobileHA: true, /* Advanced: Set to false to prevent property values from being cached between consecutive Velocity-initiated chain calls. */ _cacheValues: true, /* Advanced: Set to false if the promise should always resolve on empty element lists. */ promiseRejectEmpty: true }, /* A design goal of Velocity is to cache data wherever possible in order to avoid DOM requerying. Accordingly, each element has a data cache. */ init: function(element) { $.data(element, "velocity", { /* Store whether this is an SVG element, since its properties are retrieved and updated differently than standard HTML elements. */ isSVG: Type.isSVG(element), /* Keep track of whether the element is currently being animated by Velocity. This is used to ensure that property values are not transferred between non-consecutive (stale) calls. */ isAnimating: false, /* A reference to the element's live computedStyle object. Learn more here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */ computedStyle: null, /* Tween data is cached for each animation on the element so that data can be passed across calls -- in particular, end values are used as subsequent start values in consecutive Velocity calls. */ tweensContainer: null, /* The full root property values of each CSS hook being animated on this element are cached so that: 1) Concurrently-animating hooks sharing the same root can have their root values' merged into one while tweening. 2) Post-hook-injection root values can be transferred over to consecutively chained Velocity calls as starting root values. */ rootPropertyValueCache: {}, /* A cache for transform updates, which must be manually flushed via CSS.flushTransformCache(). */ transformCache: {} }); }, /* A parallel to jQuery's $.css(), used for getting/setting Velocity's hooked CSS properties. */ hook: null, /* Defined below. */ /* Velocity-wide animation time remapping for testing purposes. */ mock: false, version: {major: 1, minor: 5, patch: 1}, /* Set to 1 or 2 (most verbose) to output debug info to console. */ debug: false, /* Use rAF high resolution timestamp when available */ timestamp: true, /* Pause all animations */ pauseAll: function(queueName) { var currentTime = (new Date()).getTime(); $.each(Velocity.State.calls, function(i, activeCall) { if (activeCall) { /* If we have a queueName and this call is not on that queue, skip */ if (queueName !== undefined && ((activeCall[2].queue !== queueName) || (activeCall[2].queue === false))) { return true; } /* Set call to paused */ activeCall[5] = { resume: false }; } }); /* Pause timers on any currently delayed calls */ $.each(Velocity.State.delayedElements, function(k, element) { if (!element) { return; } pauseDelayOnElement(element, currentTime); }); }, /* Resume all animations */ resumeAll: function(queueName) { var currentTime = (new Date()).getTime(); $.each(Velocity.State.calls, function(i, activeCall) { if (activeCall) { /* If we have a queueName and this call is not on that queue, skip */ if (queueName !== undefined && ((activeCall[2].queue !== queueName) || (activeCall[2].queue === false))) { return true; } /* Set call to resumed if it was paused */ if (activeCall[5]) { activeCall[5].resume = true; } } }); /* Resume timers on any currently delayed calls */ $.each(Velocity.State.delayedElements, function(k, element) { if (!element) { return; } resumeDelayOnElement(element, currentTime); }); } }; /* Retrieve the appropriate scroll anchor and property name for the browser: https://developer.mozilla.org/en-US/docs/Web/API/Window.scrollY */ if (window.pageYOffset !== undefined) { Velocity.State.scrollAnchor = window; Velocity.State.scrollPropertyLeft = "pageXOffset"; Velocity.State.scrollPropertyTop = "pageYOffset"; } else { Velocity.State.scrollAnchor = document.documentElement || document.body.parentNode || document.body; Velocity.State.scrollPropertyLeft = "scrollLeft"; Velocity.State.scrollPropertyTop = "scrollTop"; } /* Shorthand alias for jQuery's $.data() utility. */ function Data(element) { /* Hardcode a reference to the plugin name. */ var response = $.data(element, "velocity"); /* jQuery <=1.4.2 returns null instead of undefined when no match is found. We normalize this behavior. */ return response === null ? undefined : response; } /************** Delay Timer **************/ function pauseDelayOnElement(element, currentTime) { /* Check for any delay timers, and pause the set timeouts (while preserving time data) to be resumed when the "resume" command is issued */ var data = Data(element); if (data && data.delayTimer && !data.delayPaused) { data.delayRemaining = data.delay - currentTime + data.delayBegin; data.delayPaused = true; clearTimeout(data.delayTimer.setTimeout); } } function resumeDelayOnElement(element, currentTime) { /* Check for any paused timers and resume */ var data = Data(element); if (data && data.delayTimer && data.delayPaused) { /* If the element was mid-delay, re initiate the timeout with the remaining delay */ data.delayPaused = false; data.delayTimer.setTimeout = setTimeout(data.delayTimer.next, data.delayRemaining); } } /************** Easing **************/ /* Step easing generator. */ function generateStep(steps) { return function(p) { return Math.round(p * steps) * (1 / steps); }; } /* Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */ function generateBezier(mX1, mY1, mX2, mY2) { var NEWTON_ITERATIONS = 4, NEWTON_MIN_SLOPE = 0.001, SUBDIVISION_PRECISION = 0.0000001, SUBDIVISION_MAX_ITERATIONS = 10, kSplineTableSize = 11, kSampleStepSize = 1.0 / (kSplineTableSize - 1.0), float32ArraySupported = "Float32Array" in window; /* Must contain four arguments. */ if (arguments.length !== 4) { return false; } /* Arguments must be numbers. */ for (var i = 0; i < 4; ++i) { if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) { return false; } } /* X values must be in the [0, 1] range. */ mX1 = Math.min(mX1, 1); mX2 = Math.min(mX2, 1); mX1 = Math.max(mX1, 0); mX2 = Math.max(mX2, 0); var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } function C(aA1) { return 3.0 * aA1; } function calcBezier(aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } function getSlope(aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } function newtonRaphsonIterate(aX, aGuessT) { for (var i = 0; i < NEWTON_ITERATIONS; ++i) { var currentSlope = getSlope(aGuessT, mX1, mX2); if (currentSlope === 0.0) { return aGuessT; } var currentX = calcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; } function calcSampleValues() { for (var i = 0; i < kSplineTableSize; ++i) { mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); } } function binarySubdivide(aX, aA, aB) { var currentX, currentT, i = 0; do { currentT = aA + (aB - aA) / 2.0; currentX = calcBezier(currentT, mX1, mX2) - aX; if (currentX > 0.0) { aB = currentT; } else { aA = currentT; } } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); return currentT; } function getTForX(aX) { var intervalStart = 0.0, currentSample = 1, lastSample = kSplineTableSize - 1; for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) { intervalStart += kSampleStepSize; } --currentSample; var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]), guessForT = intervalStart + dist * kSampleStepSize, initialSlope = getSlope(guessForT, mX1, mX2); if (initialSlope >= NEWTON_MIN_SLOPE) { return newtonRaphsonIterate(aX, guessForT); } else if (initialSlope === 0.0) { return guessForT; } else { return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize); } } var _precomputed = false; function precompute() { _precomputed = true; if (mX1 !== mY1 || mX2 !== mY2) { calcSampleValues(); } } var f = function(aX) { if (!_precomputed) { precompute(); } if (mX1 === mY1 && mX2 === mY2) { return aX; } if (aX === 0) { return 0; } if (aX === 1) { return 1; } return calcBezier(getTForX(aX), mY1, mY2); }; f.getControlPoints = function() { return [{x: mX1, y: mY1}, {x: mX2, y: mY2}]; }; var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")"; f.toString = function() { return str; }; return f; } /* Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */ /* 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 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */ var generateSpringRK4 = (function() { function springAccelerationForState(state) { return (-state.tension * state.x) - (state.friction * state.v); } function springEvaluateStateWithDerivative(initialState, dt, derivative) { var state = { x: initialState.x + derivative.dx * dt, v: initialState.v + derivative.dv * dt, tension: initialState.tension, friction: initialState.friction }; return {dx: state.v, dv: springAccelerationForState(state)}; } function springIntegrateState(state, dt) { var a = { dx: state.v, dv: springAccelerationForState(state) }, b = springEvaluateStateWithDerivative(state, dt * 0.5, a), c = springEvaluateStateWithDerivative(state, dt * 0.5, b), d = springEvaluateStateWithDerivative(state, dt, c), dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx), dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv); state.x = state.x + dxdt * dt; state.v = state.v + dvdt * dt; return state; } return function springRK4Factory(tension, friction, duration) { var initState = { x: -1, v: 0, tension: null, friction: null }, path = [0], time_lapsed = 0, tolerance = 1 / 10000, DT = 16 / 1000, have_duration, dt, last_state; tension = parseFloat(tension) || 500; friction = parseFloat(friction) || 20; duration = duration || null; initState.tension = tension; initState.friction = friction; have_duration = duration !== null; /* Calculate the actual time it takes for this animation to complete with the provided conditions. */ if (have_duration) { /* Run the simulation without a duration. */ time_lapsed = springRK4Factory(tension, friction); /* Compute the adjusted time delta. */ dt = time_lapsed / duration * DT; } else { dt = DT; } while (true) { /* Next/step function .*/ last_state = springIntegrateState(last_state || initState, dt); /* Store the position. */ path.push(1 + last_state.x); time_lapsed += 16; /* If the change threshold is reached, break. */ if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) { break; } } /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the computed path and returns a snapshot of the position according to a given percentComplete. */ return !have_duration ? time_lapsed : function(percentComplete) { return path[ (percentComplete * (path.length - 1)) | 0 ]; }; }; }()); /* jQuery easings. */ Velocity.Easings = { linear: function(p) { return p; }, swing: function(p) { return 0.5 - Math.cos(p * Math.PI) / 2; }, /* Bonus "spring" easing, which is a less exaggerated version of easeInOutElastic. */ spring: function(p) { return 1 - (Math.cos(p * 4.5 * Math.PI) * Math.exp(-p * 6)); } }; /* CSS3 and Robert Penner easings. */ $.each( [ ["ease", [0.25, 0.1, 0.25, 1.0]], ["ease-in", [0.42, 0.0, 1.00, 1.0]], ["ease-out", [0.00, 0.0, 0.58, 1.0]], ["ease-in-out", [0.42, 0.0, 0.58, 1.0]], ["easeInSine", [0.47, 0, 0.745, 0.715]], ["easeOutSine", [0.39, 0.575, 0.565, 1]], ["easeInOutSine", [0.445, 0.05, 0.55, 0.95]], ["easeInQuad", [0.55, 0.085, 0.68, 0.53]], ["easeOutQuad", [0.25, 0.46, 0.45, 0.94]], ["easeInOutQuad", [0.455, 0.03, 0.515, 0.955]], ["easeInCubic", [0.55, 0.055, 0.675, 0.19]], ["easeOutCubic", [0.215, 0.61, 0.355, 1]], ["easeInOutCubic", [0.645, 0.045, 0.355, 1]], ["easeInQuart", [0.895, 0.03, 0.685, 0.22]], ["easeOutQuart", [0.165, 0.84, 0.44, 1]], ["easeInOutQuart", [0.77, 0, 0.175, 1]], ["easeInQuint", [0.755, 0.05, 0.855, 0.06]], ["easeOutQuint", [0.23, 1, 0.32, 1]], ["easeInOutQuint", [0.86, 0, 0.07, 1]], ["easeInExpo", [0.95, 0.05, 0.795, 0.035]], ["easeOutExpo", [0.19, 1, 0.22, 1]], ["easeInOutExpo", [1, 0, 0, 1]], ["easeInCirc", [0.6, 0.04, 0.98, 0.335]], ["easeOutCirc", [0.075, 0.82, 0.165, 1]], ["easeInOutCirc", [0.785, 0.135, 0.15, 0.86]] ], function(i, easingArray) { Velocity.Easings[easingArray[0]] = generateBezier.apply(null, easingArray[1]); }); /* Determine the appropriate easing type given an easing input. */ function getEasing(value, duration) { var easing = value; /* The easing option can either be a string that references a pre-registered easing, or it can be a two-/four-item array of integers to be converted into a bezier/spring function. */ if (Type.isString(value)) { /* Ensure that the easing has been assigned to jQuery's Velocity.Easings object. */ if (!Velocity.Easings[value]) { easing = false; } } else if (Type.isArray(value) && value.length === 1) { easing = generateStep.apply(null, value); } else if (Type.isArray(value) && value.length === 2) { /* springRK4 must be passed the animation's duration. */ /* Note: If the springRK4 array contains non-numbers, generateSpringRK4() returns an easing function generated with default tension and friction values. */ easing = generateSpringRK4.apply(null, value.concat([duration])); } else if (Type.isArray(value) && value.length === 4) { /* Note: If the bezier array contains non-numbers, generateBezier() returns false. */ easing = generateBezier.apply(null, value); } else { easing = false; } /* Revert to the Velocity-wide default easing type, or fall back to "swing" (which is also jQuery's default) if the Velocity-wide default has been incorrectly modified. */ if (easing === false) { if (Velocity.Easings[Velocity.defaults.easing]) { easing = Velocity.defaults.easing; } else { easing = EASING_DEFAULT; } } return easing; } /***************** CSS Stack *****************/ /* The CSS object is a highly condensed and performant CSS stack that fully replaces jQuery's. It handles the validation, getting, and setting of both standard CSS properties and CSS property hooks. */ /* Note: A "CSS" shorthand is aliased so that our code is easier to read. */ var CSS = Velocity.CSS = { /************* RegEx *************/ RegEx: { isHex: /^#([A-f\d]{3}){1,2}$/i, /* Unwrap a property value's surrounding text, e.g. "rgba(4, 3, 2, 1)" ==> "4, 3, 2, 1" and "rect(4px 3px 2px 1px)" ==> "4px 3px 2px 1px". */ valueUnwrap: /^[A-z]+\((.*)\)$/i, wrappedValueAlreadyExtracted: /[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/, /* Split a multi-value property into an array of subvalues, e.g. "rgba(4, 3, 2, 1) 4px 3px 2px 1px" ==> [ "rgba(4, 3, 2, 1)", "4px", "3px", "2px", "1px" ]. */ valueSplit: /([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/ig }, /************ Lists ************/ Lists: { colors: ["fill", "stroke", "stopColor", "color", "backgroundColor", "borderColor", "borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor", "outlineColor"], transformsBase: ["translateX", "translateY", "scale", "scaleX", "scaleY", "skewX", "skewY", "rotateZ"], transforms3D: ["transformPerspective", "translateZ", "scaleZ", "rotateX", "rotateY"], units: [ "%", // relative "em", "ex", "ch", "rem", // font relative "vw", "vh", "vmin", "vmax", // viewport relative "cm", "mm", "Q", "in", "pc", "pt", "px", // absolute lengths "deg", "grad", "rad", "turn", // angles "s", "ms" // time ], colorNames: { "aliceblue": "240,248,255", "antiquewhite": "250,235,215", "aquamarine": "127,255,212", "aqua": "0,255,255", "azure": "240,255,255", "beige": "245,245,220", "bisque": "255,228,196", "black": "0,0,0", "blanchedalmond": "255,235,205", "blueviolet": "138,43,226", "blue": "0,0,255", "brown": "165,42,42", "burlywood": "222,184,135", "cadetblue": "95,158,160", "chartreuse": "127,255,0", "chocolate": "210,105,30", "coral": "255,127,80", "cornflowerblue": "100,149,237", "cornsilk": "255,248,220", "crimson": "220,20,60", "cyan": "0,255,255", "darkblue": "0,0,139", "darkcyan": "0,139,139", "darkgoldenrod": "184,134,11", "darkgray": "169,169,169", "darkgrey": "169,169,169", "darkgreen": "0,100,0", "darkkhaki": "189,183,107", "darkmagenta": "139,0,139", "darkolivegreen": "85,107,47", "darkorange": "255,140,0", "darkorchid": "153,50,204", "darkred": "139,0,0", "darksalmon": "233,150,122", "darkseagreen": "143,188,143", "darkslateblue": "72,61,139", "darkslategray": "47,79,79", "darkturquoise": "0,206,209", "darkviolet": "148,0,211", "deeppink": "255,20,147", "deepskyblue": "0,191,255", "dimgray": "105,105,105", "dimgrey": "105,105,105", "dodgerblue": "30,144,255", "firebrick": "178,34,34", "floralwhite": "255,250,240", "forestgreen": "34,139,34", "fuchsia": "255,0,255", "gainsboro": "220,220,220", "ghostwhite": "248,248,255", "gold": "255,215,0", "goldenrod": "218,165,32", "gray": "128,128,128", "grey": "128,128,128", "greenyellow": "173,255,47", "green": "0,128,0", "honeydew": "240,255,240", "hotpink": "255,105,180", "indianred": "205,92,92", "indigo": "75,0,130", "ivory": "255,255,240", "khaki": "240,230,140", "lavenderblush": "255,240,245", "lavender": "230,230,250", "lawngreen": "124,252,0", "lemonchiffon": "255,250,205", "lightblue": "173,216,230", "lightcoral": "240,128,128", "lightcyan": "224,255,255", "lightgoldenrodyellow": "250,250,210", "lightgray": "211,211,211", "lightgrey": "211,211,211", "lightgreen": "144,238,144", "lightpink": "255,182,193", "lightsalmon": "255,160,122", "lightseagreen": "32,178,170", "lightskyblue": "135,206,250", "lightslategray": "119,136,153", "lightsteelblue": "176,196,222", "lightyellow": "255,255,224", "limegreen": "50,205,50", "lime": "0,255,0", "linen": "250,240,230", "magenta": "255,0,255", "maroon": "128,0,0", "mediumaquamarine": "102,205,170", "mediumblue": "0,0,205", "mediumorchid": "186,85,211", "mediumpurple": "147,112,219", "mediumseagreen": "60,179,113", "mediumslateblue": "123,104,238", "mediumspringgreen": "0,250,154", "mediumturquoise": "72,209,204", "mediumvioletred": "199,21,133", "midnightblue": "25,25,112", "mintcream": "245,255,250", "mistyrose": "255,228,225", "moccasin": "255,228,181", "navajowhite": "255,222,173", "navy": "0,0,128", "oldlace": "253,245,230", "olivedrab": "107,142,35", "olive": "128,128,0", "orangered": "255,69,0", "orange": "255,165,0", "orchid": "218,112,214", "palegoldenrod": "238,232,170", "palegreen": "152,251,152", "paleturquoise": "175,238,238", "palevioletred": "219,112,147", "papayawhip": "255,239,213", "peachpuff": "255,218,185", "peru": "205,133,63", "pink": "255,192,203", "plum": "221,160,221", "powderblue": "176,224,230", "purple": "128,0,128", "red": "255,0,0", "rosybrown": "188,143,143", "royalblue": "65,105,225", "saddlebrown": "139,69,19", "salmon": "250,128,114", "sandybrown": "244,164,96", "seagreen": "46,139,87", "seashell": "255,245,238", "sienna": "160,82,45", "silver": "192,192,192", "skyblue": "135,206,235", "slateblue": "106,90,205", "slategray": "112,128,144", "snow": "255,250,250", "springgreen": "0,255,127", "steelblue": "70,130,180", "tan": "210,180,140", "teal": "0,128,128", "thistle": "216,191,216", "tomato": "255,99,71", "turquoise": "64,224,208", "violet": "238,130,238", "wheat": "245,222,179", "whitesmoke": "245,245,245", "white": "255,255,255", "yellowgreen": "154,205,50", "yellow": "255,255,0" } }, /************ Hooks ************/ /* Hooks allow a subproperty (e.g. "boxShadowBlur") of a compound-value CSS property (e.g. "boxShadow: X Y Blur Spread Color") to be animated as if it were a discrete property. */ /* Note: Beyond enabling fine-grained property animation, hooking is necessary since Velocity only tweens properties with single numeric values; unlike CSS transitions, Velocity does not interpolate compound-values. */ Hooks: { /******************** Registration ********************/ /* Templates are a concise way of indicating which subproperties must be individually registered for each compound-value CSS property. */ /* Each template consists of the compound-value's base name, its constituent subproperty names, and those subproperties' default values. */ templates: { "textShadow": ["Color X Y Blur", "black 0px 0px 0px"], "boxShadow": ["Color X Y Blur Spread", "black 0px 0px 0px 0px"], "clip": ["Top Right Bottom Left", "0px 0px 0px 0px"], "backgroundPosition": ["X Y", "0% 0%"], "transformOrigin": ["X Y Z", "50% 50% 0px"], "perspectiveOrigin": ["X Y", "50% 50%"] }, /* A "registered" hook is one that has been converted from its template form into a live, tweenable property. It contains data to associate it with its root property. */ registered: { /* Note: A registered hook looks like this ==> textShadowBlur: [ "textShadow", 3 ], which consists of the subproperty's name, the associated root property's name, and the subproperty's position in the root's value. */ }, /* Convert the templates into individual hooks then append them to the registered object above. */ register: function() { /* Color hooks registration: Colors are defaulted to white -- as opposed to black -- since colors that are currently set to "transparent" default to their respective template below when color-animated, and white is typically a closer match to transparent than black is. An exception is made for text ("color"), which is almost always set closer to black than white. */ for (var i = 0; i < CSS.Lists.colors.length; i++) { var rgbComponents = (CSS.Lists.colors[i] === "color") ? "0 0 0 1" : "255 255 255 1"; CSS.Hooks.templates[CSS.Lists.colors[i]] = ["Red Green Blue Alpha", rgbComponents]; } var rootProperty, hookTemplate, hookNames; /* In IE, color values inside compound-value properties are positioned at the end the value instead of at the beginning. Thus, we re-arrange the templates accordingly. */ if (IE) { for (rootProperty in CSS.Hooks.templates) { if (!CSS.Hooks.templates.hasOwnProperty(rootProperty)) { continue; } hookTemplate = CSS.Hooks.templates[rootProperty]; hookNames = hookTemplate[0].split(" "); var defaultValues = hookTemplate[1].match(CSS.RegEx.valueSplit); if (hookNames[0] === "Color") { /* Reposition both the hook's name and its default value to the end of their respective strings. */ hookNames.push(hookNames.shift()); defaultValues.push(defaultValues.shift()); /* Replace the existing template for the hook's root property. */ CSS.Hooks.templates[rootProperty] = [hookNames.join(" "), defaultValues.join(" ")]; } } } /* Hook registration. */ for (rootProperty in CSS.Hooks.templates) { if (!CSS.Hooks.templates.hasOwnProperty(rootProperty)) { continue; } hookTemplate = CSS.Hooks.templates[rootProperty]; hookNames = hookTemplate[0].split(" "); for (var j in hookNames) { if (!hookNames.hasOwnProperty(j)) { continue; } var fullHookName = rootProperty + hookNames[j], hookPosition = j; /* For each hook, register its full name (e.g. textShadowBlur) with its root property (e.g. textShadow) and the hook's position in its template's default value string. */ CSS.Hooks.registered[fullHookName] = [rootProperty, hookPosition]; } } }, /***************************** Injection and Extraction *****************************/ /* Look up the root property associated with the hook (e.g. return "textShadow" for "textShadowBlur"). */ /* Since a hook cannot be set directly (the browser won't recognize it), style updating for hooks is routed through the hook's root property. */ getRoot: function(property) { var hookData = CSS.Hooks.registered[property]; if (hookData) { return hookData[0]; } else { /* If there was no hook match, return the property name untouched. */ return property; } }, getUnit: function(str, start) { var unit = (str.substr(start || 0, 5).match(/^[a-z%]+/) || [])[0] || ""; if (unit && _inArray(CSS.Lists.units, unit)) { return unit; } return ""; }, fixColors: function(str) { return str.replace(/(rgba?\(\s*)?(\b[a-z]+\b)/g, function($0, $1, $2) { if (CSS.Lists.colorNames.hasOwnProperty($2)) { return ($1 ? $1 : "rgba(") + CSS.Lists.colorNames[$2] + ($1 ? "" : ",1)"); } return $1 + $2; }); }, /* Convert any rootPropertyValue, null or otherwise, into a space-delimited list of hook values so that the targeted hook can be injected or extracted at its standard position. */ cleanRootPropertyValue: function(rootProperty, rootPropertyValue) { /* If the rootPropertyValue is wrapped with "rgb()", "clip()", etc., remove the wrapping to normalize the value before manipulation. */ if (CSS.RegEx.valueUnwrap.test(rootPropertyValue)) { rootPropertyValue = rootPropertyValue.match(CSS.RegEx.valueUnwrap)[1]; } /* If rootPropertyValue is a CSS null-value (from which there's inherently no hook value to extract), default to the root's default value as defined in CSS.Hooks.templates. */ /* Note: CSS null-values include "none", "auto", and "transparent". They must be converted into their zero-values (e.g. textShadow: "none" ==> textShadow: "0px 0px 0px black") for hook manipulation to proceed. */ if (CSS.Values.isCSSNullValue(rootPropertyValue)) { rootPropertyValue = CSS.Hooks.templates[rootProperty][1]; } return rootPropertyValue; }, /* Extracted the hook's value from its root property's value. This is used to get the starting value of an animating hook. */ extractValue: function(fullHookName, rootPropertyValue) { var hookData = CSS.Hooks.registered[fullHookName]; if (hookData) { var hookRoot = hookData[0], hookPosition = hookData[1]; rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue); /* Split rootPropertyValue into its constituent hook values then grab the desired hook at its standard position. */ return rootPropertyValue.toString().match(CSS.RegEx.valueSplit)[hookPosition]; } else { /* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */ return rootPropertyValue; } }, /* Inject the hook's value into its root property's value. This is used to piece back together the root property once Velocity has updated one of its individually hooked values through tweening. */ injectValue: function(fullHookName, hookValue, rootPropertyValue) { var hookData = CSS.Hooks.registered[fullHookName]; if (hookData) { var hookRoot = hookData[0], hookPosition = hookData[1], rootPropertyValueParts, rootPropertyValueUpdated; rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue); /* Split rootPropertyValue into its individual hook values, replace the targeted value with hookValue, then reconstruct the rootPropertyValue string. */ rootPropertyValueParts = rootPropertyValue.toString().match(CSS.RegEx.valueSplit); rootPropertyValueParts[hookPosition] = hookValue; rootPropertyValueUpdated = rootPropertyValueParts.join(" "); return rootPropertyValueUpdated; } else { /* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that wa