/*!
 * CSSPlugin 3.12.2
 * https://greensock.com
 *
 * Copyright 2008-2023, GreenSock. All rights reserved.
 * Subject to the terms at https://greensock.com/standard-license or for
 * Club GreenSock members, the agreement issued with that membership.
 * @author: Jack Doyle, jack@greensock.com
*/
/* eslint-disable */

import {gsap, _getProperty, _numExp, _numWithUnitExp, getUnit, _isString, _isUndefined, _renderComplexString, _relExp, _forEachName, _sortPropTweensByPriority, _colorStringFilter, _checkPlugin, _replaceRandom, _plugins, GSCache, PropTween, _config, _ticker, _round, _missingPlugin, _getSetter, _getCache, _colorExp, _parseRelative,
	_setDefaults, _removeLinkedListItem //for the commented-out className feature.
} from "./gsap-core.js";

let _win, _doc, _docElement, _pluginInitted, _tempDiv, _tempDivStyler, _recentSetterPlugin, _reverting,
	_windowExists = () => typeof(window) !== "undefined",
	_transformProps = {},
	_RAD2DEG = 180 / Math.PI,
	_DEG2RAD = Math.PI / 180,
	_atan2 = Math.atan2,
	_bigNum = 1e8,
	_capsExp = /([A-Z])/g,
	_horizontalExp = /(left|right|width|margin|padding|x)/i,
	_complexExp = /[\s,\(]\S/,
	_propertyAliases = {autoAlpha:"opacity,visibility", scale:"scaleX,scaleY", alpha:"opacity"},
	_renderCSSProp = (ratio, data) => data.set(data.t, data.p, (Math.round((data.s + data.c * ratio) * 10000) / 10000) + data.u, data),
	_renderPropWithEnd = (ratio, data) => data.set(data.t, data.p, ratio === 1 ? data.e : (Math.round((data.s + data.c * ratio) * 10000) / 10000) + data.u, data),
	_renderCSSPropWithBeginning = (ratio, data) => data.set(data.t, data.p, ratio ? (Math.round((data.s + data.c * ratio) * 10000) / 10000) + data.u : data.b, data), //if units change, we need a way to render the original unit/value when the tween goes all the way back to the beginning (ratio:0)
	_renderRoundedCSSProp = (ratio, data) => {
		let value = data.s + data.c * ratio;
		data.set(data.t, data.p, ~~(value + (value < 0 ? -.5 : .5)) + data.u, data);
	},
	_renderNonTweeningValue = (ratio, data) => data.set(data.t, data.p, ratio ? data.e : data.b, data),
	_renderNonTweeningValueOnlyAtEnd = (ratio, data) => data.set(data.t, data.p, ratio !== 1 ? data.b : data.e, data),
	_setterCSSStyle = (target, property, value) => target.style[property] = value,
	_setterCSSProp = (target, property, value) => target.style.setProperty(property, value),
	_setterTransform = (target, property, value) => target._gsap[property] = value,
	_setterScale = (target, property, value) => target._gsap.scaleX = target._gsap.scaleY = value,
	_setterScaleWithRender = (target, property, value, data, ratio) => {
		let cache = target._gsap;
		cache.scaleX = cache.scaleY = value;
		cache.renderTransform(ratio, cache);
	},
	_setterTransformWithRender = (target, property, value, data, ratio) => {
		let cache = target._gsap;
		cache[property] = value;
		cache.renderTransform(ratio, cache);
	},
	_transformProp = "transform",
	_transformOriginProp = _transformProp + "Origin",
	_saveStyle = function(property, isNotCSS) {
		let target = this.target,
			style = target.style;
		if ((property in _transformProps) && style) {
			this.tfm = this.tfm || {};
			if (property !== "transform") {
				property = _propertyAliases[property] || property;
				~property.indexOf(",") ? property.split(",").forEach(a => this.tfm[a] = _get(target, a)) : (this.tfm[property] = target._gsap.x ? target._gsap[property] : _get(target, property)); // note: scale would map to "scaleX,scaleY", thus we loop and apply them both.
			} else {
				return _propertyAliases.transform.split(",").forEach(p => _saveStyle.call(this, p, isNotCSS));
			}
			if (this.props.indexOf(_transformProp) >= 0) { return; }
			if (target._gsap.svg) {
				this.svgo = target.getAttribute("data-svg-origin");
				this.props.push(_transformOriginProp, isNotCSS, "");
			}
			property = _transformProp;
		}
		(style || isNotCSS) && this.props.push(property, isNotCSS, style[property]);
	},
	_removeIndependentTransforms = style => {
		if (style.translate) {
			style.removeProperty("translate");
			style.removeProperty("scale");
			style.removeProperty("rotate");
		}
	},
	_revertStyle = function() {
		let props = this.props,
			target = this.target,
			style = target.style,
			cache = target._gsap,
			i, p;
		for (i = 0; i < props.length; i+=3) { // stored like this: property, isNotCSS, value
			props[i+1] ? target[props[i]] = props[i+2] : props[i+2] ? (style[props[i]] = props[i+2]) : style.removeProperty(props[i].substr(0,2) === "--" ? props[i] : props[i].replace(_capsExp, "-$1").toLowerCase());
		}
		if (this.tfm) {
			for (p in this.tfm) {
				cache[p] = this.tfm[p];
			}
			if (cache.svg) {
				cache.renderTransform();
				target.setAttribute("data-svg-origin", this.svgo || "");
			}
			i = _reverting();
			if ((!i || !i.isStart) && !style[_transformProp]) {
				_removeIndependentTransforms(style);
				cache.uncache = 1; // if it's a startAt that's being reverted in the _initTween() of the core, we don't need to uncache transforms. This is purely a performance optimization.
			}
		}
	},
	_getStyleSaver = (target, properties) => {
		let saver = {
			target,
			props: [],
			revert: _revertStyle,
			save: _saveStyle
		};
		target._gsap || gsap.core.getCache(target); // just make sure there's a _gsap cache defined because we read from it in _saveStyle() and it's more efficient to just check it here once.
		properties && properties.split(",").forEach(p => saver.save(p));
		return saver;
	},
	_supports3D,
	_createElement = (type, ns) => {
		let e = _doc.createElementNS ? _doc.createElementNS((ns || "http://www.w3.org/1999/xhtml").replace(/^https/, "http"), type) : _doc.createElement(type); //some servers swap in https for http in the namespace which can break things, making "style" inaccessible.
		return e.style ? e : _doc.createElement(type); //some environments won't allow access to the element's style when created with a namespace in which case we default to the standard createElement() to work around the issue. Also note that when GSAP is embedded directly inside an SVG file, createElement() won't allow access to the style object in Firefox (see https://greensock.com/forums/topic/20215-problem-using-tweenmax-in-standalone-self-containing-svg-file-err-cannot-set-property-csstext-of-undefined/).
	},
	_getComputedProperty = (target, property, skipPrefixFallback) => {
		let cs = getComputedStyle(target);
		return cs[property] || cs.getPropertyValue(property.replace(_capsExp, "-$1").toLowerCase()) || cs.getPropertyValue(property) || (!skipPrefixFallback && _getComputedProperty(target, _checkPropPrefix(property) || property, 1)) || ""; //css variables may not need caps swapped out for dashes and lowercase.
	},
	_prefixes = "O,Moz,ms,Ms,Webkit".split(","),
	_checkPropPrefix = (property, element, preferPrefix) => {
		let e = element || _tempDiv,
			s = e.style,
			i = 5;
		if (property in s && !preferPrefix) {
			return property;
		}
		property = property.charAt(0).toUpperCase() + property.substr(1);
		while (i-- && !((_prefixes[i]+property) in s)) { }
		return (i < 0) ? null : ((i === 3) ? "ms" : (i >= 0) ? _prefixes[i] : "") + property;
	},
	_initCore = () => {
		if (_windowExists() && window.document) {
			_win = window;
			_doc = _win.document;
			_docElement = _doc.documentElement;
			_tempDiv = _createElement("div") || {style:{}};
			_tempDivStyler = _createElement("div");
			_transformProp = _checkPropPrefix(_transformProp);
			_transformOriginProp = _transformProp + "Origin";
			_tempDiv.style.cssText = "border-width:0;line-height:0;position:absolute;padding:0"; //make sure to override certain properties that may contaminate measurements, in case the user has overreaching style sheets.
			_supports3D = !!_checkPropPrefix("perspective");
			_reverting = gsap.core.reverting;
			_pluginInitted = 1;
		}
	},
	_getBBoxHack = function(swapIfPossible) { //works around issues in some browsers (like Firefox) that don't correctly report getBBox() on SVG elements inside a <defs> element and/or <mask>. We try creating an SVG, adding it to the documentElement and toss the element in there so that it's definitely part of the rendering tree, then grab the bbox and if it works, we actually swap out the original getBBox() method for our own that does these extra steps whenever getBBox is needed. This helps ensure that performance is optimal (only do all these extra steps when absolutely necessary...most elements don't need it).
		let svg = _createElement("svg", (this.ownerSVGElement && this.ownerSVGElement.getAttribute("xmlns")) || "http://www.w3.org/2000/svg"),
			oldParent = this.parentNode,
			oldSibling = this.nextSibling,
			oldCSS = this.style.cssText,
			bbox;
		_docElement.appendChild(svg);
		svg.appendChild(this);
		this.style.display = "block";
		if (swapIfPossible) {
			try {
				bbox = this.getBBox();
				this._gsapBBox = this.getBBox; //store the original
				this.getBBox = _getBBoxHack;
			} catch (e) { }
		} else if (this._gsapBBox) {
			bbox = this._gsapBBox();
		}
		if (oldParent) {
			if (oldSibling) {
				oldParent.insertBefore(this, oldSibling);
			} else {
				oldParent.appendChild(this);
			}
		}
		_docElement.removeChild(svg);
		this.style.cssText = oldCSS;
		return bbox;
	},
	_getAttributeFallbacks = (target, attributesArray) => {
		let i = attributesArray.length;
		while (i--) {
			if (target.hasAttribute(attributesArray[i])) {
				return target.getAttribute(attributesArray[i]);
			}
		}
	},
	_getBBox = target => {
		let bounds;
		try {
			bounds = target.getBBox(); //Firefox throws errors if you try calling getBBox() on an SVG element that's not rendered (like in a <symbol> or <defs>). https://bugzilla.mozilla.org/show_bug.cgi?id=612118
		} catch (error) {
			bounds = _getBBoxHack.call(target, true);
		}
		(bounds && (bounds.width || bounds.height)) || target.getBBox === _getBBoxHack || (bounds = _getBBoxHack.call(target, true));
		//some browsers (like Firefox) misreport the bounds if the element has zero width and height (it just assumes it's at x:0, y:0), thus we need to manually grab the position in that case.
		return (bounds && !bounds.width && !bounds.x && !bounds.y) ? {x: +_getAttributeFallbacks(target, ["x","cx","x1"]) || 0, y:+_getAttributeFallbacks(target, ["y","cy","y1"]) || 0, width:0, height:0} : bounds;
	},
	_isSVG = e => !!(e.getCTM && (!e.parentNode || e.ownerSVGElement) && _getBBox(e)), //reports if the element is an SVG on which getBBox() actually works
	_removeProperty = (target, property) => {
		if (property) {
			let style = target.style;
			if (property in _transformProps && property !== _transformOriginProp) {
				property = _transformProp;
			}
			if (style.removeProperty) {
				if (property.substr(0,2) === "ms" || property.substr(0,6) === "webkit") { //Microsoft and some Webkit browsers don't conform to the standard of capitalizing the first prefix character, so we adjust so that when we prefix the caps with a dash, it's correct (otherwise it'd be "ms-transform" instead of "-ms-transform" for IE9, for example)
					property = "-" + property;
				}
				style.removeProperty(property.replace(_capsExp, "-$1").toLowerCase());
			} else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()"
				style.removeAttribute(property);
			}
		}
	},
	_addNonTweeningPT = (plugin, target, property, beginning, end, onlySetAtEnd) => {
		let pt = new PropTween(plugin._pt, target, property, 0, 1, onlySetAtEnd ? _renderNonTweeningValueOnlyAtEnd : _renderNonTweeningValue);
		plugin._pt = pt;
		pt.b = beginning;
		pt.e = end;
		plugin._props.push(property);
		return pt;
	},
	_nonConvertibleUnits = {deg:1, rad:1, turn:1},
	_nonStandardLayouts = {grid:1, flex:1},
	//takes a single value like 20px and converts it to the unit specified, like "%", returning only the numeric amount.
	_convertToUnit = (target, property, value, unit) => {
		let curValue = parseFloat(value) || 0,
			curUnit = (value + "").trim().substr((curValue + "").length) || "px", // some browsers leave extra whitespace at the beginning of CSS variables, hence the need to trim()
			style = _tempDiv.style,
			horizontal = _horizontalExp.test(property),
			isRootSVG = target.tagName.toLowerCase() === "svg",
			measureProperty = (isRootSVG ? "client" : "offset") + (horizontal ? "Width" : "Height"),
			amount = 100,
			toPixels = unit === "px",
			toPercent = unit === "%",
			px, parent, cache, isSVG;
		if (unit === curUnit || !curValue || _nonConvertibleUnits[unit] || _nonConvertibleUnits[curUnit]) {
			return curValue;
		}
		(curUnit !== "px" && !toPixels) && (curValue = _convertToUnit(target, property, value, "px"));
		isSVG = target.getCTM && _isSVG(target);
		if ((toPercent || curUnit === "%") && (_transformProps[property] || ~property.indexOf("adius"))) {
			px = isSVG ? target.getBBox()[horizontal ? "width" : "height"] : target[measureProperty];
			return _round(toPercent ? curValue / px * amount : curValue / 100 * px);
		}
		style[horizontal ? "width" : "height"] = amount + (toPixels ? curUnit : unit);
		parent = (~property.indexOf("adius") || (unit === "em" && target.appendChild && !isRootSVG)) ? target : target.parentNode;
		if (isSVG) {
			parent = (target.ownerSVGElement || {}).parentNode;
		}
		if (!parent || parent === _doc || !parent.appendChild) {
			parent = _doc.body;
		}
		cache = parent._gsap;
		if (cache && toPercent && cache.width && horizontal && cache.time === _ticker.time && !cache.uncache) {
			return _round(curValue / cache.width * amount);
		} else {
			(toPercent || curUnit === "%") && !_nonStandardLayouts[_getComputedProperty(parent, "display")] && (style.position = _getComputedProperty(target, "position"));
			(parent === target) && (style.position = "static"); // like for borderRadius, if it's a % we must have it relative to the target itself but that may not have position: relative or position: absolute in which case it'd go up the chain until it finds its offsetParent (bad). position: static protects against that.
			parent.appendChild(_tempDiv);
			px = _tempDiv[measureProperty];
			parent.removeChild(_tempDiv);
			style.position = "absolute";
			if (horizontal && toPercent) {
				cache = _getCache(parent);
				cache.time = _ticker.time;
				cache.width = parent[measureProperty];
			}
		}
		return _round(toPixels ? px * curValue / amount : px && curValue ? amount / px * curValue : 0);
	},
	_get = (target, property, unit, uncache) => {
		let value;
		_pluginInitted || _initCore();
		if ((property in _propertyAliases) && property !== "transform") {
			property = _propertyAliases[property];
			if (~property.indexOf(",")) {
				property = property.split(",")[0];
			}
		}
		if (_transformProps[property] && property !== "transform") {
			value = _parseTransform(target, uncache);
			value = (property !== "transformOrigin") ? value[property] : value.svg ? value.origin : _firstTwoOnly(_getComputedProperty(target, _transformOriginProp)) + " " + value.zOrigin + "px";
		} else {
			value = target.style[property];
			if (!value || value === "auto" || uncache || ~(value + "").indexOf("calc(")) {
				value = (_specialProps[property] && _specialProps[property](target, property, unit)) || _getComputedProperty(target, property) || _getProperty(target, property) || (property === "opacity" ? 1 : 0); // note: some browsers, like Firefox, don't report borderRadius correctly! Instead, it only reports every corner like  borderTopLeftRadius
			}
		}
		return unit && !~(value + "").trim().indexOf(" ") ? _convertToUnit(target, property, value, unit) + unit : value;

	},
	_tweenComplexCSSString = function(target, prop, start, end) { // note: we call _tweenComplexCSSString.call(pluginInstance...) to ensure that it's scoped properly. We may call it from within a plugin too, thus "this" would refer to the plugin.
		if (!start || start === "none") { // some browsers like Safari actually PREFER the prefixed property and mis-report the unprefixed value like clipPath (BUG). In other words, even though clipPath exists in the style ("clipPath" in target.style) and it's set in the CSS properly (along with -webkit-clip-path), Safari reports clipPath as "none" whereas WebkitClipPath reports accurately like "ellipse(100% 0% at 50% 0%)", so in this case we must SWITCH to using the prefixed property instead. See https://greensock.com/forums/topic/18310-clippath-doesnt-work-on-ios/
			let p = _checkPropPrefix(prop, target, 1),
				s = p && _getComputedProperty(target, p, 1);
			if (s && s !== start) {
				prop = p;
				start = s;
			} else if (prop === "borderColor") {
				start = _getComputedProperty(target, "borderTopColor"); // Firefox bug: always reports "borderColor" as "", so we must fall back to borderTopColor. See https://greensock.com/forums/topic/24583-how-to-return-colors-that-i-had-after-reverse/
			}
		}
		let pt = new PropTween(this._pt, target.style, prop, 0, 1, _renderComplexString),
			index = 0,
			matchIndex = 0,
			a, result,	startValues, startNum, color, startValue, endValue, endNum, chunk, endUnit, startUnit, endValues;
		pt.b = start;
		pt.e = end;
		start += ""; // ensure values are strings
		end += "";
		if (end === "auto") {
			target.style[prop] = end;
			end = _getComputedProperty(target, prop) || end;
			target.style[prop] = start;
		}
		a = [start, end];
		_colorStringFilter(a); // pass an array with the starting and ending values and let the filter do whatever it needs to the values. If colors are found, it returns true and then we must match where the color shows up order-wise because for things like boxShadow, sometimes the browser provides the computed values with the color FIRST, but the user provides it with the color LAST, so flip them if necessary. Same for drop-shadow().
		start = a[0];
		end = a[1];
		startValues = start.match(_numWithUnitExp) || [];
		endValues = end.match(_numWithUnitExp) || [];
		if (endValues.length) {
			while ((result = _numWithUnitExp.exec(end))) {
				endValue = result[0];
				chunk = end.substring(index, result.index);
				if (color) {
					color = (color + 1) % 5;
				} else if (chunk.substr(-5) === "rgba(" || chunk.substr(-5) === "hsla(") {
					color = 1;
				}
				if (endValue !== (startValue = startValues[matchIndex++] || "")) {
					startNum = parseFloat(startValue) || 0;
					startUnit = startValue.substr((startNum + "").length);
					(endValue.charAt(1) === "=") && (endValue = _parseRelative(startNum, endValue) + startUnit);
					endNum = parseFloat(endValue);
					endUnit = endValue.substr((endNum + "").length);
					index = _numWithUnitExp.lastIndex - endUnit.length;
					if (!endUnit) { //if something like "perspective:300" is passed in and we must add a unit to the end
						endUnit = endUnit || _config.units[prop] || startUnit;
						if (index === end.length) {
							end += endUnit;
							pt.e += endUnit;
						}
					}
					if (startUnit !== endUnit) {
						startNum = _convertToUnit(target, prop, startValue, endUnit) || 0;
					}
					// these nested PropTweens are handled in a special way - we'll never actually call a render or setter method on them. We'll just loop through them in the parent complex string PropTween's render method.
					pt._pt = {
						_next: pt._pt,
						p: (chunk || (matchIndex === 1)) ? chunk : ",", //note: SVG spec allows omission of comma/space when a negative sign is wedged between two numbers, like 2.5-5.3 instead of 2.5,-5.3 but when tweening, the negative value may switch to positive, so we insert the comma just in case.
						s: startNum,
						c: endNum - startNum,
						m: (color && color < 4) || prop === "zIndex" ? Math.round : 0
					};
				}
			}
			pt.c = (index < end.length) ? end.substring(index, end.length) : ""; //we use the "c" of the PropTween to store the final part of the string (after the last number)
		} else {
			pt.r = prop === "display" && end === "none" ? _renderNonTweeningValueOnlyAtEnd : _renderNonTweeningValue;
		}
		_relExp.test(end) && (pt.e = 0); //if the end string contains relative values or dynamic random(...) values, delete the end it so that on the final render we don't actually set it to the string with += or -= characters (forces it to use the calculated value).
		this._pt = pt; //start the linked list with this new PropTween. Remember, we call _tweenComplexCSSString.call(pluginInstance...) to ensure that it's scoped properly. We may call it from within another plugin too, thus "this" would refer to the plugin.
		return pt;
	},
	_keywordToPercent = {top:"0%", bottom:"100%", left:"0%", right:"100%", center:"50%"},
	_convertKeywordsToPercentages = value => {
		let split = value.split(" "),
			x = split[0],
			y = split[1] || "50%";
		if (x === "top" || x === "bottom" || y === "left" || y === "right") { //the user provided them in the wrong order, so flip them
			value = x;
			x = y;
			y = value;
		}
		split[0] = _keywordToPercent[x] || x;
		split[1] = _keywordToPercent[y] || y;
		return split.join(" ");
	},
	_renderClearProps = (ratio, data) => {
		if (data.tween && data.tween._time === data.tween._dur) {
			let target = data.t,
				style = target.style,
				props = data.u,
				cache = target._gsap,
				prop, clearTransforms, i;
			if (props === "all" || props === true) {
				style.cssText = "";
				clearTransforms = 1;
			} else {
				props = props.split(",");
				i = props.length;
				while (--i > -1) {
					prop = props[i];
					if (_transformProps[prop]) {
						clearTransforms = 1;
						prop = (prop === "transformOrigin") ? _transformOriginProp : _transformProp;
					}
					_removeProperty(target, prop);
				}
			}
			if (clearTransforms) {
				_removeProperty(target, _transformProp);
				if (cache) {
					cache.svg && target.removeAttribute("transform");
					_parseTransform(target, 1); // force all the cached values back to "normal"/identity, otherwise if there's another tween that's already set to render transforms on this element, it could display the wrong values.
					cache.uncache = 1;
					_removeIndependentTransforms(style);
				}
			}
		}
	},
	// note: specialProps should return 1 if (and only if) they have a non-zero priority. It indicates we need to sort the linked list.
	_specialProps = {
		clearProps(plugin, target, property, endValue, tween) {
			if (tween.data !== "isFromStart") {
				let pt = plugin._pt = new PropTween(plugin._pt, target, property, 0, 0, _renderClearProps);
				pt.u = endValue;
				pt.pr = -10;
				pt.tween = tween;
				plugin._props.push(property);
				return 1;
			}
		}
		/* className feature (about 0.4kb gzipped).
		, className(plugin, target, property, endValue, tween) {
			let _renderClassName = (ratio, data) => {
					data.css.render(ratio, data.css);
					if (!ratio || ratio === 1) {
						let inline = data.rmv,
							target = data.t,
							p;
						target.setAttribute("class", ratio ? data.e : data.b);
						for (p in inline) {
							_removeProperty(target, p);
						}
					}
				},
				_getAllStyles = (target) => {
					let styles = {},
						computed = getComputedStyle(target),
						p;
					for (p in computed) {
						if (isNaN(p) && p !== "cssText" && p !== "length") {
							styles[p] = computed[p];
						}
					}
					_setDefaults(styles, _parseTransform(target, 1));
					return styles;
				},
				startClassList = target.getAttribute("class"),
				style = target.style,
				cssText = style.cssText,
				cache = target._gsap,
				classPT = cache.classPT,
				inlineToRemoveAtEnd = {},
				data = {t:target, plugin:plugin, rmv:inlineToRemoveAtEnd, b:startClassList, e:(endValue.charAt(1) !== "=") ? endValue : startClassList.replace(new RegExp("(?:\\s|^)" + endValue.substr(2) + "(?![\\w-])"), "") + ((endValue.charAt(0) === "+") ? " " + endValue.substr(2) : "")},
				changingVars = {},
				startVars = _getAllStyles(target),
				transformRelated = /(transform|perspective)/i,
				endVars, p;
			if (classPT) {
				classPT.r(1, classPT.d);
				_removeLinkedListItem(classPT.d.plugin, classPT, "_pt");
			}
			target.setAttribute("class", data.e);
			endVars = _getAllStyles(target, true);
			target.setAttribute("class", startClassList);
			for (p in endVars) {
				if (endVars[p] !== startVars[p] && !transformRelated.test(p)) {
					changingVars[p] = endVars[p];
					if (!style[p] && style[p] !== "0") {
						inlineToRemoveAtEnd[p] = 1;
					}
				}
			}
			cache.classPT = plugin._pt = new PropTween(plugin._pt, target, "className", 0, 0, _renderClassName, data, 0, -11);
			if (style.cssText !== cssText) { //only apply if things change. Otherwise, in cases like a background-image that's pulled dynamically, it could cause a refresh. See https://greensock.com/forums/topic/20368-possible-gsap-bug-switching-classnames-in-chrome/.
				style.cssText = cssText; //we recorded cssText before we swapped classes and ran _getAllStyles() because in cases when a className tween is overwritten, we remove all the related tweening properties from that class change (otherwise class-specific stuff can't override properties we've directly set on the target's style object due to specificity).
			}
			_parseTransform(target, true); //to clear the caching of transforms
			data.css = new gsap.plugins.css();
			data.css.init(target, changingVars, tween);
			plugin._props.push(...data.css._props);
			return 1;
		}
		*/
	},





	/*
	 * --------------------------------------------------------------------------------------
	 * TRANSFORMS
	 * --------------------------------------------------------------------------------------
	 */
	_identity2DMatrix = [1,0,0,1,0,0],
	_rotationalProperties = {},
	_isNullTransform = value => (value === "matrix(1, 0, 0, 1, 0, 0)" || value === "none" || !value),
	_getComputedTransformMatrixAsArray = target => {
		let matrixString = _getComputedProperty(target, _transformProp);
		return _isNullTransform(matrixString) ? _identity2DMatrix : matrixString.substr(7).match(_numExp).map(_round);
	},
	_getMatrix = (target, force2D) => {
		let cache = target._gsap || _getCache(target),
			style = target.style,
			matrix = _getComputedTransformMatrixAsArray(target),
			parent, nextSibling, temp, addedToDOM;
		if (cache.svg && target.getAttribute("transform")) {
			temp = target.transform.baseVal.consolidate().matrix; //ensures that even complex values like "translate(50,60) rotate(135,0,0)" are parsed because it mashes it into a matrix.
			matrix = [temp.a, temp.b, temp.c, temp.d, temp.e, temp.f];
			return (matrix.join(",") === "1,0,0,1,0,0") ? _identity2DMatrix : matrix;
		} else if (matrix === _identity2DMatrix && !target.offsetParent && target !== _docElement && !cache.svg) { //note: if offsetParent is null, that means the element isn't in the normal document flow, like if it has display:none or one of its ancestors has display:none). Firefox returns null for getComputedStyle() if the element is in an iframe that has display:none. https://bugzilla.mozilla.org/show_bug.cgi?id=548397
			//browsers don't report transforms accurately unless the element is in the DOM and has a display value that's not "none". Firefox and Microsoft browsers have a partial bug where they'll report transforms even if display:none BUT not any percentage-based values like translate(-50%, 8px) will be reported as if it's translate(0, 8px).
			temp = style.display;
			style.display = "block";
			parent = target.parentNode;
			if (!parent || !target.offsetParent) { // note: in 3.3.0 we switched target.offsetParent to _doc.body.contains(target) to avoid [sometimes unnecessary] MutationObserver calls but that wasn't adequate because there are edge cases where nested position: fixed elements need to get reparented to accurately sense transforms. See https://github.com/greensock/GSAP/issues/388 and https://github.com/greensock/GSAP/issues/375
				addedToDOM = 1; //flag
				nextSibling = target.nextElementSibling;
				_docElement.appendChild(target); //we must add it to the DOM in order to get values properly
			}
			matrix = _getComputedTransformMatrixAsArray(target);
			temp ? (style.display = temp) : _removeProperty(target, "display");
			if (addedToDOM) {
				nextSibling ? parent.insertBefore(target, nextSibling) : parent ? parent.appendChild(target) : _docElement.removeChild(target);
			}
		}
		return (force2D && matrix.length > 6) ? [matrix[0], matrix[1], matrix[4], matrix[5], matrix[12], matrix[13]] : matrix;
	},
	_applySVGOrigin = (target, origin, originIsAbsolute, smooth, matrixArray, pluginToAddPropTweensTo) => {
		let cache = target._gsap,
			matrix = matrixArray || _getMatrix(target, true),
			xOriginOld = cache.xOrigin || 0,
			yOriginOld = cache.yOrigin || 0,
			xOffsetOld = cache.xOffset || 0,
			yOffsetOld = cache.yOffset || 0,
			a = matrix[0],
			b = matrix[1],
			c = matrix[2],
			d = matrix[3],
			tx = matrix[4],
			ty = matrix[5],
			originSplit = origin.split(" "),
			xOrigin = parseFloat(originSplit[0]) || 0,
			yOrigin = parseFloat(originSplit[1]) || 0,
			bounds, determinant, x, y;
		if (!originIsAbsolute) {
			bounds = _getBBox(target);
			xOrigin = bounds.x + (~originSplit[0].indexOf("%") ? xOrigin / 100 * bounds.width : xOrigin);
			yOrigin = bounds.y + (~((originSplit[1] || originSplit[0]).indexOf("%")) ? yOrigin / 100 * bounds.height : yOrigin);
		} else if (matrix !== _identity2DMatrix && (determinant = (a * d - b * c))) { //if it's zero (like if scaleX and scaleY are zero), skip it to avoid errors with dividing by zero.
			x = xOrigin * (d / determinant) + yOrigin * (-c / determinant) + ((c * ty - d * tx) / determinant);
			y = xOrigin * (-b / determinant) + yOrigin * (a / determinant) - ((a * ty - b * tx) / determinant);
			xOrigin = x;
			yOrigin = y;
		}
		if (smooth || (smooth !== false && cache.smooth)) {
			tx = xOrigin - xOriginOld;
			ty = yOrigin - yOriginOld;
			cache.xOffset = xOffsetOld + (tx * a + ty * c) - tx;
			cache.yOffset = yOffsetOld + (tx * b + ty * d) - ty;
		} else {
			cache.xOffset = cache.yOffset = 0;
		}
		cache.xOrigin = xOrigin;
		cache.yOrigin = yOrigin;
		cache.smooth = !!smooth;
		cache.origin = origin;
		cache.originIsAbsolute = !!originIsAbsolute;
		target.style[_transformOriginProp] = "0px 0px"; //otherwise, if someone sets  an origin via CSS, it will likely interfere with the SVG transform attribute ones (because remember, we're baking the origin into the matrix() value).
		if (pluginToAddPropTweensTo) {
			_addNonTweeningPT(pluginToAddPropTweensTo, cache, "xOrigin", xOriginOld, xOrigin);
			_addNonTweeningPT(pluginToAddPropTweensTo, cache, "yOrigin", yOriginOld, yOrigin);
			_addNonTweeningPT(pluginToAddPropTweensTo, cache, "xOffset", xOffsetOld, cache.xOffset);
			_addNonTweeningPT(pluginToAddPropTweensTo, cache, "yOffset", yOffsetOld, cache.yOffset);
		}
		target.setAttribute("data-svg-origin", xOrigin + " " + yOrigin);
	},
	_parseTransform = (target, uncache) => {
		let cache = target._gsap || new GSCache(target);
		if ("x" in cache && !uncache && !cache.uncache) {
			return cache;
		}
		let style = target.style,
			invertedScaleX = cache.scaleX < 0,
			px = "px",
			deg = "deg",
			cs = getComputedStyle(target),
			origin = _getComputedProperty(target, _transformOriginProp) || "0",
			x, y, z, scaleX, scaleY, rotation, rotationX, rotationY, skewX, skewY, perspective, xOrigin, yOrigin,
			matrix, angle, cos, sin, a, b, c, d, a12, a22, t1, t2, t3, a13, a23, a33, a42, a43, a32;
		x = y = z = rotation = rotationX = rotationY = skewX = skewY = perspective = 0;
		scaleX = scaleY = 1;
		cache.svg = !!(target.getCTM && _isSVG(target));

		if (cs.translate) { // accommodate independent transforms by combining them into normal ones.
			if (cs.translate !== "none" || cs.scale !== "none" || cs.rotate !== "none") {
				style[_transformProp] = (cs.translate !== "none" ? "translate3d(" + (cs.translate + " 0 0").split(" ").slice(0, 3).join(", ") + ") " : "") + (cs.rotate !== "none" ? "rotate(" + cs.rotate + ") " : "") + (cs.scale !== "none" ? "scale(" + cs.scale.split(" ").join(",") + ") " : "") + (cs[_transformProp] !== "none" ? cs[_transformProp] : "");
			}
			style.scale = style.rotate = style.translate = "none";
		}

		matrix = _getMatrix(target, cache.svg);
		if (cache.svg) {
			if (cache.uncache) { // if cache.uncache is true (and maybe if origin is 0,0), we need to set element.style.transformOrigin = (cache.xOrigin - bbox.x) + "px " + (cache.yOrigin - bbox.y) + "px". Previously we let the data-svg-origin stay instead, but when introducing revert(), it complicated things.
				t2 = target.getBBox();
				origin = (cache.xOrigin - t2.x) + "px " + (cache.yOrigin - t2.y) + "px";
				t1 = "";
			} else {
				t1 = !uncache && target.getAttribute("data-svg-origin"); //  Remember, to work around browser inconsistencies we always force SVG elements' transformOrigin to 0,0 and offset the translation accordingly.
			}
			_applySVGOrigin(target, t1 || origin, !!t1 || cache.originIsAbsolute, cache.smooth !== false, matrix);
		}
		xOrigin = cache.xOrigin || 0;
		yOrigin = cache.yOrigin || 0;
		if (matrix !== _identity2DMatrix) {
			a = matrix[0]; //a11
			b = matrix[1]; //a21
			c = matrix[2]; //a31
			d = matrix[3]; //a41
			x = a12 = matrix[4];
			y = a22 = matrix[5];

			//2D matrix
			if (matrix.length === 6) {
				scaleX = Math.sqrt(a * a + b * b);
				scaleY = Math.sqrt(d * d + c * c);
				rotation = (a || b) ? _atan2(b, a) * _RAD2DEG : 0; //note: if scaleX is 0, we cannot accurately measure rotation. Same for skewX with a scaleY of 0. Therefore, we default to the previously recorded value (or zero if that doesn't exist).
				skewX = (c || d) ? _atan2(c, d) * _RAD2DEG + rotation : 0;
				skewX && (scaleY *= Math.abs(Math.cos(skewX * _DEG2RAD)));
				if (cache.svg) {
					x -= xOrigin - (xOrigin * a + yOrigin * c);
					y -= yOrigin - (xOrigin * b + yOrigin * d);
				}

			//3D matrix
			} else {
				a32 = matrix[6];
				a42 = matrix[7];
				a13 = matrix[8];
				a23 = matrix[9];
				a33 = matrix[10];
				a43 = matrix[11];
				x = matrix[12];
				y = matrix[13];
				z = matrix[14];

				angle = _atan2(a32, a33);
				rotationX = angle * _RAD2DEG;
				//rotationX
				if (angle) {
					cos = Math.cos(-angle);
					sin = Math.sin(-angle);
					t1 = a12*cos+a13*sin;
					t2 = a22*cos+a23*sin;
					t3 = a32*cos+a33*sin;
					a13 = a12*-sin+a13*cos;
					a23 = a22*-sin+a23*cos;
					a33 = a32*-sin+a33*cos;
					a43 = a42*-sin+a43*cos;
					a12 = t1;
					a22 = t2;
					a32 = t3;
				}
				//rotationY
				angle = _atan2(-c, a33);
				rotationY = angle * _RAD2DEG;
				if (angle) {
					cos = Math.cos(-angle);
					sin = Math.sin(-angle);
					t1 = a*cos-a13*sin;
					t2 = b*cos-a23*sin;
					t3 = c*cos-a33*sin;
					a43 = d*sin+a43*cos;
					a = t1;
					b = t2;
					c = t3;
				}
				//rotationZ
				angle = _atan2(b, a);
				rotation = angle * _RAD2DEG;
				if (angle) {
					cos = Math.cos(angle);
					sin = Math.sin(angle);
					t1 = a*cos+b*sin;
					t2 = a12*cos+a22*sin;
					b = b*cos-a*sin;
					a22 = a22*cos-a12*sin;
					a = t1;
					a12 = t2;
				}

				if (rotationX && Math.abs(rotationX) + Math.abs(rotation) > 359.9) { //when rotationY is set, it will often be parsed as 180 degrees different than it should be, and rotationX and rotation both being 180 (it looks the same), so we adjust for that here.
					rotationX = rotation = 0;
					rotationY = 180 - rotationY;
				}
				scaleX = _round(Math.sqrt(a * a + b * b + c * c));
				scaleY = _round(Math.sqrt(a22 * a22 + a32 * a32));
				angle = _atan2(a12, a22);
				skewX = (Math.abs(angle) > 0.0002) ? angle * _RAD2DEG : 0;
				perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0;
			}

			if (cache.svg) { //sense if there are CSS transforms applied on an SVG element in which case we must overwrite them when rendering. The transform attribute is more reliable cross-browser, but we can't just remove the CSS ones because they may be applied in a CSS rule somewhere (not just inline).
				t1 = target.getAttribute("transform");
				cache.forceCSS = target.setAttribute("transform", "") || (!_isNullTransform(_getComputedProperty(target, _transformProp)));
				t1 && target.setAttribute("transform", t1);
			}
		}

		if (Math.abs(skewX) > 90 && Math.abs(skewX) < 270) {
			if (invertedScaleX) {
				scaleX *= -1;
				skewX += (rotation <= 0) ? 180 : -180;
				rotation += (rotation <= 0) ? 180 : -180;
			} else {
				scaleY *= -1;
				skewX += (skewX <= 0) ? 180 : -180;
			}
		}
		uncache = uncache || cache.uncache;
		cache.x = x - ((cache.xPercent = x && ((!uncache && cache.xPercent) || (Math.round(target.offsetWidth / 2) === Math.round(-x) ? -50 : 0))) ? target.offsetWidth * cache.xPercent / 100 : 0) + px;
		cache.y = y - ((cache.yPercent = y && ((!uncache && cache.yPercent) || (Math.round(target.offsetHeight / 2) === Math.round(-y) ? -50 : 0))) ? target.offsetHeight * cache.yPercent / 100 : 0) + px;
		cache.z = z + px;
		cache.scaleX = _round(scaleX);
		cache.scaleY = _round(scaleY);
		cache.rotation = _round(rotation) + deg;
		cache.rotationX = _round(rotationX) + deg;
		cache.rotationY = _round(rotationY) + deg;
		cache.skewX = skewX + deg;
		cache.skewY = skewY + deg;
		cache.transformPerspective = perspective + px;
		if ((cache.zOrigin = parseFloat(origin.split(" ")[2]) || 0)) {
			style[_transformOriginProp] = _firstTwoOnly(origin);
		}
		cache.xOffset = cache.yOffset = 0;
		cache.force3D = _config.force3D;
		cache.renderTransform = cache.svg ? _renderSVGTransforms : _supports3D ? _renderCSSTransforms : _renderNon3DTransforms;
		cache.uncache = 0;
		return cache;
	},
	_firstTwoOnly = value => (value = value.split(" "))[0] + " " + value[1], //for handling transformOrigin values, stripping out the 3rd dimension
	_addPxTranslate = (target, start, value) => {
		let unit = getUnit(start);
		return _round(parseFloat(start) + parseFloat(_convertToUnit(target, "x", value + "px", unit))) + unit;
	},
	_renderNon3DTransforms = (ratio, cache) => {
		cache.z = "0px";
		cache.rotationY = cache.rotationX = "0deg";
		cache.force3D = 0;
		_renderCSSTransforms(ratio, cache);
	},
	_zeroDeg = "0deg",
	_zeroPx = "0px",
	_endParenthesis = ") ",
	_renderCSSTransforms = function(ratio, cache) {
		let {xPercent, yPercent, x, y, z, rotation, rotationY, rotationX, skewX, skewY, scaleX, scaleY, transformPerspective, force3D, target, zOrigin} = cache || this,
			transforms = "",
			use3D = (force3D === "auto" && ratio && ratio !== 1) || force3D === true;

		// Safari has a bug that causes it not to render 3D transform-origin values properly, so we force the z origin to 0, record it in the cache, and then do the math here to offset the translate values accordingly (basically do the 3D transform-origin part manually)
		if (zOrigin && (rotationX !== _zeroDeg || rotationY !== _zeroDeg)) {
			let angle = parseFloat(rotationY) * _DEG2RAD,
				a13 = Math.sin(angle),
				a33 = Math.cos(angle),
				cos;
			angle = parseFloat(rotationX) * _DEG2RAD;
			cos = Math.cos(angle);
			x = _addPxTranslate(target, x, a13 * cos * -zOrigin);
			y = _addPxTranslate(target, y, -Math.sin(angle) * -zOrigin);
			z = _addPxTranslate(target, z, a33 * cos * -zOrigin + zOrigin);
		}

		if (transformPerspective !== _zeroPx) {
			transforms += "perspective(" + transformPerspective + _endParenthesis;
		}
		if (xPercent || yPercent) {
			transforms += "translate(" + xPercent + "%, " + yPercent + "%) ";
		}
		if (use3D || x !== _zeroPx || y !== _zeroPx || z !== _zeroPx) {
			transforms += (z !== _zeroPx || use3D) ? "translate3d(" + x + ", " + y + ", " + z + ") " : "translate(" + x + ", " + y + _endParenthesis;
		}
		if (rotation !== _zeroDeg) {
			transforms += "rotate(" + rotation + _endParenthesis;
		}
		if (rotationY !== _zeroDeg) {
			transforms += "rotateY(" + rotationY + _endParenthesis;
		}
		if (rotationX !== _zeroDeg) {
			transforms += "rotateX(" + rotationX + _endParenthesis;
		}
		if (skewX !== _zeroDeg || skewY !== _zeroDeg) {
			transforms += "skew(" + skewX + ", " + skewY + _endParenthesis;
		}
		if (scaleX !== 1 || scaleY !== 1) {
			transforms += "scale(" + scaleX + ", " + scaleY + _endParenthesis;
		}
		target.style[_transformProp] = transforms || "translate(0, 0)";
	},
	_renderSVGTransforms = function(ratio, cache) {
		let {xPercent, yPercent, x, y, rotation, skewX, skewY, scaleX, scaleY, target, xOrigin, yOrigin, xOffset, yOffset, forceCSS} = cache || this,
			tx = parseFloat(x),
			ty = parseFloat(y),
			a11, a21, a12, a22, temp;
		rotation = parseFloat(rotation);
		skewX = parseFloat(skewX);
		skewY = parseFloat(skewY);
		if (skewY) { //for performance reasons, we combine all skewing into the skewX and rotation values. Remember, a skewY of 10 degrees looks the same as a rotation of 10 degrees plus a skewX of 10 degrees.
			skewY = parseFloat(skewY);
			skewX += skewY;
			rotation += skewY;
		}
		if (rotation || skewX) {
			rotation *= _DEG2RAD;
			skewX *= _DEG2RAD;
			a11 = Math.cos(rotation) * scaleX;
			a21 = Math.sin(rotation) * scaleX;
			a12 = Math.sin(rotation - skewX) * -scaleY;
			a22 = Math.cos(rotation - skewX) * scaleY;
			if (skewX) {
				skewY *= _DEG2RAD;
				temp = Math.tan(skewX - skewY);
				temp = Math.sqrt(1 + temp * temp);
				a12 *= temp;
				a22 *= temp;
				if (skewY) {
					temp = Math.tan(skewY);
					temp = Math.sqrt(1 + temp * temp);
					a11 *= temp;
					a21 *= temp;
				}
			}
			a11 = _round(a11);
			a21 = _round(a21);
			a12 = _round(a12);
			a22 = _round(a22);
		} else {
			a11 = scaleX;
			a22 = scaleY;
			a21 = a12 = 0;
		}
		if ((tx && !~(x + "").indexOf("px")) || (ty && !~(y + "").indexOf("px"))) {
			tx = _convertToUnit(target, "x", x, "px");
			ty = _convertToUnit(target, "y", y, "px");
		}
		if (xOrigin || yOrigin || xOffset || yOffset) {
			tx = _round(tx + xOrigin - (xOrigin * a11 + yOrigin * a12) + xOffset);
			ty = _round(ty + yOrigin - (xOrigin * a21 + yOrigin * a22) + yOffset);
		}
		if (xPercent || yPercent) {
			//The SVG spec doesn't support percentage-based translation in the "transform" attribute, so we merge it into the translation to simulate it.
			temp = target.getBBox();
			tx = _round(tx + xPercent / 100 * temp.width);
			ty = _round(ty + yPercent / 100 * temp.height);
		}
		temp = "matrix(" + a11 + "," + a21 + "," + a12 + "," + a22 + "," + tx + "," + ty + ")";
		target.setAttribute("transform", temp);
		forceCSS && (target.style[_transformProp] = temp); //some browsers prioritize CSS transforms over the transform attribute. When we sense that the user has CSS transforms applied, we must overwrite them this way (otherwise some browser simply won't render the transform attribute changes!)
	},
	_addRotationalPropTween = function(plugin, target, property, startNum, endValue) {
		let cap = 360,
			isString = _isString(endValue),
			endNum = parseFloat(endValue) * ((isString && ~endValue.indexOf("rad")) ? _RAD2DEG : 1),
			change = endNum - startNum,
			finalValue = (startNum + change) + "deg",
			direction, pt;
		if (isString) {
			direction = endValue.split("_")[1];
			if (direction === "short") {
				change %= cap;
				if (change !== change % (cap / 2)) {
					change += (change < 0) ? cap : -cap;
				}
			}
			if (direction === "cw" && change < 0) {
				change = ((change + cap * _bigNum) % cap) - ~~(change / cap) * cap;
			} else if (direction === "ccw" && change > 0) {
				change = ((change - cap * _bigNum) % cap) - ~~(change / cap) * cap;
			}
		}
		plugin._pt = pt = new PropTween(plugin._pt, target, property, startNum, change, _renderPropWithEnd);
		pt.e = finalValue;
		pt.u = "deg";
		plugin._props.push(property);
		return pt;
	},
	_assign = (target, source) => { // Internet Explorer doesn't have Object.assign(), so we recreate it here.
		for (let p in source) {
			target[p] = source[p];
		}
		return target;
	},
	_addRawTransformPTs = (plugin, transforms, target) => { //for handling cases where someone passes in a whole transform string, like transform: "scale(2, 3) rotate(20deg) translateY(30em)"
		let startCache = _assign({}, target._gsap),
			exclude = "perspective,force3D,transformOrigin,svgOrigin",
			style = target.style,
			endCache, p, startValue, endValue, startNum, endNum, startUnit, endUnit;
		if (startCache.svg) {
			startValue = target.getAttribute("transform");
			target.setAttribute("transform", "");
			style[_transformProp] = transforms;
			endCache = _parseTransform(target, 1);
			_removeProperty(target, _transformProp);
			target.setAttribute("transform", startValue);
		} else {
			startValue = getComputedStyle(target)[_transformProp];
			style[_transformProp] = transforms;
			endCache = _parseTransform(target, 1);
			style[_transformProp] = startValue;
		}
		for (p in _transformProps) {
			startValue = startCache[p];
			endValue = endCache[p];
			if (startValue !== endValue && exclude.indexOf(p) < 0) { //tweening to no perspective gives very unintuitive results - just keep the same perspective in that case.
				startUnit = getUnit(startValue);
				endUnit = getUnit(endValue);
				startNum = (startUnit !== endUnit) ? _convertToUnit(target, p, startValue, endUnit) : parseFloat(startValue);
				endNum = parseFloat(endValue);
				plugin._pt = new PropTween(plugin._pt, endCache, p, startNum, endNum - startNum, _renderCSSProp);
				plugin._pt.u = endUnit || 0;
				plugin._props.push(p);
			}
		}
		_assign(endCache, startCache);
	};

