"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _convertUnit = _interopRequireDefault(require("./convertUnit")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function isValueType(type) { switch (type) { case 'LengthValue': case 'AngleValue': case 'TimeValue': case 'FrequencyValue': case 'ResolutionValue': case 'EmValue': case 'ExValue': case 'ChValue': case 'RemValue': case 'VhValue': case 'VwValue': case 'VminValue': case 'VmaxValue': case 'PercentageValue': case 'Number': return true; } return false; } function flip(operator) { return operator === '+' ? '-' : '+'; } function isAddSubOperator(operator) { return operator === '+' || operator === '-'; } function collectAddSubItems(preOperator, node, collected, precision) { if (!isAddSubOperator(preOperator)) { throw new Error(`invalid operator ${preOperator}`); } var type = node.type; if (isValueType(type)) { var itemIndex = collected.findIndex(function (x) { return x.node.type === type; }); if (itemIndex >= 0) { if (node.value === 0) { return; } var _covertNodesUnits = covertNodesUnits(collected[itemIndex].node, node, precision), reducedNode = _covertNodesUnits.left, current = _covertNodesUnits.right; if (collected[itemIndex].preOperator === '-') { collected[itemIndex].preOperator = '+'; reducedNode.value *= -1; } if (preOperator === "+") { reducedNode.value += current.value; } else { reducedNode.value -= current.value; } // make sure reducedNode.value >= 0 if (reducedNode.value >= 0) { collected[itemIndex] = { node: reducedNode, preOperator: '+' }; } else { reducedNode.value *= -1; collected[itemIndex] = { node: reducedNode, preOperator: '-' }; } } else { // make sure node.value >= 0 if (node.value >= 0) { collected.push({ node, preOperator }); } else { node.value *= -1; collected.push({ node, preOperator: flip(preOperator) }); } } } else if (type === "MathExpression") { if (isAddSubOperator(node.operator)) { collectAddSubItems(preOperator, node.left, collected, precision); var collectRightOperator = preOperator === '-' ? flip(node.operator) : node.operator; collectAddSubItems(collectRightOperator, node.right, collected, precision); } else { // * or / var _reducedNode = reduce(node, precision); // prevent infinite recursive call if (_reducedNode.type !== "MathExpression" || isAddSubOperator(_reducedNode.operator)) { collectAddSubItems(preOperator, _reducedNode, collected, precision); } else { collected.push({ node: _reducedNode, preOperator }); } } } else { collected.push({ node, preOperator }); } } function reduceAddSubExpression(node, precision) { var collected = []; collectAddSubItems('+', node, collected, precision); var withoutZeroItem = collected.filter(function (item) { return !(isValueType(item.node.type) && item.node.value === 0); }); var firstNonZeroItem = withoutZeroItem[0]; // could be undefined // prevent producing "calc(-var(--a))" or "calc()" // which is invalid css if (!firstNonZeroItem || firstNonZeroItem.preOperator === '-' && !isValueType(firstNonZeroItem.node.type)) { var firstZeroItem = collected.find(function (item) { return isValueType(item.node.type) && item.node.value === 0; }); withoutZeroItem.unshift(firstZeroItem); } // make sure the preOperator of the first item is + if (withoutZeroItem[0].preOperator === '-' && isValueType(withoutZeroItem[0].node.type)) { withoutZeroItem[0].node.value *= -1; withoutZeroItem[0].preOperator = '+'; } var root = withoutZeroItem[0].node; for (var i = 1; i < withoutZeroItem.length; i++) { root = { type: 'MathExpression', operator: withoutZeroItem[i].preOperator, left: root, right: withoutZeroItem[i].node }; } return root; } function reduceDivisionExpression(node) { if (!isValueType(node.right.type)) { return node; } if (node.right.type !== 'Number') { throw new Error(`Cannot divide by "${node.right.unit}", number expected`); } return applyNumberDivision(node.left, node.right.value); } // apply (expr) / number function applyNumberDivision(node, divisor) { if (divisor === 0) { throw new Error('Cannot divide by zero'); } if (isValueType(node.type)) { node.value /= divisor; return node; } if (node.type === "MathExpression" && isAddSubOperator(node.operator)) { // turn (a + b) / num into a/num + b/num // is good for further reduction // checkout the test case // "should reduce division before reducing additions" return { type: "MathExpression", operator: node.operator, left: applyNumberDivision(node.left, divisor), right: applyNumberDivision(node.right, divisor) }; } // it is impossible to reduce it into a single value // .e.g the node contains css variable // so we just preserve the division and let browser do it return { type: "MathExpression", operator: '/', left: node, right: { type: "Number", value: divisor } }; } function reduceMultiplicationExpression(node) { // (expr) * number if (node.right.type === 'Number') { return applyNumberMultiplication(node.left, node.right.value); } // number * (expr) if (node.left.type === 'Number') { return applyNumberMultiplication(node.right, node.left.value); } return node; } // apply (expr) / number function applyNumberMultiplication(node, multiplier) { if (isValueType(node.type)) { node.value *= multiplier; return node; } if (node.type === "MathExpression" && isAddSubOperator(node.operator)) { // turn (a + b) * num into a*num + b*num // is good for further reduction // checkout the test case // "should reduce multiplication before reducing additions" return { type: "MathExpression", operator: node.operator, left: applyNumberMultiplication(node.left, multiplier), right: applyNumberMultiplication(node.right, multiplier) }; } // it is impossible to reduce it into a single value // .e.g the node contains css variable // so we just preserve the division and let browser do it return { type: "MathExpression", operator: '*', left: node, right: { type: "Number", value: multiplier } }; } function covertNodesUnits(left, right, precision) { switch (left.type) { case 'LengthValue': case 'AngleValue': case 'TimeValue': case 'FrequencyValue': case 'ResolutionValue': if (right.type === left.type && right.unit && left.unit) { var converted = (0, _convertUnit.default)(right.value, right.unit, left.unit, precision); right = { type: left.type, value: converted, unit: left.unit }; } return { left, right }; default: return { left, right }; } } function reduce(node, precision) { if (node.type === "MathExpression") { if (isAddSubOperator(node.operator)) { // reduceAddSubExpression will call reduce recursively return reduceAddSubExpression(node, precision); } node.left = reduce(node.left, precision); node.right = reduce(node.right, precision); switch (node.operator) { case "/": return reduceDivisionExpression(node, precision); case "*": return reduceMultiplicationExpression(node, precision); } return node; } return node; } var _default = reduce; exports.default = _default; module.exports = exports.default;