Skip to content

Commit fa8a5bb

Browse files
committed
Add precision option
poke @7studio
1 parent 213332b commit fa8a5bb

File tree

4 files changed

+100
-85
lines changed

4 files changed

+100
-85
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.2.0 - 2014-11-24
2+
3+
* Decimal precision is now customisable as the `precision` option
4+
15
# 1.1.4 - 2014-11-12
26

37
* 5 decimals rounding for everything

README.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
# reduce-css-calc [![Build Status](https://travis-ci.org/MoOx/reduce-css-calc.png)](https://travis-ci.org/MoOx/reduce-css-calc)
22

3-
Reduce CSS calc() function to the maximum.
3+
> Reduce CSS calc() function to the maximum.
44
55
Particularly useful for packages like [rework-calc](https://github.com/reworkcss/rework-calc) or [postcss-calc](https://github.com/postcss/postcss-calc).
66

77
## Installation
88

9-
```bash
10-
npm install reduce-css-calc
9+
```console
10+
$ npm install reduce-css-calc
1111
```
1212

1313
## Usage
1414

15+
### `var reducedString = reduceCSSCalc(string, precision)`
16+
1517
```javascript
1618
var reduceCSSCalc = require('reduce-css-calc')
1719

@@ -21,6 +23,12 @@ reduceCSSCalc("calc(1 + 1)")
2123
reduceCSSCalc("calc((6 / 2) - (4 * 2) + 1)")
2224
// -4
2325

26+
reduceCSSCalc("calc(1/3)")
27+
// 0.33333
28+
29+
reduceCSSCalc("calc(1/3)", 10)
30+
// 0.3333333333
31+
2432
reduceCSSCalc("calc(3rem * 2 - 1rem)")
2533
// 5rem
2634

@@ -52,11 +60,11 @@ See [unit tests](test/index.js) for others examples.
5260

5361
Work on a branch, install dev-dependencies, respect coding style & run tests before submitting a bug fix or a feature.
5462

55-
```bash
56-
git clone https://github.com/MoOx/reduce-css-calc.git
57-
git checkout -b patch-1
58-
npm install
59-
npm test
63+
```console
64+
$ git clone https://github.com/MoOx/reduce-css-calc.git
65+
$ git checkout -b patch-1
66+
$ npm install
67+
$ npm test
6068
```
6169

6270
## [Changelog](CHANGELOG.md)

index.js

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ var reduceFunctionCall = require("reduce-function-call")
88
* Constantes
99
*/
1010
var MAX_STACK = 100 // should be enough for a single calc()...
11-
var DECIMAL_PRECISION = 100000 // 5 decimals
1211
var NESTED_CALC_RE = /(\+|\-|\*|\\|[^a-z]|)(\s*)(\()/g
1312

1413
/**
@@ -28,103 +27,104 @@ module.exports = reduceCSSCalc
2827
*
2928
* @param {String} value css input
3029
*/
31-
function reduceCSSCalc(value) {
30+
function reduceCSSCalc(value, decimalPrecision) {
3231
stack = 0
33-
return reduceFunctionCall(value, /((?:\-[a-z]+\-)?calc)\(/, evaluateExpression)
34-
}
32+
decimalPrecision = Math.pow(10, decimalPrecision === undefined ? 5 : decimalPrecision)
33+
34+
/**
35+
* Evaluates an expression
36+
*
37+
* @param {String} expression
38+
* @returns {String}
39+
*/
40+
function evaluateExpression (expression, functionIdentifier, call) {
41+
if (stack++ > MAX_STACK) {
42+
stack = 0
43+
throw new Error("Call stack overflow for " + call)
44+
}
3545

36-
/**
37-
* Evaluates an expression
38-
*
39-
* @param {String} expression
40-
* @returns {String}
41-
*/
42-
function evaluateExpression (expression, functionIdentifier, call) {
43-
if (stack++ > MAX_STACK) {
44-
stack = 0
45-
throw new Error("Call stack overflow for " + call)
46-
}
46+
if (expression === "") {
47+
throw new Error(functionIdentifier + "(): '" + call + "' must contain a non-whitespace string")
48+
}
4749

48-
if (expression === "") {
49-
throw new Error(functionIdentifier + "(): '" + call + "' must contain a non-whitespace string")
50-
}
50+
expression = evaluateNestedExpression(expression, call)
5151

52-
expression = evaluateNestedExpression(expression, call)
52+
var units = getUnitsInExpression(expression)
5353

54-
var units = getUnitsInExpression(expression)
54+
// If multiple units let the expression be (i.e. browser calc())
55+
if (units.length > 1) {
56+
return functionIdentifier + "(" + expression + ")"
57+
}
5558

56-
// If multiple units let the expression be (i.e. browser calc())
57-
if (units.length > 1) {
58-
return functionIdentifier + "(" + expression + ")"
59-
}
59+
var unit = units[0] || ""
6060

61-
var unit = units[0] || ""
61+
if (unit === "%") {
62+
// Convert percentages to numbers, to handle expressions like: 50% * 50% (will become: 25%):
63+
expression = expression.replace(/\b[0-9\.]+%/g, function(percent) {
64+
return parseFloat(percent.slice(0, -1)) * 0.01
65+
})
66+
}
6267

63-
if (unit === "%") {
64-
// Convert percentages to numbers, to handle expressions like: 50% * 50% (will become: 25%):
65-
expression = expression.replace(/\b[0-9\.]+%/g, function(percent) {
66-
return parseFloat(percent.slice(0, -1)) * 0.01
67-
})
68-
}
68+
// Remove units in expression:
69+
var toEvaluate = expression.replace(new RegExp(unit, "g"), "")
70+
var result
6971

70-
// Remove units in expression:
71-
var toEvaluate = expression.replace(new RegExp(unit, "g"), "")
72-
var result
72+
try {
73+
result = eval(toEvaluate)
74+
}
75+
catch (e) {
76+
return functionIdentifier + "(" + expression + ")"
77+
}
7378

74-
try {
75-
result = eval(toEvaluate)
76-
}
77-
catch (e) {
78-
return functionIdentifier + "(" + expression + ")"
79-
}
79+
// Transform back to a percentage result:
80+
if (unit === "%") {
81+
result *= 100
82+
}
8083

81-
// Transform back to a percentage result:
82-
if (unit === "%") {
83-
result *= 100
84-
}
84+
// adjust rounding shit
85+
// (0.1 * 0.2 === 0.020000000000000004)
86+
result = Math.round(result * decimalPrecision) / decimalPrecision
8587

86-
// adjust rounding shit
87-
// (0.1 * 0.2 === 0.020000000000000004)
88-
result = Math.round(result * DECIMAL_PRECISION) / DECIMAL_PRECISION
88+
// We don't need units for zero values...
89+
if (result !== 0) {
90+
result += unit
91+
}
8992

90-
// We don't need units for zero values...
91-
if (result !== 0) {
92-
result += unit
93+
return result
9394
}
9495

95-
return result
96-
}
97-
98-
/**
99-
* Evaluates nested expressions
100-
*
101-
* @param {String} expression
102-
* @returns {String}
103-
*/
104-
function evaluateNestedExpression(expression, call) {
105-
var evaluatedPart = ""
106-
var nonEvaluatedPart = expression
107-
var matches
108-
while ((matches = NESTED_CALC_RE.exec(nonEvaluatedPart))) {
109-
if (matches[0].index > 0) {
110-
evaluatedPart += nonEvaluatedPart.substring(0, matches[0].index)
111-
}
112-
113-
var balancedExpr = balanced("(", ")", nonEvaluatedPart.substring([0].index))
114-
if (balancedExpr.body === "") {
115-
throw new Error("'" + expression + "' must contain a non-whitespace string")
96+
/**
97+
* Evaluates nested expressions
98+
*
99+
* @param {String} expression
100+
* @returns {String}
101+
*/
102+
function evaluateNestedExpression(expression, call) {
103+
var evaluatedPart = ""
104+
var nonEvaluatedPart = expression
105+
var matches
106+
while ((matches = NESTED_CALC_RE.exec(nonEvaluatedPart))) {
107+
if (matches[0].index > 0) {
108+
evaluatedPart += nonEvaluatedPart.substring(0, matches[0].index)
109+
}
110+
111+
var balancedExpr = balanced("(", ")", nonEvaluatedPart.substring([0].index))
112+
if (balancedExpr.body === "") {
113+
throw new Error("'" + expression + "' must contain a non-whitespace string")
114+
}
115+
116+
var evaluated = evaluateExpression(balancedExpr.body, "", call)
117+
118+
evaluatedPart += balancedExpr.pre + evaluated
119+
nonEvaluatedPart = balancedExpr.post
116120
}
117121

118-
var evaluated = evaluateExpression(balancedExpr.body, "", call)
119-
120-
evaluatedPart += balancedExpr.pre + evaluated
121-
nonEvaluatedPart = balancedExpr.post
122+
return evaluatedPart + nonEvaluatedPart
122123
}
123124

124-
return evaluatedPart + nonEvaluatedPart
125+
return reduceFunctionCall(value, /((?:\-[a-z]+\-)?calc)\(/, evaluateExpression)
125126
}
126127

127-
128128
/**
129129
* Checks what units are used in an expression
130130
*

test/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ test("reduce prefixed css calc()", function(t) {
7575
test("handle rounding issues", function(t) {
7676
t.equal(reduceCSSCalc("calc(10% * 20%)"), "2%", "should round percentage")
7777
t.equal(reduceCSSCalc("calc(3rem * 1.2)"), "3.6rem", "should round floats")
78+
t.equal(reduceCSSCalc("calc(1/3)"), "0.33333", "should round with default precision to 5 decimals")
79+
t.equal(reduceCSSCalc("calc(1/3)", 10), "0.3333333333", "should round with desired precision (10)")
80+
t.equal(reduceCSSCalc("calc(3 * 1.2)", 0), "4", "should round with desired precision (O)")
7881
t.end()
7982
})
8083

0 commit comments

Comments
 (0)