Skip to content

Commit 6d168f4

Browse files
committed
Merge pull request niklasvh#708 from usmonster/fix-firefox-gradients
Linear gradients now parse color names
2 parents bebb353 + 318ca48 commit 6d168f4

File tree

7 files changed

+141
-30
lines changed

7 files changed

+141
-30
lines changed

src/color.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ Color.prototype.hex6 = function(value) {
6969
};
7070

7171

72-
var _rgb = /^rgb\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3})\)$/;
72+
var _rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/;
7373

7474
Color.prototype.rgb = function(value) {
7575
var match = null;
@@ -81,7 +81,7 @@ Color.prototype.rgb = function(value) {
8181
return match !== null;
8282
};
8383

84-
var _rgba = /^rgba\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3}) *, *(\d+\.?\d*)\)$/;
84+
var _rgba = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?\.?\d+)\s*\)$/;
8585

8686
Color.prototype.rgba = function(value) {
8787
var match = null;
@@ -101,12 +101,13 @@ Color.prototype.toString = function() {
101101
};
102102

103103
Color.prototype.namedColor = function(value) {
104-
var color = colors[value.toLowerCase()];
104+
value = value.toLowerCase();
105+
var color = colors[value];
105106
if (color) {
106107
this.r = color[0];
107108
this.g = color[1];
108109
this.b = color[2];
109-
} else if (value.toLowerCase() === "transparent") {
110+
} else if (value === "transparent") {
110111
this.r = this.g = this.b = this.a = 0;
111112
return true;
112113
}

src/gradientcontainer.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ function GradientContainer(imageData) {
99
this.promise = Promise.resolve(true);
1010
}
1111

12-
GradientContainer.prototype.TYPES = {
12+
GradientContainer.TYPES = {
1313
LINEAR: 1,
1414
RADIAL: 2
1515
};
1616

17+
// TODO: support hsl[a], negative %/length values
18+
// TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle )
19+
GradientContainer.REGEXP_COLORSTOP = /^\s*(rgba?\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*[0-9\.]+)?\s*\)|[a-z]{3,20}|#[a-f0-9]{3,6})(?:\s+(\d{1,3}(?:\.\d+)?)(%|px)?)?(?:\s|$)/i;
20+
1721
module.exports = GradientContainer;

src/lineargradientcontainer.js

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
var GradientContainer = require('./gradientcontainer');
22
var Color = require('./color');
33

4-
var COLOR_STOP_REGEXP = /^\s*(.*)\s*(\d{1,3})?(%|px)?$/;
5-
64
function LinearGradientContainer(imageData) {
75
GradientContainer.apply(this, arguments);
8-
this.type = this.TYPES.LINEAR;
6+
this.type = GradientContainer.TYPES.LINEAR;
97

10-
var hasDirection = imageData.args[0].match(this.stepRegExp) === null;
8+
var hasDirection = LinearGradientContainer.REGEXP_DIRECTION.test( imageData.args[0] ) ||
9+
!GradientContainer.REGEXP_COLORSTOP.test( imageData.args[0] );
1110

1211
if (hasDirection) {
13-
imageData.args[0].split(" ").reverse().forEach(function(position) {
12+
imageData.args[0].split(/\s+/).reverse().forEach(function(position, index) {
1413
switch(position) {
1514
case "left":
1615
this.x0 = 0;
@@ -36,22 +35,41 @@ function LinearGradientContainer(imageData) {
3635
this.x1 = x0;
3736
this.y1 = y0;
3837
break;
38+
case "center":
39+
break; // centered by default
40+
// Firefox internally converts position keywords to percentages:
41+
// http://www.w3.org/TR/2010/WD-CSS2-20101207/colors.html#propdef-background-position
42+
default: // percentage or absolute length
43+
// TODO: support absolute start point positions (e.g., use bounds to convert px to a ratio)
44+
var ratio = parseFloat(position, 10) * 1e-2;
45+
if (isNaN(ratio)) { // invalid or unhandled value
46+
break;
47+
}
48+
if (index === 0) {
49+
this.y0 = ratio;
50+
this.y1 = 1 - this.y0;
51+
} else {
52+
this.x0 = ratio;
53+
this.x1 = 1 - this.x0;
54+
}
55+
break;
3956
}
4057
}, this);
4158
} else {
4259
this.y0 = 0;
4360
this.y1 = 1;
4461
}
4562

46-
this.colorStops = imageData.args.slice(hasDirection ? 1 : 0)
47-
.map(function(colorStop) { return colorStop.match(COLOR_STOP_REGEXP);})
48-
.filter(function(colorStopMatch) { return !!colorStopMatch;})
49-
.map(function(colorStopMatch) {
50-
return {
51-
color: new Color(colorStopMatch[1]),
52-
stop: colorStopMatch[3] === "%" ? colorStopMatch[2] / 100 : null
53-
};
54-
});
63+
this.colorStops = imageData.args.slice(hasDirection ? 1 : 0).map(function(colorStop) {
64+
var colorStopMatch = colorStop.match(GradientContainer.REGEXP_COLORSTOP);
65+
var value = +colorStopMatch[2];
66+
var unit = value === 0 ? "%" : colorStopMatch[3]; // treat "0" as "0%"
67+
return {
68+
color: new Color(colorStopMatch[1]),
69+
// TODO: support absolute stop positions (e.g., compute gradient line length & convert px to ratio)
70+
stop: unit === "%" ? value / 100 : null
71+
};
72+
});
5573

5674
if (this.colorStops[0].stop === null) {
5775
this.colorStops[0].stop = 0;
@@ -61,6 +79,7 @@ function LinearGradientContainer(imageData) {
6179
this.colorStops[this.colorStops.length - 1].stop = 1;
6280
}
6381

82+
// calculates and fills-in explicit stop positions when omitted from rule
6483
this.colorStops.forEach(function(colorStop, index) {
6584
if (colorStop.stop === null) {
6685
this.colorStops.slice(index).some(function(find, count) {
@@ -77,6 +96,7 @@ function LinearGradientContainer(imageData) {
7796

7897
LinearGradientContainer.prototype = Object.create(GradientContainer.prototype);
7998

80-
LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/;
99+
// TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle )
100+
LinearGradientContainer.REGEXP_DIRECTION = /^\s*(?:to|left|right|top|bottom|center|\d{1,3}(?:\.\d+)?%?)(?:\s|$)/i;
81101

82102
module.exports = LinearGradientContainer;

src/webkitgradientcontainer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ var GradientContainer = require('./gradientcontainer');
22

33
function WebkitGradientContainer(imageData) {
44
GradientContainer.apply(this, arguments);
5-
this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL;
5+
this.type = imageData.args[0] === "linear" ? GradientContainer.TYPES.LINEAR : GradientContainer.TYPES.RADIAL;
66
}
77

88
WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);

tests/cases/background/linear-gradient.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,13 @@
112112
.linearGradient8 {
113113
background: linear-gradient(to top left, #fff 0%, #00263c 100%);
114114
}
115-
116115
.linearGradient9 {
117116
background: linear-gradient(to top left, white 0%, black 100%);
118117
}
118+
119+
.linearGradient10 {
120+
background: linear-gradient(to left top, #0000Ff, rgb(255, 0,0) 50px, green 199px, rgba(0, 0, 0, 0.5) 100.0%);
121+
}
119122
</style>
120123

121124
</head>
@@ -130,6 +133,7 @@
130133
<div class="linearGradient7">&nbsp;</div>
131134
<div class="linearGradient8">&nbsp;</div>
132135
<div class="linearGradient9">&nbsp;</div>
136+
<div class="linearGradient10">&nbsp;</div>
133137
</div>
134138
</body>
135139
</html>

tests/mocha/gradients.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ describe("Gradients", function() {
99
" rgb(0, 255, 0)"
1010
]
1111
},
12+
{
13+
method: "linear-gradient",
14+
args: [
15+
"left",
16+
" red",
17+
" rgb(255, 255, 0)",
18+
" rgb(0, 255, 0)"
19+
]
20+
},
1221
{
1322
method: 'linear-gradient',
1423
args: [
@@ -23,6 +32,20 @@ describe("Gradients", function() {
2332
" rgb(38, 85, 139) 100%"
2433
]
2534
},
35+
{
36+
method: 'linear-gradient',
37+
args: [
38+
"left",
39+
" rgb(206, 219, 233) 0%",
40+
" rgb(170, 197, 222) 17px",
41+
" rgb(97, 153, 199) 50%",
42+
" rgb(58, 132, 195) 51px",
43+
" rgb(65, 154, 214) 59%",
44+
" rgb(75, 184, 240) 71px",
45+
" rgb(58, 139, 194) 84%",
46+
" rgb(38, 85, 139) 100px"
47+
]
48+
},
2649
{
2750
method: "gradient",
2851
args: [
@@ -35,6 +58,20 @@ describe("Gradients", function() {
3558
" to(rgb(191, 110, 78))"
3659
]
3760
},
61+
{
62+
method: "gradient",
63+
args: [
64+
"linear",
65+
" 50% 0%",
66+
" 50% 100%",
67+
" from(rgb(255, 0, 0))",
68+
" color-stop(0.314159, green)",
69+
" color-stop(0.51, rgb(0, 0, 255))",
70+
// temporary workaround for Blink/WebKit bug: crbug.com/453414
71+
//" to(rgba(0, 0, 0, 0.5))"
72+
" to(rgba(0, 0, 0, 0))"
73+
]
74+
},
3875
{
3976
method: 'linear-gradient',
4077
args: [

tests/mocha/parsing.html

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<!DOCTYPE html>
12
<html>
23
<head>
34
<meta charset="utf-8">
@@ -67,7 +68,8 @@
6768
<div style="background-position: left bottom;"></div>
6869
</div>
6970

70-
<style>
71+
<div id="backgroundGradients">
72+
<style scoped>
7173
.linearGradientSimple {
7274
/* FF 3.6+ */
7375
background: -moz-linear-gradient(left, #ff0000, #ffff00, #00ff00);
@@ -82,6 +84,20 @@
8284
/* W3C */
8385
background: linear-gradient(left, #ff0000, #ffff00, #00ff00);
8486
}
87+
.linearGradientSimple2 {
88+
/* FF 3.6+ */
89+
background: -moz-linear-gradient(left, red, #ff0, #0f0);
90+
/* Chrome,Safari4+ */
91+
background: -webkit-gradient(linear, left center, right center, color-stop(red), color-stop(#ff0), color-stop(#0f0));
92+
/* Chrome 10+, Safari 5.1+ */
93+
background: -webkit-linear-gradient(left, red, #ff0, #0f0);
94+
/* Opera 11.10+ */
95+
background: -o-linear-gradient(left, red, #ff0, #0f0);
96+
/* IE 10+ */
97+
background: -ms-linear-gradient(left, red, #ff0, #0f0);
98+
/* W3C */
99+
background: linear-gradient(left, red, #ff0, #0f0);
100+
}
85101
.linearGradientWithStops {
86102
/* FF 3.6+ */
87103
background: -moz-linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
@@ -96,7 +112,23 @@
96112
/* W3C */
97113
background: linear-gradient(left, #cedbe9 0%, #aac5de 17%, #6199c7 50%, #3a84c3 51%, #419ad6 59%, #4bb8f0 71%, #3a8bc2 84%, #26558b 100%);
98114
}
99-
.linearGradient3 {
115+
.linearGradientWithPixelLengthStops {
116+
width: 100px;
117+
height: 100px;
118+
/* FF 3.6+ */
119+
background: -moz-linear-gradient(left, #cedbe9 0%, #aac5de 17px, #6199c7 50%, #3a84c3 51px, #419ad6 59%, #4bb8f0 71px, #3a8bc2 84%, #26558b 100px);
120+
/* Chrome, Safari 4+ */
121+
background: -webkit-gradient(linear, left center, right center, color-stop(0%, #cedbe9), color-stop(17px, #aac5de), color-stop(50%, #6199c7), color-stop(51%, #3a84c3), color-stop(59%, #419ad6), color-stop(71px, #4bb8f0), color-stop(84%, #3a8bc2), color-stop(100px, #26558b));
122+
/* Chrome 10+, Safari 5.1+ */
123+
background: -webkit-linear-gradient(left, #cedbe9 0%, #aac5de 17px, #6199c7 50%, #3a84c3 51px, #419ad6 59%, #4bb8f0 71px, #3a8bc2 84%, #26558b 100px);
124+
/* Opera 11.10+ */
125+
background: -o-linear-gradient(left, #cedbe9 0%, #aac5de 17px, #6199c7 50%, #3a84c3 51px, #419ad6 59%, #4bb8f0 71px, #3a8bc2 84%, #26558b 100px);
126+
/* IE 10+ */
127+
background: -ms-linear-gradient(left, #cedbe9 0%, #aac5de 17px, #6199c7 50%, #3a84c3 51px, #419ad6 59%, #4bb8f0 71px, #3a8bc2 84%, #26558b 100px);
128+
/* W3C */
129+
background: linear-gradient(left, #cedbe9 0%, #aac5de 17px, #6199c7 50%, #3a84c3 51px, #419ad6 59%, #4bb8f0 71px, #3a8bc2 84%, #26558b 100px);
130+
}
131+
.linearGradient5 {
100132
/* FF 3.6+ */
101133
background: -moz-linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
102134
/* Chrome, Safari 4+ */
@@ -108,8 +140,19 @@
108140
/* W3C */
109141
background: linear-gradient(top, #f0b7a1 0%, #8c3310 50%, #752201 51%, #bf6e4e 100%);
110142
}
111-
112-
.linearGradient4 {
143+
.linearGradient6 {
144+
/* FF 3.6+ */
145+
background: -moz-linear-gradient(top, #F00 0, green 31.4159%, #0000fF 51%, rgba(0, 0, 0, 0.0) 100%);
146+
/* Chrome, Safari 4+ */
147+
background: -webkit-gradient(linear, center top, center bottom, color-stop(0, #F00), color-stop(31.4159%, green), color-stop(51%, #0000fF), color-stop(100%, rgba(0, 0, 0, 0.0)));
148+
/* Opera 11.10+ */
149+
background: -o-linear-gradient(top, #F00 0, green 31.4159%, #0000fF 51%, rgba(0, 0, 0, 0.0) 100%);
150+
/* IE 10+ */
151+
background: -ms-linear-gradient(top, #F00 0, green 31.4159%, #0000fF 51%, rgba(0, 0, 0, 0.0) 100%);
152+
/* W3C */
153+
background: linear-gradient(top, #F00 0, green 31.4159%, #0000fF 51%, rgba(0, 0, 0, 0.0) 100%);
154+
}
155+
.linearGradient7 {
113156
background: -webkit-linear-gradient(0deg, #ddd, #ddd 50%, transparent 50%);
114157
background: linear-gradient(0deg, #ddd, #ddd 50%, transparent 50%);
115158
}
@@ -157,12 +200,14 @@
157200
background: -ms-radial-gradient(75% 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
158201
background: radial-gradient(75% 19%, ellipse cover, #ababab, #0000ff 33%,#991f1f 100%);
159202
}
160-
</style>
161-
<div id="backgroundGradients">
203+
</style>
162204
<div class="linearGradientSimple"></div>
205+
<div class="linearGradientSimple2"></div>
163206
<div class="linearGradientWithStops"></div>
164-
<div class="linearGradient3"></div>
165-
<div class="linearGradient4"></div>
207+
<div class="linearGradientWithPixelLengthStops"></div>
208+
<div class="linearGradient5"></div>
209+
<div class="linearGradient6"></div>
210+
<div class="linearGradient7"></div>
166211
<div class="radialGradient"></div>
167212
<div class="radialGradient2"></div>
168213
<div class="radialGradient3"></div>

0 commit comments

Comments
 (0)