diff options
Diffstat (limited to 'node_modules/reduce-css-calc/index.js')
| -rwxr-xr-x | node_modules/reduce-css-calc/index.js | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/node_modules/reduce-css-calc/index.js b/node_modules/reduce-css-calc/index.js new file mode 100755 index 00000000..3ec72471 --- /dev/null +++ b/node_modules/reduce-css-calc/index.js @@ -0,0 +1,164 @@ +/** + * Module dependencies + */ +var balanced = require("balanced-match") +var reduceFunctionCall = require("reduce-function-call") +var mexp = require("math-expression-evaluator") + +/** + * Constantes + */ +var MAX_STACK = 100 // should be enough for a single calc()... +var NESTED_CALC_RE = /(\+|\-|\*|\\|[^a-z]|)(\s*)(\()/g + +/** + * Global variables + */ +var stack + +/** + * Expose reduceCSSCalc plugin + * + * @type {Function} + */ +module.exports = reduceCSSCalc + +/** + * Reduce CSS calc() in a string, whenever it's possible + * + * @param {String} value css input + */ +function reduceCSSCalc(value, decimalPrecision) { + stack = 0 + decimalPrecision = Math.pow(10, decimalPrecision === undefined ? 5 : decimalPrecision) + + // Allow calc() on multiple lines + value = value.replace(/\n+/g, " ") + + /** + * Evaluates an expression + * + * @param {String} expression + * @returns {String} + */ + function evaluateExpression (expression, functionIdentifier, call) { + if (stack++ > MAX_STACK) { + stack = 0 + throw new Error("Call stack overflow for " + call) + } + + if (expression === "") { + throw new Error(functionIdentifier + "(): '" + call + "' must contain a non-whitespace string") + } + + expression = evaluateNestedExpression(expression, call) + + var units = getUnitsInExpression(expression) + + // If the expression contains multiple units or CSS variables, + // then let the expression be (i.e. browser calc()) + if (units.length > 1 || expression.indexOf("var(") > -1) { + return functionIdentifier + "(" + expression + ")" + } + + var unit = units[0] || "" + + if (unit === "%") { + // Convert percentages to numbers, to handle expressions like: 50% * 50% (will become: 25%): + // console.log(expression) + expression = expression.replace(/\b[0-9\.]+%/g, function(percent) { + return parseFloat(percent.slice(0, -1)) * 0.01 + }) + } + + // Remove units in expression: + var toEvaluate = expression.replace(new RegExp(unit, "gi"), "") + var result + + try { + result = mexp.eval(toEvaluate) + } + catch (e) { + return functionIdentifier + "(" + expression + ")" + } + + // Transform back to a percentage result: + if (unit === "%") { + result *= 100 + } + + // adjust rounding shit + // (0.1 * 0.2 === 0.020000000000000004) + if (functionIdentifier.length || unit === "%") { + result = Math.round(result * decimalPrecision) / decimalPrecision + } + + // Add unit + result += unit + + return result + } + + /** + * Evaluates nested expressions + * + * @param {String} expression + * @returns {String} + */ + function evaluateNestedExpression(expression, call) { + // Remove the calc part from nested expressions to ensure + // better browser compatibility + expression = expression.replace(/((?:\-[a-z]+\-)?calc)/g, "") + var evaluatedPart = "" + var nonEvaluatedPart = expression + var matches + while ((matches = NESTED_CALC_RE.exec(nonEvaluatedPart))) { + if (matches[0].index > 0) { + evaluatedPart += nonEvaluatedPart.substring(0, matches[0].index) + } + + var balancedExpr = balanced("(", ")", nonEvaluatedPart.substring([0].index)) + if (balancedExpr.body === "") { + throw new Error("'" + expression + "' must contain a non-whitespace string") + } + + var evaluated = evaluateExpression(balancedExpr.body, "", call) + + evaluatedPart += balancedExpr.pre + evaluated + nonEvaluatedPart = balancedExpr.post + } + + return evaluatedPart + nonEvaluatedPart + } + + return reduceFunctionCall(value, /((?:\-[a-z]+\-)?calc)\(/, evaluateExpression) +} + +/** + * Checks what units are used in an expression + * + * @param {String} expression + * @returns {Array} + */ + +function getUnitsInExpression(expression) { + var uniqueUnits = [] + var uniqueLowerCaseUnits = [] + var unitRegEx = /[\.0-9]([%a-z]+)/gi + var matches = unitRegEx.exec(expression) + + while (matches) { + if (!matches || !matches[1]) { + continue + } + + if (uniqueLowerCaseUnits.indexOf(matches[1].toLowerCase()) === -1) { + uniqueUnits.push(matches[1]) + uniqueLowerCaseUnits.push(matches[1].toLowerCase()) + } + + matches = unitRegEx.exec(expression) + } + + return uniqueUnits +} |
