UNPKG

vue-parent-change-transition

Version:
855 lines (819 loc) 162 kB
/*! * VERSION: 1.20.5 * DATE: 2018-05-21 * UPDATES AND DOCS AT: http://greensock.com * * @license Copyright (c) 2008-2018, GreenSock. All rights reserved. * This work is subject to the terms at http://greensock.com/standard-license or for * Club GreenSock members, the software agreement that was issued with your membership. * * @author: Jack Doyle, jack@greensock.com */ var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node (_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { "use strict"; _gsScope._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) { /** @constructor **/ var CSSPlugin = function() { TweenPlugin.call(this, "css"); this._overwriteProps.length = 0; this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method) }, _globals = _gsScope._gsDefine.globals, _hasPriority, //turns true whenever a CSSPropTween instance is created that has a priority other than 0. This helps us discern whether or not we should spend the time organizing the linked list or not after a CSSPlugin's _onInitTween() method is called. _suffixMap, //we set this in _onInitTween() each time as a way to have a persistent variable we can use in other methods like _parse() without having to pass it around as a parameter and we keep _parse() decoupled from a particular CSSPlugin instance _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter _overwriteProps, //alias to the currently instantiating CSSPlugin's _overwriteProps array. We use this closure in order to avoid having to pass a reference around from method to method and aid in minification. _specialProps = {}, p = CSSPlugin.prototype = new TweenPlugin("css"); p.constructor = CSSPlugin; CSSPlugin.version = "1.20.5"; CSSPlugin.API = 2; CSSPlugin.defaultTransformPerspective = 0; CSSPlugin.defaultSkewType = "compensated"; CSSPlugin.defaultSmoothOrigin = true; p = "px"; //we'll reuse the "p" variable to keep file size down CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""}; var _numExp = /(?:\-|\.|\b)(\d|\.|e\-)+/g, _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g, _valuesExp = /(?:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi, //finds all the values that begin with numbers or += or -= and then a number. Includes suffixes. We use this to split complex values apart like "1px 5px 20px rgb(255,102,51)" _NaNExp = /(?![+-]?\d*\.?\d+|[+-]|e[+-]\d+)[^0-9]/g, //also allows scientific notation and doesn't kill the leading -/+ in -= and += _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g, _opacityExp = /opacity *= *([^)]*)/i, _opacityValExp = /opacity:([^;]*)/i, _alphaFilterExp = /alpha\(opacity *=.+?\)/i, _rgbhslExp = /^(rgb|hsl)/, _capsExp = /([A-Z])/g, _camelExp = /-([a-z])/gi, _urlExp = /(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi, //for pulling out urls from url(...) or url("...") strings (some browsers wrap urls in quotes, some don't when reporting things like backgroundImage) _camelFunc = function(s, g) { return g.toUpperCase(); }, _horizExp = /(?:Left|Right|Width)/i, _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi, _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i, _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis _complexExp = /[\s,\(]/i, //for testing a string to find if it has a space, comma, or open parenthesis (clues that it's a complex value) _DEG2RAD = Math.PI / 180, _RAD2DEG = 180 / Math.PI, _forcePT = {}, _dummyElement = {style:{}}, _doc = _gsScope.document || {createElement: function() {return _dummyElement;}}, _createElement = function(type, ns) { return _doc.createElementNS ? _doc.createElementNS(ns || "http://www.w3.org/1999/xhtml", type) : _doc.createElement(type); }, _tempDiv = _createElement("div"), _tempImg = _createElement("img"), _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins _agent = (_gsScope.navigator || {}).userAgent || "", _autoRound, _reqSafariFix, //we won't apply the Safari transform fix until we actually come across a tween that affects a transform property (to maintain best performance). _isSafari, _isFirefox, //Firefox has a bug that causes 3D transformed elements to randomly disappear unless a repaint is forced after each update on each element. _isSafariLT6, //Safari (and Android 4 which uses a flavor of Safari) has a bug that prevents changes to "top" and "left" properties from rendering properly if changed on the same frame as a transform UNLESS we set the element's WebkitBackfaceVisibility to hidden (weird, I know). Doing this for Android 3 and earlier seems to actually cause other problems, though (fun!) _ieVers, _supportsOpacity = (function() { //we set _isSafari, _ieVers, _isFirefox, and _supportsOpacity all in one function here to reduce file size slightly, especially in the minified version. var i = _agent.indexOf("Android"), a = _createElement("a"); _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || parseFloat(_agent.substr(i+8, 2)) > 3)); _isSafariLT6 = (_isSafari && (parseFloat(_agent.substr(_agent.indexOf("Version/")+8, 2)) < 6)); _isFirefox = (_agent.indexOf("Firefox") !== -1); if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent) || (/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) { _ieVers = parseFloat( RegExp.$1 ); } if (!a) { return false; } a.style.cssText = "top:1px;opacity:.55;"; return /^0.55/.test(a.style.opacity); }()), _getIEOpacity = function(v) { return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1); }, _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE. if (_gsScope.console) { console.log(s); } }, _target, //when initting a CSSPlugin, we set this variable so that we can access it from within many other functions without having to pass it around as params _index, //when initting a CSSPlugin, we set this variable so that we can access it from within many other functions without having to pass it around as params _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-" _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz". // @private feed in a camelCase property name like "transform" and it will check to see if it is valid as-is or if it needs a vendor prefix. It returns the corrected camelCase property name (i.e. "WebkitTransform" or "MozTransform" or "transform" or null if no such property is found, like if the browser is IE8 or before, "transform" won't be found at all) _checkPropPrefix = function(p, e) { e = e || _tempDiv; var s = e.style, a, i; if (s[p] !== undefined) { return p; } p = p.charAt(0).toUpperCase() + p.substr(1); a = ["O","Moz","ms","Ms","Webkit"]; i = 5; while (--i > -1 && s[a[i]+p] === undefined) { } if (i >= 0) { _prefix = (i === 3) ? "ms" : a[i]; _prefixCSS = "-" + _prefix.toLowerCase() + "-"; return _prefix + p; } return null; }, _getComputedStyle = (typeof(window) !== "undefined" ? window : _doc.defaultView || {getComputedStyle:function() {}}).getComputedStyle, /** * @private Returns the css style for a particular property of an element. For example, to get whatever the current "left" css value for an element with an ID of "myElement", you could do: * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left"); * * @param {!Object} t Target element whose style property you want to query * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.) * @param {Object=} cs Computed style object. This just provides a way to speed processing if you're going to get several properties on the same element in quick succession - you can reuse the result of the getComputedStyle() call. * @param {boolean=} calc If true, the value will not be read directly from the element's "style" property (if it exists there), but instead the getComputedStyle() result will be used. This can be useful when you want to ensure that the browser itself is interpreting the value. * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto". * @return {?string} The current property value */ _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) { var rv; if (!_supportsOpacity) if (p === "opacity") { //several versions of IE don't use the standard "opacity" property - they use things like filter:alpha(opacity=50), so we parse that here. return _getIEOpacity(t); } if (!calc && t.style[p]) { rv = t.style[p]; } else if ((cs = cs || _getComputedStyle(t))) { rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase()); } else if (t.currentStyle) { rv = t.currentStyle[p]; } return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv; }, /** * @private Pass the target element, the property name, the numeric value, and the suffix (like "%", "em", "px", etc.) and it will spit back the equivalent pixel number. * @param {!Object} t Target element * @param {!string} p Property name (like "left", "top", "marginLeft", etc.) * @param {!number} v Value * @param {string=} sfx Suffix (like "px" or "%" or "em") * @param {boolean=} recurse If true, the call is a recursive one. In some browsers (like IE7/8), occasionally the value isn't accurately reported initially, but if we run the function again it will take effect. * @return {number} value in pixels */ _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) { if (sfx === "px" || (!sfx && p !== "lineHeight")) { return v; } if (sfx === "auto" || !v) { return 0; } var horiz = _horizExp.test(p), node = t, style = _tempDiv.style, neg = (v < 0), precise = (v === 1), pix, cache, time; if (neg) { v = -v; } if (precise) { v *= 100; } if (p === "lineHeight" && !sfx) { //special case of when a simple lineHeight (without a unit) is used. Set it to the value, read back the computed value, and then revert. cache = _getComputedStyle(t).lineHeight; t.style.lineHeight = v; pix = parseFloat(_getComputedStyle(t).lineHeight); t.style.lineHeight = cache; } else if (sfx === "%" && p.indexOf("border") !== -1) { pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight); } else { style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;"; if (sfx === "%" || !node.appendChild || sfx.charAt(0) === "v" || sfx === "rem") { node = t.parentNode || _doc.body; if (_getStyle(node, "display").indexOf("flex") !== -1) { //Edge and IE11 have a bug that causes offsetWidth to report as 0 if the container has display:flex and the child is position:relative. Switching to position: absolute solves it. style.position = "absolute"; } cache = node._gsCache; time = TweenLite.ticker.frame; if (cache && horiz && cache.time === time) { //performance optimization: we record the width of elements along with the ticker frame so that we can quickly get it again on the same tick (seems relatively safe to assume it wouldn't change on the same tick) return cache.width * v / 100; } style[(horiz ? "width" : "height")] = v + sfx; } else { style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx; } node.appendChild(_tempDiv); pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]); node.removeChild(_tempDiv); if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) { cache = node._gsCache = node._gsCache || {}; cache.time = time; cache.width = pix / v * 100; } if (pix === 0 && !recurse) { pix = _convertToPixels(t, p, v, sfx, true); } } if (precise) { pix /= 100; } return neg ? -pix : pix; }, _calculateOffset = _internals.calculateOffset = function(t, p, cs) { //for figuring out "top" or "left" in px when it's "auto". We need to factor in margin with the offsetLeft/offsetTop if (_getStyle(t, "position", cs) !== "absolute") { return 0; } var dim = ((p === "left") ? "Left" : "Top"), v = _getStyle(t, "margin" + dim, cs); return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0); }, // @private returns at object containing ALL of the style properties in camelCase and their associated values. _getAllStyles = function(t, cs) { var s = {}, i, tr, p; if ((cs = cs || _getComputedStyle(t, null))) { if ((i = cs.length)) { while (--i > -1) { p = cs[i]; if (p.indexOf("-transform") === -1 || _transformPropCSS === p) { //Some webkit browsers duplicate transform values, one non-prefixed and one prefixed ("transform" and "WebkitTransform"), so we must weed out the extra one here. s[p.replace(_camelExp, _camelFunc)] = cs.getPropertyValue(p); } } } else { //some browsers behave differently - cs.length is always 0, so we must do a for...in loop. for (i in cs) { if (i.indexOf("Transform") === -1 || _transformProp === i) { //Some webkit browsers duplicate transform values, one non-prefixed and one prefixed ("transform" and "WebkitTransform"), so we must weed out the extra one here. s[i] = cs[i]; } } } } else if ((cs = t.currentStyle || t.style)) { for (i in cs) { if (typeof(i) === "string" && s[i] === undefined) { s[i.replace(_camelExp, _camelFunc)] = cs[i]; } } } if (!_supportsOpacity) { s.opacity = _getIEOpacity(t); } tr = _getTransform(t, cs, false); s.rotation = tr.rotation; s.skewX = tr.skewX; s.scaleX = tr.scaleX; s.scaleY = tr.scaleY; s.x = tr.x; s.y = tr.y; if (_supports3D) { s.z = tr.z; s.rotationX = tr.rotationX; s.rotationY = tr.rotationY; s.scaleZ = tr.scaleZ; } if (s.filters) { delete s.filters; } return s; }, // @private analyzes two style objects (as returned by _getAllStyles()) and only looks for differences between them that contain tweenable values (like a number or color). It returns an object with a "difs" property which refers to an object containing only those isolated properties and values for tweening, and a "firstMPT" property which refers to the first MiniPropTween instance in a linked list that recorded all the starting values of the different properties so that we can revert to them at the end or beginning of the tween - we don't want the cascading to get messed up. The forceLookup parameter is an optional generic object with properties that should be forced into the results - this is necessary for className tweens that are overwriting others because imagine a scenario where a rollover/rollout adds/removes a class and the user swipes the mouse over the target SUPER fast, thus nothing actually changed yet and the subsequent comparison of the properties would indicate they match (especially when px rounding is taken into consideration), thus no tweening is necessary even though it SHOULD tween and remove those properties after the tween (otherwise the inline styles will contaminate things). See the className SpecialProp code for details. _cssDif = function(t, s1, s2, vars, forceLookup) { var difs = {}, style = t.style, val, p, mpt; for (p in s2) { if (p !== "cssText") if (p !== "length") if (isNaN(p)) if (s1[p] !== (val = s2[p]) || (forceLookup && forceLookup[p])) if (p.indexOf("Origin") === -1) if (typeof(val) === "number" || typeof(val) === "string") { difs[p] = (val === "auto" && (p === "left" || p === "top")) ? _calculateOffset(t, p) : ((val === "" || val === "auto" || val === "none") && typeof(s1[p]) === "string" && s1[p].replace(_NaNExp, "") !== "") ? 0 : val; //if the ending value is defaulting ("" or "auto"), we check the starting value and if it can be parsed into a number (a string which could have a suffix too, like 700px), then we swap in 0 for "" or "auto" so that things actually tween. if (style[p] !== undefined) { //for className tweens, we must remember which properties already existed inline - the ones that didn't should be removed when the tween isn't in progress because they were only introduced to facilitate the transition between classes. mpt = new MiniPropTween(style, p, style[p], mpt); } } } if (vars) { for (p in vars) { //copy properties (except className) if (p !== "className") { difs[p] = vars[p]; } } } return {difs:difs, firstMPT:mpt}; }, _dimensions = {width:["Left","Right"], height:["Top","Bottom"]}, _margins = ["marginLeft","marginRight","marginTop","marginBottom"], /** * @private Gets the width or height of an element * @param {!Object} t Target element * @param {!string} p Property name ("width" or "height") * @param {Object=} cs Computed style object (if one exists). Just a speed optimization. * @return {number} Dimension (in pixels) */ _getDimension = function(t, p, cs) { if ((t.nodeName + "").toLowerCase() === "svg") { //Chrome no longer supports offsetWidth/offsetHeight on SVG elements. return (cs || _getComputedStyle(t))[p] || 0; } else if (t.getCTM && _isSVG(t)) { return t.getBBox()[p] || 0; } var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight), a = _dimensions[p], i = a.length; cs = cs || _getComputedStyle(t, null); while (--i > -1) { v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0; v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0; } return v; }, // @private Parses position-related complex strings like "top left" or "50px 10px" or "70% 20%", etc. which are used for things like transformOrigin or backgroundPosition. Optionally decorates a supplied object (recObj) with the following properties: "ox" (offsetX), "oy" (offsetY), "oxp" (if true, "ox" is a percentage not a pixel value), and "oxy" (if true, "oy" is a percentage not a pixel value) _parsePosition = function(v, recObj) { if (v === "contain" || v === "auto" || v === "auto auto") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto". return v + " "; } if (v == null || v === "") { v = "0 0"; } var a = v.split(" "), x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0], y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1], i; if (a.length > 3 && !recObj) { //multiple positions a = v.split(", ").join(",").split(","); v = []; for (i = 0; i < a.length; i++) { v.push(_parsePosition(a[i])); } return v.join(","); } if (y == null) { y = (x === "center") ? "50%" : "0"; } else if (y === "center") { y = "50%"; } if (x === "center" || (isNaN(parseFloat(x)) && (x + "").indexOf("=") === -1)) { //remember, the user could flip-flop the values and say "bottom center" or "center bottom", etc. "center" is ambiguous because it could be used to describe horizontal or vertical, hence the isNaN(). If there's an "=" sign in the value, it's relative. x = "50%"; } v = x + " " + y + ((a.length > 2) ? " " + a[2] : ""); if (recObj) { recObj.oxp = (x.indexOf("%") !== -1); recObj.oyp = (y.indexOf("%") !== -1); recObj.oxr = (x.charAt(1) === "="); recObj.oyr = (y.charAt(1) === "="); recObj.ox = parseFloat(x.replace(_NaNExp, "")); recObj.oy = parseFloat(y.replace(_NaNExp, "")); recObj.v = v; } return recObj || v; }, /** * @private Takes an ending value (typically a string, but can be a number) and a starting value and returns the change between the two, looking for relative value indicators like += and -= and it also ignores suffixes (but make sure the ending value starts with a number or +=/-= and that the starting value is a NUMBER!) * @param {(number|string)} e End value which is typically a string, but could be a number * @param {(number|string)} b Beginning value which is typically a string but could be a number * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized) */ _parseChange = function(e, b) { if (typeof(e) === "function") { e = e(_index, _target); } return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : (parseFloat(e) - parseFloat(b)) || 0; }, /** * @private Takes a value and a default number, checks if the value is relative, null, or numeric and spits back a normalized number accordingly. Primarily used in the _parseTransform() function. * @param {Object} v Value to be parsed * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter) * @return {number} Parsed value */ _parseVal = function(v, d) { if (typeof(v) === "function") { v = v(_index, _target); } return (v == null) ? d : (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * parseFloat(v.substr(2)) + d : parseFloat(v) || 0; }, /** * @private Translates strings like "40deg" or "40" or 40rad" or "+=40deg" or "270_short" or "-90_cw" or "+=45_ccw" to a numeric radian angle. Of course a starting/default value must be fed in too so that relative values can be calculated properly. * @param {Object} v Value to be parsed * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter) * @param {string=} p property name for directionalEnd (optional - only used when the parsed value is directional ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation). Property name would be "rotation", "rotationX", or "rotationY" * @param {Object=} directionalEnd An object that will store the raw end values for directional angles ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation. * @return {number} parsed angle in radians */ _parseAngle = function(v, d, p, directionalEnd) { var min = 0.000001, cap, split, dif, result, isRelative; if (typeof(v) === "function") { v = v(_index, _target); } if (v == null) { result = d; } else if (typeof(v) === "number") { result = v; } else { cap = 360; split = v.split("_"); isRelative = (v.charAt(1) === "="); dif = (isRelative ? parseInt(v.charAt(0) + "1", 10) * parseFloat(split[0].substr(2)) : parseFloat(split[0])) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - (isRelative ? 0 : d); if (split.length) { if (directionalEnd) { directionalEnd[p] = d + dif; } if (v.indexOf("short") !== -1) { dif = dif % cap; if (dif !== dif % (cap / 2)) { dif = (dif < 0) ? dif + cap : dif - cap; } } if (v.indexOf("_cw") !== -1 && dif < 0) { dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; } else if (v.indexOf("ccw") !== -1 && dif > 0) { dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; } } result = d + dif; } if (result < min && result > -min) { result = 0; } return result; }, _colorLookup = {aqua:[0,255,255], lime:[0,255,0], silver:[192,192,192], black:[0,0,0], maroon:[128,0,0], teal:[0,128,128], blue:[0,0,255], navy:[0,0,128], white:[255,255,255], fuchsia:[255,0,255], olive:[128,128,0], yellow:[255,255,0], orange:[255,165,0], gray:[128,128,128], purple:[128,0,128], green:[0,128,0], red:[255,0,0], pink:[255,192,203], cyan:[0,255,255], transparent:[255,255,255,0]}, _hue = function(h, m1, m2) { h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h; return ((((h * 6 < 1) ? m1 + (m2 - m1) * h * 6 : (h < 0.5) ? m2 : (h * 3 < 2) ? m1 + (m2 - m1) * (2 / 3 - h) * 6 : m1) * 255) + 0.5) | 0; }, /** * @private Parses a color (like #9F0, #FF9900, rgb(255,51,153) or hsl(108, 50%, 10%)) into an array with 3 elements for red, green, and blue or if toHSL parameter is true, it will populate the array with hue, saturation, and lightness values. If a relative value is found in an hsl() or hsla() string, it will preserve those relative prefixes and all the values in the array will be strings instead of numbers (in all other cases it will be populated with numbers). * @param {(string|number)} v The value the should be parsed which could be a string like #9F0 or rgb(255,102,51) or rgba(255,0,0,0.5) or it could be a number like 0xFF00CC or even a named color like red, blue, purple, etc. * @param {(boolean)} toHSL If true, an hsl() or hsla() value will be returned instead of rgb() or rgba() * @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order, or if the toHSL parameter was true, the array will contain hue, saturation and lightness (and optionally alpha) in that order. Always numbers unless there's a relative prefix found in an hsl() or hsla() string and toHSL is true. */ _parseColor = CSSPlugin.parseColor = function(v, toHSL) { var a, r, g, b, h, s, l, max, min, d, wasHSL; if (!v) { a = _colorLookup.black; } else if (typeof(v) === "number") { a = [v >> 16, (v >> 8) & 255, v & 255]; } else { if (v.charAt(v.length - 1) === ",") { //sometimes a trailing comma is included and we should chop it off (typically from a comma-delimited list of values like a textShadow:"2px 2px 2px blue, 5px 5px 5px rgb(255,0,0)" - in this example "blue," has a trailing comma. We could strip it out inside parseComplex() but we'd need to do it to the beginning and ending values plus it wouldn't provide protection from other potential scenarios like if the user passes in a similar value. v = v.substr(0, v.length - 1); } if (_colorLookup[v]) { a = _colorLookup[v]; } else if (v.charAt(0) === "#") { if (v.length === 4) { //for shorthand like #9F0 r = v.charAt(1); g = v.charAt(2); b = v.charAt(3); v = "#" + r + r + g + g + b + b; } v = parseInt(v.substr(1), 16); a = [v >> 16, (v >> 8) & 255, v & 255]; } else if (v.substr(0, 3) === "hsl") { a = wasHSL = v.match(_numExp); if (!toHSL) { h = (Number(a[0]) % 360) / 360; s = Number(a[1]) / 100; l = Number(a[2]) / 100; g = (l <= 0.5) ? l * (s + 1) : l + s - l * s; r = l * 2 - g; if (a.length > 3) { a[3] = Number(a[3]); } a[0] = _hue(h + 1 / 3, r, g); a[1] = _hue(h, r, g); a[2] = _hue(h - 1 / 3, r, g); } else if (v.indexOf("=") !== -1) { //if relative values are found, just return the raw strings with the relative prefixes in place. return v.match(_relNumExp); } } else { a = v.match(_numExp) || _colorLookup.transparent; } a[0] = Number(a[0]); a[1] = Number(a[1]); a[2] = Number(a[2]); if (a.length > 3) { a[3] = Number(a[3]); } } if (toHSL && !wasHSL) { r = a[0] / 255; g = a[1] / 255; b = a[2] / 255; max = Math.max(r, g, b); min = Math.min(r, g, b); l = (max + min) / 2; if (max === min) { h = s = 0; } else { d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); h = (max === r) ? (g - b) / d + (g < b ? 6 : 0) : (max === g) ? (b - r) / d + 2 : (r - g) / d + 4; h *= 60; } a[0] = (h + 0.5) | 0; a[1] = (s * 100 + 0.5) | 0; a[2] = (l * 100 + 0.5) | 0; } return a; }, _formatColors = function(s, toHSL) { var colors = s.match(_colorExp) || [], charIndex = 0, parsed = "", i, color, temp; if (!colors.length) { return s; } for (i = 0; i < colors.length; i++) { color = colors[i]; temp = s.substr(charIndex, s.indexOf(color, charIndex)-charIndex); charIndex += temp.length + color.length; color = _parseColor(color, toHSL); if (color.length === 3) { color.push(1); } parsed += temp + (toHSL ? "hsla(" + color[0] + "," + color[1] + "%," + color[2] + "%," + color[3] : "rgba(" + color.join(",")) + ")"; } return parsed + s.substr(charIndex); }, _colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#(?:[0-9a-f]{3}){1,2}\\b"; //we'll dynamically build this Regular Expression to conserve file size. After building it, it will be able to find rgb(), rgba(), # (hexadecimal), and named color values like red, blue, purple, etc. for (p in _colorLookup) { _colorExp += "|" + p + "\\b"; } _colorExp = new RegExp(_colorExp+")", "gi"); CSSPlugin.colorStringFilter = function(a) { var combined = a[0] + " " + a[1], toHSL; if (_colorExp.test(combined)) { toHSL = (combined.indexOf("hsl(") !== -1 || combined.indexOf("hsla(") !== -1); a[0] = _formatColors(a[0], toHSL); a[1] = _formatColors(a[1], toHSL); } _colorExp.lastIndex = 0; }; if (!TweenLite.defaultStringFilter) { TweenLite.defaultStringFilter = CSSPlugin.colorStringFilter; } /** * @private Returns a formatter function that handles taking a string (or number in some cases) and returning a consistently formatted one in terms of delimiters, quantity of values, etc. For example, we may get boxShadow values defined as "0px red" or "0px 0px 10px rgb(255,0,0)" or "0px 0px 20px 20px #F00" and we need to ensure that what we get back is described with 4 numbers and a color. This allows us to feed it into the _parseComplex() method and split the values up appropriately. The neat thing about this _getFormatter() function is that the dflt defines a pattern as well as a default, so for example, _getFormatter("0px 0px 0px 0px #777", true) not only sets the default as 0px for all distances and #777 for the color, but also sets the pattern such that 4 numbers and a color will always get returned. * @param {!string} dflt The default value and pattern to follow. So "0px 0px 0px 0px #777" will ensure that 4 numbers and a color will always get returned. * @param {boolean=} clr If true, the values should be searched for color-related data. For example, boxShadow values typically contain a color whereas borderRadius don't. * @param {boolean=} collapsible If true, the value is a top/left/right/bottom style one that acts like margin or padding, where if only one value is received, it's used for all 4; if 2 are received, the first is duplicated for 3rd (bottom) and the 2nd is duplicated for the 4th spot (left), etc. * @return {Function} formatter function */ var _getFormatter = function(dflt, clr, collapsible, multi) { if (dflt == null) { return function(v) {return v;}; } var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "", dVals = dflt.split(dColor).join("").match(_valuesExp) || [], pfx = dflt.substr(0, dflt.indexOf(dVals[0])), sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "", delim = (dflt.indexOf(" ") !== -1) ? " " : ",", numVals = dVals.length, dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "", formatter; if (!numVals) { return function(v) {return v;}; } if (clr) { formatter = function(v) { var color, vals, i, a; if (typeof(v) === "number") { v += dSfx; } else if (multi && _commasOutsideParenExp.test(v)) { a = v.replace(_commasOutsideParenExp, "|").split("|"); for (i = 0; i < a.length; i++) { a[i] = formatter(a[i]); } return a.join(","); } color = (v.match(_colorExp) || [dColor])[0]; vals = v.split(color).join("").match(_valuesExp) || []; i = vals.length; if (numVals > i--) { while (++i < numVals) { vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i]; } } return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : ""); }; return formatter; } formatter = function(v) { var vals, a, i; if (typeof(v) === "number") { v += dSfx; } else if (multi && _commasOutsideParenExp.test(v)) { a = v.replace(_commasOutsideParenExp, "|").split("|"); for (i = 0; i < a.length; i++) { a[i] = formatter(a[i]); } return a.join(","); } vals = v.match(_valuesExp) || []; i = vals.length; if (numVals > i--) { while (++i < numVals) { vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i]; } } return pfx + vals.join(delim) + sfx; }; return formatter; }, /** * @private returns a formatter function that's used for edge-related values like marginTop, marginLeft, paddingBottom, paddingRight, etc. Just pass a comma-delimited list of property names related to the edges. * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft" * @return {Function} a formatter function */ _getEdgeParser = function(props) { props = props.split(","); return function(t, e, p, cssp, pt, plugin, vars) { var a = (e + "").split(" "), i; vars = {}; for (i = 0; i < 4; i++) { vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)]; } return cssp.parse(t, vars, pt, plugin); }; }, // @private used when other plugins must tween values first, like BezierPlugin or ThrowPropsPlugin, etc. That plugin's setRatio() gets called first so that the values are updated, and then we loop through the MiniPropTweens which handle copying the values into their appropriate slots so that they can then be applied correctly in the main CSSPlugin setRatio() method. Remember, we typically create a proxy object that has a bunch of uniquely-named properties that we feed to the sub-plugin and it does its magic normally, and then we must interpret those values and apply them to the css because often numbers must get combined/concatenated, suffixes added, etc. to work with css, like boxShadow could have 4 values plus a color. _setPluginRatio = _internals._setPluginRatio = function(v) { this.plugin.setRatio(v); var d = this.data, proxy = d.proxy, mpt = d.firstMPT, min = 0.000001, val, pt, i, str, p; while (mpt) { val = proxy[mpt.v]; if (mpt.r) { val = mpt.r(val); } else if (val < min && val > -min) { val = 0; } mpt.t[mpt.p] = val; mpt = mpt._next; } if (d.autoRotate) { d.autoRotate.rotation = d.mod ? d.mod.call(this._tween, proxy.rotation, this.t, this._tween) : proxy.rotation; //special case for ModifyPlugin to hook into an auto-rotating bezier } //at the end, we must set the CSSPropTween's "e" (end) value dynamically here because that's what is used in the final setRatio() method. Same for "b" at the beginning. if (v === 1 || v === 0) { mpt = d.firstMPT; p = (v === 1) ? "e" : "b"; while (mpt) { pt = mpt.t; if (!pt.type) { pt[p] = pt.s + pt.xs0; } else if (pt.type === 1) { str = pt.xs0 + pt.s + pt.xs1; for (i = 1; i < pt.l; i++) { str += pt["xn"+i] + pt["xs"+(i+1)]; } pt[p] = str; } mpt = mpt._next; } } }, /** * @private @constructor Used by a few SpecialProps to hold important values for proxies. For example, _parseToProxy() creates a MiniPropTween instance for each property that must get tweened on the proxy, and we record the original property name as well as the unique one we create for the proxy, plus whether or not the value needs to be rounded plus the original value. * @param {!Object} t target object whose property we're tweening (often a CSSPropTween) * @param {!string} p property name * @param {(number|string|object)} v value * @param {MiniPropTween=} next next MiniPropTween in the linked list * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer */ MiniPropTween = function(t, p, v, next, r) { this.t = t; this.p = p; this.v = v; this.r = r; if (next) { next._prev = this; this._next = next; } }, /** * @private Most other plugins (like BezierPlugin and ThrowPropsPlugin and others) can only tween numeric values, but CSSPlugin must accommodate special values that have a bunch of extra data (like a suffix or strings between numeric values, etc.). For example, boxShadow has values like "10px 10px 20px 30px rgb(255,0,0)" which would utterly confuse other plugins. This method allows us to split that data apart and grab only the numeric data and attach it to uniquely-named properties of a generic proxy object ({}) so that we can feed that to virtually any plugin to have the numbers tweened. However, we must also keep track of which properties from the proxy go with which CSSPropTween values and instances. So we create a linked list of MiniPropTweens. Each one records a target (the original CSSPropTween), property (like "s" or "xn1" or "xn2") that we're tweening and the unique property name that was used for the proxy (like "boxShadow_xn1" and "boxShadow_xn2") and whether or not they need to be rounded. That way, in the _setPluginRatio() method we can simply copy the values over from the proxy to the CSSPropTween instance(s). Then, when the main CSSPlugin setRatio() method runs and applies the CSSPropTween values accordingly, they're updated nicely. So the external plugin tweens the numbers, _setPluginRatio() copies them over, and setRatio() acts normally, applying css-specific values to the element. * This method returns an object that has the following properties: * - proxy: a generic object containing the starting values for all the properties that will be tweened by the external plugin. This is what we feed to the external _onInitTween() as the target * - end: a generic object containing the ending values for all the properties that will be tweened by the external plugin. This is what we feed to the external plugin's _onInitTween() as the destination values * - firstMPT: the first MiniPropTween in the linked list * - pt: the first CSSPropTween in the linked list that was created when parsing. If shallow is true, this linked list will NOT attach to the one passed into the _parseToProxy() as the "pt" (4th) parameter. * @param {!Object} t target object to be tweened * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed * @param {!CSSPlugin} cssp The CSSPlugin instance * @param {CSSPropTween=} pt the next CSSPropTween in the linked list * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values * @param {boolean=} shallow if true, the resulting linked list from the parse will NOT be attached to the CSSPropTween that was passed in as the "pt" (4th) parameter. * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions) */ _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) { var bpt = pt, start = {}, end = {}, transform = cssp._transform, oldForce = _forcePT, i, p, xp, mpt, firstPT; cssp._transform = null; _forcePT = vars; pt = firstPT = cssp.parse(t, vars, pt, plugin); _forcePT = oldForce; //break off from the linked list so the new ones are isolated. if (shallow) { cssp._transform = transform; if (bpt) { bpt._prev = null; if (bpt._prev) { bpt._prev._next = null; } } } while (pt && pt !== bpt) { if (pt.type <= 1) { p = pt.p; end[p] = pt.s + pt.c; start[p] = pt.s; if (!shallow) { mpt = new MiniPropTween(pt, "s", p, mpt, pt.r); pt.c = 0; } if (pt.type === 1) { i = pt.l; while (--i > 0) { xp = "xn" + i; p = pt.p + "_" + xp; end[p] = pt.data[xp]; start[p] = pt[xp]; if (!shallow) { mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]); } } } } pt = pt._next; } return {proxy:start, end:end, firstMPT:mpt, pt:firstPT}; }, /** * @constructor Each property that is tweened has at least one CSSPropTween associated with it. These instances store important information like the target, property, starting value, amount of change, etc. They can also optionally have a number of "extra" strings and numeric values named xs1, xn1, xs2, xn2, xs3, xn3, etc. where "s" indicates string and "n" indicates number. These can be pieced together in a complex-value tween (type:1) that has alternating types of data like a string, number, string, number, etc. For example, boxShadow could be "5px 5px 8px rgb(102, 102, 51)". In that value, there are 6 numbers that may need to tween and then pieced back together into a string again with spaces, suffixes, etc. xs0 is special in that it stores the suffix for standard (type:0) tweens, -OR- the first string (prefix) in a complex-value (type:1) CSSPropTween -OR- it can be the non-tweening value in a type:-1 CSSPropTween. We do this to conserve memory. * CSSPropTweens have the following optional properties as well (not defined through the constructor): * - l: Length in terms of the number of extra properties that the CSSPropTween has (default: 0). For example, for a boxShadow we may need to tween 5 numbers in which case l would be 5; Keep in mind that the start/end values for the first number that's tweened are always stored in the s and c properties to conserve memory. All additional values thereafter are stored in xn1, xn2, etc. * - xfirst: The first instance of any sub-CSSPropTweens that are tweening properties of this instance. For example, we may split up a boxShadow tween so that there's a main CSSPropTween of type:1 that has various xs* and xn* values associated with the h-shadow, v-shadow, blur, color, etc. Then we spawn a CSSPropTween for each of those that has a higher priority and runs BEFORE the main CSSPropTween so that the values are all set by the time it needs to re-assemble them. The xfirst gives us an easy way to identify the first one in that chain which typically ends at the main one (because they're all prepende to the linked list) * - plugin: The TweenPlugin instance that will handle the tweening of any complex values. For example, sometimes we don't want to use normal subtweens (like xfirst refers to) to tween the values - we might want ThrowPropsPlugin or BezierPlugin some other plugin to do the actual tweening, so we create a plugin instance and store a reference here. We need this reference so that if we get a request to round values or disable a tween, we can pass along that request. * - data: Arbitrary data that needs to be stored with the CSSPropTween. Typically if we're going to have a plugin handle the tweening of a complex-value tween, we create a generic object that stores the END values that we're tweening to and the CSSPropTween's xs1, xs2, etc. have the starting values. We store that object as data. That way, we can simply pass that object to the plugin and use the CSSPropTween as the target. * - setRatio: Only used for type:2 tweens that require custom functionality. In this case, we call the CSSPropTween's setRatio() method and pass the ratio each time the tween updates. This isn't quite as efficient as doing things directly in the CSSPlugin's setRatio() method, but it's very convenient and flexible. * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything. * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width". * @param {number} s Starting numeric value * @param {number} c Change in numeric value over the course of the entire tween. For example, if element.width starts at 5 and should end at 100, c would be 95. * @param {CSSPropTween=} next The next CSSPropTween in the linked list. If one is defined, we will define its _prev as the new instance, and the new instance's _next will be pointed at it. * @param {number=} type The type of CSSPropTween where -1 = a non-tweening value, 0 = a standard simple tween, 1 = a complex value (like one that has multiple numbers in a comma- or space-delimited string like border:"1px solid red"), and 2 = one that uses a custom setRatio function that does all of the work of applying the values on each update. * @param {string=} n Name of the property that should be used for overwriting purposes which is typically the same as p but not always. For example, we may need to create a subtween for the 2nd part of a "clip:rect(...)" tween in which case "p" might be xs1 but "n" is still "clip" * @param {boolean=} r If true, the value(s) should be rounded * @param {number=} pr Priority in the linked list order. Higher priority CSSPropTweens will be updated before lower priority ones. The default priority is 0. * @param {string=} b Beginning value. We store this to ensure that it is EXACTLY what it was when the tween began without any risk of interpretation issues. * @param {string=} e Ending value. We store this to ensure that it is EXACTLY what the user defined at the end of the tween without any risk of interpretation issues. */ CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) { this.t = t; //target this.p = p; //property this.s = s; //starting value this.c = c; //change value this.n = n || p; //name that this CSSPropTween should be associated to (usually the same as p, but not always - n is what overwriting looks at) if (!(t instanceof CSSPropTween)) { _overwriteProps.push(this.n); } this.r = !r ? r : (typeof(r) === "function") ? r : Math.round; //round (boolean) this.type = type || 0; //0 = normal tween, -1 = non-tweening (in which case xs0 will be applied to the target's property, like tp.t[tp.p] = tp.xs0), 1 = complex-value SpecialProp, 2 = custom setRatio() that does all the work if (pr) { this.pr = pr; _hasPriority = true; } this.b = (b === undefined) ? s : b; this.e = (e === undefined) ? s + c : e; if (next) { this._next = next; next._prev = this; } }, _addNonTweeningNumericPT = function(target, prop, start, end, next, overwriteProp) { //cleans up some code redundancies and helps minification. Just a fast way to add a NUMERIC non-tweening CSSPropTween var pt = new CSSPropTween(target, prop, start, end - start, next, -1, overwriteProp); pt.b = start; pt.e = pt.xs0 = end; return pt; }, /** * Takes a target, the beginning value and ending value (as strings) and parses them into a CSSPropTween (possibly with child CSSPropTweens) that accommodates multiple numbers, colors, comma-delimited values, etc. For example: * sp.parseComplex(element, "boxShadow", "5px 10px 20px rgb(255,102,51)", "0px 0px 0px red", true, "0px 0px 0px rgb(0,0,0,0)", pt); * It will walk through the beginning and ending values (which should be in the same format with the same number and type of values) and figure out which parts are numbers, what strings separate the numeric/tweenable values, and then create the CSSPropTweens accordingly. If a plugin is defined, no child CSSPropTweens will be created. Instead, the ending values will be stored in the "data" property of the returned CSSPropTween like: {s:-5, xn1:-10, xn2:-20, xn3:255, xn4:0, xn5:0} so that it can be fed to any other plugin and it'll be plain numeric tweens but the recomposition of the complex value will be handled inside CSSPlugin's setRatio(). * If a setRatio is defined, the type of the CSSPropTween will be set to 2 and recomposition of the values will be the responsibility of that method. * * @param {!Object} t Target whose property will be tweened * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow") * @param {string} b Beginning value * @param {string} e Ending value * @param {boolean} clrs If true, the value could contain a color value like "rgb(255,0,0)" or "#F00" or "red". The default is