// handle splitting apart padding, margin, borderWidth, and borderRadius into their 4 components. Firefox, for example, won't report borderRadius correctly - it will only do borderTopLeftRadius and the other corners. We also want to handle paddingTop, marginLeft, borderRightWidth, etc.
_forEachName("padding,margin,Width,Radius", (name, index) => {
	let t = "Top",
		r = "Right",
		b = "Bottom",
		l = "Left",
		props = (index < 3 ? [t,r,b,l] : [t+l, t+r, b+r, b+l]).map(side => index < 2 ? name + side : "border" + side + name);
	_specialProps[(index > 1 ? "border" + name : name)] = function(plugin, target, property, endValue, tween) {
		let a, vars;
		if (arguments.length < 4) { // getter, passed target, property, and unit (from _get())
			a = props.map(prop => _get(plugin, prop, property));
			vars = a.join(" ");
			return vars.split(a[0]).length === 5 ? a[0] : vars;
		}
		a = (endValue + "").split(" ");
		vars = {};
		props.forEach((prop, i) => vars[prop] = a[i] = a[i] || a[(((i - 1) / 2) | 0)]);
		plugin.init(target, vars, tween);
	}
});


export const CSSPlugin = {
	name: "css",
	register: _initCore,
	targetTest(target) {
		return target.style && target.nodeType;
	},
	init(target, vars, tween, index, targets) {
		let props = this._props,
			style = target.style,
			startAt = tween.vars.startAt,
			startValue, endValue, endNum, startNum, type, specialProp, p, startUnit, endUnit, relative, isTransformRelated, transformPropTween, cache, smooth, hasPriority, inlineProps;
		_pluginInitted || _initCore();
		// we may call init() multiple times on the same plugin instance, like when adding special properties, so make sure we don't overwrite the revert data or inlineProps
		this.styles = this.styles || _getStyleSaver(target);
		inlineProps = this.styles.props;
		this.tween = tween;
		for (p in vars) {
			if (p === "autoRound") {
				continue;
			}
			endValue = vars[p];
			if (_plugins[p] && _checkPlugin(p, vars, tween, index, target, targets)) { // plugins
				continue;
			}
			type = typeof(endValue);
			specialProp = _specialProps[p];
			if (type === "function") {
				endValue = endValue.call(tween, index, target, targets);
				type = typeof(endValue);
			}
			if (type === "string" && ~endValue.indexOf("random(")) {
				endValue = _replaceRandom(endValue);
			}
			if (specialProp) {
				specialProp(this, target, p, endValue, tween) && (hasPriority = 1);
			} else if (p.substr(0,2) === "--") { //CSS variable
				startValue = (getComputedStyle(target).getPropertyValue(p) + "").trim();
				endValue += "";
				_colorExp.lastIndex = 0;
				if (!_colorExp.test(startValue)) { // colors don't have units
					startUnit = getUnit(startValue);
					endUnit = getUnit(endValue);
				}
				endUnit ? startUnit !== endUnit && (startValue = _convertToUnit(target, p, startValue, endUnit) + endUnit) : startUnit && (endValue += startUnit);
				this.add(style, "setProperty", startValue, endValue, index, targets, 0, 0, p);
				props.push(p);
				inlineProps.push(p, 0, style[p]);
			} else if (type !== "undefined") {
				if (startAt && p in startAt) { // in case someone hard-codes a complex value as the start, like top: "calc(2vh / 2)". Without this, it'd use the computed value (always in px)
					startValue = typeof(startAt[p]) === "function" ? startAt[p].call(tween, index, target, targets) : startAt[p];
					_isString(startValue) && ~startValue.indexOf("random(") && (startValue = _replaceRandom(startValue));
					getUnit(startValue + "") || (startValue += _config.units[p] || getUnit(_get(target, p)) || ""); // for cases when someone passes in a unitless value like {x: 100}; if we try setting translate(100, 0px) it won't work.
					(startValue + "").charAt(1) === "=" && (startValue = _get(target, p)); // can't work with relative values
				} else {
					startValue = _get(target, p);
				}
				startNum = parseFloat(startValue);
				relative = (type === "string" && endValue.charAt(1) === "=") && endValue.substr(0, 2);
				relative && (endValue = endValue.substr(2));
				endNum = parseFloat(endValue);
				if (p in _propertyAliases) {
					if (p === "autoAlpha") { //special case where we control the visibility along with opacity. We still allow the opacity value to pass through and get tweened.
						if (startNum === 1 && _get(target, "visibility") === "hidden" && endNum) { //if visibility is initially set to "hidden", we should interpret that as intent to make opacity 0 (a convenience)
							startNum = 0;
						}
						inlineProps.push("visibility", 0, style.visibility);
						_addNonTweeningPT(this, style, "visibility", startNum ? "inherit" : "hidden", endNum ? "inherit" : "hidden", !endNum);
					}
					if (p !== "scale" && p !== "transform") {
						p = _propertyAliases[p];
						~p.indexOf(",") && (p = p.split(",")[0]);
					}
				}

				isTransformRelated = (p in _transformProps);

				//--- TRANSFORM-RELATED ---
				if (isTransformRelated) {
					this.styles.save(p);
					if (!transformPropTween) {
						cache = target._gsap;
						(cache.renderTransform && !vars.parseTransform) || _parseTransform(target, vars.parseTransform); // if, for example, gsap.set(... {transform:"translateX(50vw)"}), the _get() call doesn't parse the transform, thus cache.renderTransform won't be set yet so force the parsing of the transform here.
						smooth = (vars.smoothOrigin !== false && cache.smooth);
						transformPropTween = this._pt = new PropTween(this._pt, style, _transformProp, 0, 1, cache.renderTransform, cache, 0, -1); //the first time through, create the rendering PropTween so that it runs LAST (in the linked list, we keep adding to the beginning)
						transformPropTween.dep = 1; //flag it as dependent so that if things get killed/overwritten and this is the only PropTween left, we can safely kill the whole tween.
					}
					if (p === "scale") {
						this._pt = new PropTween(this._pt, cache, "scaleY", cache.scaleY, ((relative ? _parseRelative(cache.scaleY, relative + endNum) : endNum) - cache.scaleY) || 0, _renderCSSProp);
						this._pt.u = 0;
						props.push("scaleY", p);
						p += "X";
					} else if (p === "transformOrigin") {
						inlineProps.push(_transformOriginProp, 0, style[_transformOriginProp]);
						endValue = _convertKeywordsToPercentages(endValue); //in case something like "left top" or "bottom right" is passed in. Convert to percentages.
						if (cache.svg) {
							_applySVGOrigin(target, endValue, 0, smooth, 0, this);
						} else {
							endUnit = parseFloat(endValue.split(" ")[2]) || 0; //handle the zOrigin separately!
							endUnit !== cache.zOrigin && _addNonTweeningPT(this, cache, "zOrigin", cache.zOrigin, endUnit);
							_addNonTweeningPT(this, style, p, _firstTwoOnly(startValue), _firstTwoOnly(endValue));
						}
						continue;
					} else if (p === "svgOrigin") {
						_applySVGOrigin(target, endValue, 1, smooth, 0, this);
						continue;
					} else if (p in _rotationalProperties) {
						_addRotationalPropTween(this, cache, p, startNum, relative ? _parseRelative(startNum, relative + endValue) : endValue);
						continue;

					} else if (p === "smoothOrigin") {
						_addNonTweeningPT(this, cache, "smooth", cache.smooth, endValue);
						continue;
					} else if (p === "force3D") {
						cache[p] = endValue;
						continue;
					} else if (p === "transform") {
						_addRawTransformPTs(this, endValue, target);
						continue;
					}
				} else if (!(p in style)) {
					p = _checkPropPrefix(p) || p;
				}

				if (isTransformRelated || ((endNum || endNum === 0) && (startNum || startNum === 0) && !_complexExp.test(endValue) && (p in style))) {
					startUnit = (startValue + "").substr((startNum + "").length);
					endNum || (endNum = 0); // protect against NaN
					endUnit = getUnit(endValue) || ((p in _config.units) ? _config.units[p] : startUnit);
					startUnit !== endUnit && (startNum = _convertToUnit(target, p, startValue, endUnit));
					this._pt = new PropTween(this._pt, isTransformRelated ? cache : style, p, startNum, (relative ? _parseRelative(startNum, relative + endNum) : endNum) - startNum, (!isTransformRelated && (endUnit === "px" || p === "zIndex") && vars.autoRound !== false) ? _renderRoundedCSSProp : _renderCSSProp);
					this._pt.u = endUnit || 0;
					if (startUnit !== endUnit && endUnit !== "%") { //when the tween goes all the way back to the beginning, we need to revert it to the OLD/ORIGINAL value (with those units). We record that as a "b" (beginning) property and point to a render method that handles that. (performance optimization)
						this._pt.b = startValue;
						this._pt.r = _renderCSSPropWithBeginning;
					}
				} else if (!(p in style)) {
					if (p in target) { //maybe it's not a style - it could be a property added directly to an element in which case we'll try to animate that.
						this.add(target, p, startValue || target[p], relative ? relative + endValue : endValue, index, targets);
					} else if (p !== "parseTransform") {
						_missingPlugin(p, endValue);
						continue;
					}
				} else {
					_tweenComplexCSSString.call(this, target, p, startValue, relative ? relative + endValue : endValue);
				}
				isTransformRelated || (p in style ? inlineProps.push(p, 0, style[p]) : inlineProps.push(p, 1, startValue || target[p]));
				props.push(p);
			}
		}
		hasPriority && _sortPropTweensByPriority(this);

	},
	render(ratio, data) {
		if (data.tween._time || !_reverting()) {
			let pt = data._pt;
			while (pt) {
				pt.r(ratio, pt.d);
				pt = pt._next;
			}
		} else {
			data.styles.revert();
		}
	},
	get: _get,
	aliases: _propertyAliases,
	getSetter(target, property, plugin) { //returns a setter function that accepts target, property, value and applies it accordingly. Remember, properties like "x" aren't as simple as target.style.property = value because they've got to be applied to a proxy object and then merged into a transform string in a renderer.
		let p = _propertyAliases[property];
		(p && p.indexOf(",") < 0) && (property = p);
		return (property in _transformProps && property !== _transformOriginProp && (target._gsap.x || _get(target, "x"))) ? (plugin && _recentSetterPlugin === plugin ? (property === "scale" ? _setterScale : _setterTransform) : (_recentSetterPlugin = plugin || {}) && (property === "scale" ? _setterScaleWithRender : _setterTransformWithRender)) : target.style && !_isUndefined(target.style[property]) ? _setterCSSStyle : ~property.indexOf("-") ? _setterCSSProp : _getSetter(target, property);
	},
	core: { _removeProperty, _getMatrix }

};

gsap.utils.checkPrefix = _checkPropPrefix;
gsap.core.getStyleSaver = _getStyleSaver;
(function(positionAndScale, rotation, others, aliases) {
	let all = _forEachName(positionAndScale + "," + rotation + "," + others, name => {_transformProps[name] = 1});
	_forEachName(rotation, name => {_config.units[name] = "deg"; _rotationalProperties[name] = 1});
	_propertyAliases[all[13]] = positionAndScale + "," + rotation;
	_forEachName(aliases, name => {
		let split = name.split(":");
		_propertyAliases[split[1]] = all[split[0]];
	});
})("x,y,z,scale,scaleX,scaleY,xPercent,yPercent", "rotation,rotationX,rotationY,skewX,skewY", "transform,transformOrigin,svgOrigin,force3D,smoothOrigin,transformPerspective", "0:translateX,1:translateY,2:translateZ,8:rotate,8:rotationZ,8:rotateZ,9:rotateX,10:rotateY");
_forEachName("x,y,z,top,right,bottom,left,width,height,fontSize,padding,margin,perspective", name => {_config.units[name] = "px"});

gsap.registerPlugin(CSSPlugin);

export { CSSPlugin as default, _getBBox, _createElement, _checkPropPrefix as checkPrefix };