diff --git a/src/core/consts.js b/src/core/consts.js index 5fed2fd71..166d67eba 100644 --- a/src/core/consts.js +++ b/src/core/consts.js @@ -113,3 +113,4 @@ export const lowerCaseRgx = /([a-z])([A-Z])/g; export const transformsExecRgx = /(\w+)(\([^)]+\)+)/g; // Match inline transforms with cacl() values, returns the value wrapped in () export const relativeValuesExecRgx = /(\*=|\+=|-=)/; export const cssVariableMatchRgx = /var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/; +export const cssUrlFunctionRgx = /url\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi; diff --git a/src/core/values.js b/src/core/values.js index aa3f01743..5c30b9f50 100644 --- a/src/core/values.js +++ b/src/core/values.js @@ -5,6 +5,7 @@ import { valueTypes, digitWithExponentRgx, unitsExecRgx, + cssUrlFunctionRgx, isDomSymbol, isSvgSymbol, proxyTargetSymbol, @@ -162,6 +163,42 @@ export const createDecomposedValueTargetObject = () => { } } +/** + * @param {String} str + * @param {TweenDecomposedValue} targetObject + * @return {TweenDecomposedValue} + */ +const decomposeComplexStringValue = (str, targetObject) => { + const numbers = targetObject.d; + const strings = targetObject.s; + let lastIndex = 0; + + const appendParsedSegment = segment => { + const matchedNumbers = segment.match(digitWithExponentRgx); + if (matchedNumbers) { + const splitStrings = segment.split(digitWithExponentRgx); + strings[strings.length - 1] += splitStrings[0]; + for (let i = 0, l = matchedNumbers.length; i < l; i++) { + numbers.push(+matchedNumbers[i]); + strings.push(splitStrings[i + 1]); + } + } else { + strings[strings.length - 1] += segment; + } + } + + strings.push(emptyString); + str.replace(cssUrlFunctionRgx, (urlToken, offset) => { + appendParsedSegment(str.slice(lastIndex, offset)); + strings[strings.length - 1] += urlToken; + lastIndex = offset + urlToken.length; + return urlToken; + }); + appendParsedSegment(str.slice(lastIndex)); + + return targetObject; +} + /** * @param {String|Number} rawValue * @param {TweenDecomposedValue} targetObject @@ -208,11 +245,10 @@ export const decomposeRawValue = (rawValue, targetObject) => { return targetObject; } else { // Is a more complex string (generally svg coords, calc() or filters CSS values) - const matchedNumbers = str.match(digitWithExponentRgx); targetObject.t = valueTypes.COMPLEX; - targetObject.d = matchedNumbers ? matchedNumbers.map(Number) : []; - targetObject.s = str.split(digitWithExponentRgx) || []; - return targetObject; + targetObject.d = []; + targetObject.s = []; + return decomposeComplexStringValue(str, targetObject); } } } diff --git a/tests/suites/utils.test.js b/tests/suites/utils.test.js index 6223aa7b4..d49642716 100644 --- a/tests/suites/utils.test.js +++ b/tests/suites/utils.test.js @@ -115,6 +115,17 @@ suite('Utils', () => { expect(utils.get('.with-inline-styles', 'width')).to.equal('42px'); }); + test('Set CSS background image URL strings', () => { + const imageUrl = 'https://cdn.advoxel.com/60b03fea860898c1125db547/feeds/1f946da9-3b60-4d74-bc38-45c1713ab0e2/assets/300x250_crea1.jpg'; + const $target = document.querySelector('#target-id'); + + utils.set($target, { + backgroundImage: `url(${imageUrl})`, + }); + + expect($target.style.backgroundImage).to.equal(`url("${imageUrl}")`); + }); + test('Get CSS transforms', () => { utils.set(['#target-id', '.with-inline-transforms'], { translateX: 41,