diff options
Diffstat (limited to 'addons/web_editor/static/src/js/common/utils.js')
| -rw-r--r-- | addons/web_editor/static/src/js/common/utils.js | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/addons/web_editor/static/src/js/common/utils.js b/addons/web_editor/static/src/js/common/utils.js new file mode 100644 index 00000000..1cd10318 --- /dev/null +++ b/addons/web_editor/static/src/js/common/utils.js @@ -0,0 +1,266 @@ +odoo.define('web_editor.utils', function (require) { +'use strict'; + +const {ColorpickerWidget} = require('web.Colorpicker'); + +/** + * window.getComputedStyle cannot work properly with CSS shortcuts (like + * 'border-width' which is a shortcut for the top + right + bottom + left border + * widths. If an option wants to customize such a shortcut, it should be listed + * here with the non-shortcuts property it stands for, in order. + * + * @type {Object<string[]>} + */ +const CSS_SHORTHANDS = { + 'border-width': ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'], + 'border-radius': ['border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'], + 'border-color': ['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'], + 'border-style': ['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'], +}; +/** + * Key-value mapping to list converters from an unit A to an unit B. + * - The key is a string in the format '$1-$2' where $1 is the CSS symbol of + * unit A and $2 is the CSS symbol of unit B. + * - The value is a function that converts the received value (expressed in + * unit A) to another value expressed in unit B. Two other parameters is + * received: the css property on which the unit applies and the jQuery element + * on which that css property may change. + */ +const CSS_UNITS_CONVERSION = { + 's-ms': () => 1000, + 'ms-s': () => 0.001, + 'rem-px': () => _computePxByRem(), + 'px-rem': () => _computePxByRem(true), +}; +/** + * Colors of the default palette, used for substitution in shapes/illustrations. + * key: number of the color in the palette (ie, o-color-<1-5>) + * value: color hex code + */ +const DEFAULT_PALETTE = { + '1': '#3AADAA', + '2': '#7C6576', + '3': '#F6F6F6', + '4': '#FFFFFF', + '5': '#383E45', +}; + +/** + * Computes the number of "px" needed to make a "rem" unit. Subsequent calls + * returns the cached computed value. + * + * @param {boolean} [toRem=false] + * @returns {float} - number of px by rem if 'toRem' is false + * - the inverse otherwise + */ +function _computePxByRem(toRem) { + if (_computePxByRem.PX_BY_REM === undefined) { + const htmlStyle = window.getComputedStyle(document.documentElement); + _computePxByRem.PX_BY_REM = parseFloat(htmlStyle['font-size']); + } + return toRem ? (1 / _computePxByRem.PX_BY_REM) : _computePxByRem.PX_BY_REM; +} +/** + * Converts the given (value + unit) string to a numeric value expressed in + * the other given css unit. + * + * e.g. fct('400ms', 's') -> 0.4 + * + * @param {string} value + * @param {string} unitTo + * @param {string} [cssProp] - the css property on which the unit applies + * @param {jQuery} [$target] - the jQuery element on which that css property + * may change + * @returns {number} + */ +function _convertValueToUnit(value, unitTo, cssProp, $target) { + const m = _getNumericAndUnit(value); + if (!m) { + return NaN; + } + const numValue = parseFloat(m[0]); + const valueUnit = m[1]; + return _convertNumericToUnit(numValue, valueUnit, unitTo, cssProp, $target); +} +/** + * Converts the given numeric value expressed in the given css unit into + * the corresponding numeric value expressed in the other given css unit. + * + * e.g. fct(400, 'ms', 's') -> 0.4 + * + * @param {number} value + * @param {string} unitFrom + * @param {string} unitTo + * @param {string} [cssProp] - the css property on which the unit applies + * @param {jQuery} [$target] - the jQuery element on which that css property + * may change + * @returns {number} + */ +function _convertNumericToUnit(value, unitFrom, unitTo, cssProp, $target) { + if (Math.abs(value) < Number.EPSILON || unitFrom === unitTo) { + return value; + } + const converter = CSS_UNITS_CONVERSION[`${unitFrom}-${unitTo}`]; + if (converter === undefined) { + throw new Error(`Cannot convert '${unitFrom}' units into '${unitTo}' units !`); + } + return value * converter(cssProp, $target); +} +/** + * Returns the numeric value and unit of a css value. + * + * e.g. fct('400ms') -> [400, 'ms'] + * + * @param {string} value + * @returns {Array|null} + */ +function _getNumericAndUnit(value) { + const m = value.trim().match(/^(-?[0-9.]+)([A-Za-z% -]*)$/); + if (!m) { + return null; + } + return [m[1].trim(), m[2].trim()]; +} +/** + * Checks if two css values are equal. + * + * @param {string} value1 + * @param {string} value2 + * @param {string} [cssProp] - the css property on which the unit applies + * @param {jQuery} [$target] - the jQuery element on which that css property + * may change + * @returns {boolean} + */ +function _areCssValuesEqual(value1, value2, cssProp, $target) { + // String comparison first + if (value1 === value2) { + return true; + } + + // It could be a CSS variable, in that case the actual value has to be + // retrieved before comparing. + if (value1.startsWith('var(--')) { + value1 = _getCSSVariableValue(value1.substring(6, value1.length - 1)); + } + if (value2.startsWith('var(--')) { + value2 = _getCSSVariableValue(value2.substring(6, value2.length - 1)); + } + if (value1 === value2) { + return true; + } + + // They may be colors, normalize then re-compare the resulting string + const color1 = ColorpickerWidget.normalizeCSSColor(value1); + const color2 = ColorpickerWidget.normalizeCSSColor(value2); + if (color1 === color2) { + return true; + } + + // Convert the second value in the unit of the first one and compare + // floating values + const data = _getNumericAndUnit(value1); + if (!data) { + return false; + } + const numValue1 = data[0]; + const numValue2 = _convertValueToUnit(value2, data[1], cssProp, $target); + return (Math.abs(numValue1 - numValue2) < Number.EPSILON); +} +/** + * @param {string|number} name + * @returns {boolean} + */ +function _isColorCombinationName(name) { + const number = parseInt(name); + return (!isNaN(number) && number % 100 !== 0); +} +/** + * @param {string[]} colorNames + * @param {string} [prefix='bg-'] + * @returns {string[]} + */ +function _computeColorClasses(colorNames, prefix = 'bg-') { + let hasCCClasses = false; + const isBgPrefix = (prefix === 'bg-'); + const classes = colorNames.map(c => { + if (isBgPrefix && _isColorCombinationName(c)) { + hasCCClasses = true; + return `o_cc${c}`; + } + return (prefix + c); + }); + if (hasCCClasses) { + classes.push('o_cc'); + } + return classes; +} +/** + * @param {string} key + * @param {CSSStyleDeclaration} [htmlStyle] if not provided, it is computed + * @returns {string} + */ +function _getCSSVariableValue(key, htmlStyle) { + if (htmlStyle === undefined) { + htmlStyle = window.getComputedStyle(document.documentElement); + } + // Get trimmed value from the HTML element + let value = htmlStyle.getPropertyValue(`--${key}`).trim(); + // If it is a color value, it needs to be normalized + value = ColorpickerWidget.normalizeCSSColor(value); + // Normally scss-string values are "printed" single-quoted. That way no + // magic conversation is needed when customizing a variable: either save it + // quoted for strings or non quoted for colors, numbers, etc. However, + // Chrome has the annoying behavior of changing the single-quotes to + // double-quotes when reading them through getPropertyValue... + return value.replace(/"/g, "'"); +} +/** + * Normalize a color in case it is a variable name so it can be used outside of + * css. + * + * @param {string} color the color to normalize into a css value + * @returns {string} the normalized color + */ +function _normalizeColor(color) { + if (ColorpickerWidget.isCSSColor(color)) { + return color; + } + return _getCSSVariableValue(color); +} +/** + * Parse an element's background-image's url. + * + * @param {string} string a css value in the form 'url("...")' + * @returns {string|false} the src of the image or false if not parsable + */ +function _getBgImageURL(el) { + const string = $(el).css('background-image'); + const match = string.match(/^url\((['"])(.*?)\1\)$/); + if (!match) { + return ''; + } + const matchedURL = match[2]; + // Make URL relative if possible + const fullURL = new URL(matchedURL, window.location.origin); + if (fullURL.origin === window.location.origin) { + return fullURL.href.slice(fullURL.origin.length); + } + return matchedURL; +} + +return { + CSS_SHORTHANDS: CSS_SHORTHANDS, + CSS_UNITS_CONVERSION: CSS_UNITS_CONVERSION, + DEFAULT_PALETTE: DEFAULT_PALETTE, + computePxByRem: _computePxByRem, + convertValueToUnit: _convertValueToUnit, + convertNumericToUnit: _convertNumericToUnit, + getNumericAndUnit: _getNumericAndUnit, + areCssValuesEqual: _areCssValuesEqual, + isColorCombinationName: _isColorCombinationName, + computeColorClasses: _computeColorClasses, + getCSSVariableValue: _getCSSVariableValue, + normalizeColor: _normalizeColor, + getBgImageURL: _getBgImageURL, +}; +}); |
