summaryrefslogtreecommitdiff
path: root/addons/web_editor/static/src/js/common/utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web_editor/static/src/js/common/utils.js')
-rw-r--r--addons/web_editor/static/src/js/common/utils.js266
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,
+};
+});