diff --git a/src/color.js b/src/color.js index 1c9938f..fc6d604 100644 --- a/src/color.js +++ b/src/color.js @@ -69,7 +69,7 @@ Color.prototype.hex6 = function(value) { }; -var _rgb = /^rgb\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3})\)$/; +var _rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/; Color.prototype.rgb = function(value) { var match = null; @@ -81,7 +81,7 @@ Color.prototype.rgb = function(value) { return match !== null; }; -var _rgba = /^rgba\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3}) *, *(\d+\.?\d*)\)$/; +var _rgba = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?\.?\d+)\s*\)$/; Color.prototype.rgba = function(value) { var match = null; @@ -101,12 +101,13 @@ Color.prototype.toString = function() { }; Color.prototype.namedColor = function(value) { - var color = colors[value.toLowerCase()]; + value = value.toLowerCase(); + var color = colors[value]; if (color) { this.r = color[0]; this.g = color[1]; this.b = color[2]; - } else if (value.toLowerCase() === "transparent") { + } else if (value === "transparent") { this.r = this.g = this.b = this.a = 0; return true; } diff --git a/src/gradientcontainer.js b/src/gradientcontainer.js index 8520f34..0c3ea61 100644 --- a/src/gradientcontainer.js +++ b/src/gradientcontainer.js @@ -9,9 +9,13 @@ function GradientContainer(imageData) { this.promise = Promise.resolve(true); } -GradientContainer.prototype.TYPES = { +GradientContainer.TYPES = { LINEAR: 1, RADIAL: 2 }; +// TODO: support hsl[a], negative %/length values +// TODO: support (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle ) +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; + module.exports = GradientContainer; diff --git a/src/lineargradientcontainer.js b/src/lineargradientcontainer.js index 803491e..b1ce32f 100644 --- a/src/lineargradientcontainer.js +++ b/src/lineargradientcontainer.js @@ -1,16 +1,15 @@ var GradientContainer = require('./gradientcontainer'); var Color = require('./color'); -var COLOR_STOP_REGEXP = /^\s*(.*)\s*(\d{1,3})?(%|px)?$/; - function LinearGradientContainer(imageData) { GradientContainer.apply(this, arguments); - this.type = this.TYPES.LINEAR; + this.type = GradientContainer.TYPES.LINEAR; - var hasDirection = imageData.args[0].match(this.stepRegExp) === null; + var hasDirection = LinearGradientContainer.REGEXP_DIRECTION.test( imageData.args[0] ) || + !GradientContainer.REGEXP_COLORSTOP.test( imageData.args[0] ); if (hasDirection) { - imageData.args[0].split(" ").reverse().forEach(function(position) { + imageData.args[0].split(/\s+/).reverse().forEach(function(position, index) { switch(position) { case "left": this.x0 = 0; @@ -36,6 +35,24 @@ function LinearGradientContainer(imageData) { this.x1 = x0; this.y1 = y0; break; + case "center": + break; // centered by default + // Firefox internally converts position keywords to percentages: + // http://www.w3.org/TR/2010/WD-CSS2-20101207/colors.html#propdef-background-position + default: // percentage or absolute length + // TODO: support absolute start point positions (e.g., use bounds to convert px to a ratio) + var ratio = parseFloat(position, 10) * 1e-2; + if (isNaN(ratio)) { // invalid or unhandled value + break; + } + if (index === 0) { + this.y0 = ratio; + this.y1 = 1 - this.y0; + } else { + this.x0 = ratio; + this.x1 = 1 - this.x0; + } + break; } }, this); } else { @@ -43,15 +60,16 @@ function LinearGradientContainer(imageData) { this.y1 = 1; } - this.colorStops = imageData.args.slice(hasDirection ? 1 : 0) - .map(function(colorStop) { return colorStop.match(COLOR_STOP_REGEXP);}) - .filter(function(colorStopMatch) { return !!colorStopMatch;}) - .map(function(colorStopMatch) { - return { - color: new Color(colorStopMatch[1]), - stop: colorStopMatch[3] === "%" ? colorStopMatch[2] / 100 : null - }; - }); + this.colorStops = imageData.args.slice(hasDirection ? 1 : 0).map(function(colorStop) { + var colorStopMatch = colorStop.match(GradientContainer.REGEXP_COLORSTOP); + var value = +colorStopMatch[2]; + var unit = value === 0 ? "%" : colorStopMatch[3]; // treat "0" as "0%" + return { + color: new Color(colorStopMatch[1]), + // TODO: support absolute stop positions (e.g., compute gradient line length & convert px to ratio) + stop: unit === "%" ? value / 100 : null + }; + }); if (this.colorStops[0].stop === null) { this.colorStops[0].stop = 0; @@ -61,6 +79,7 @@ function LinearGradientContainer(imageData) { this.colorStops[this.colorStops.length - 1].stop = 1; } + // calculates and fills-in explicit stop positions when omitted from rule this.colorStops.forEach(function(colorStop, index) { if (colorStop.stop === null) { this.colorStops.slice(index).some(function(find, count) { @@ -77,6 +96,7 @@ function LinearGradientContainer(imageData) { LinearGradientContainer.prototype = Object.create(GradientContainer.prototype); -LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/; +// TODO: support (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle ) +LinearGradientContainer.REGEXP_DIRECTION = /^\s*(?:to|left|right|top|bottom|center|\d{1,3}(?:\.\d+)?%?)(?:\s|$)/i; module.exports = LinearGradientContainer; diff --git a/src/webkitgradientcontainer.js b/src/webkitgradientcontainer.js index d196354..36fceab 100644 --- a/src/webkitgradientcontainer.js +++ b/src/webkitgradientcontainer.js @@ -2,7 +2,7 @@ var GradientContainer = require('./gradientcontainer'); function WebkitGradientContainer(imageData) { GradientContainer.apply(this, arguments); - this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL; + this.type = imageData.args[0] === "linear" ? GradientContainer.TYPES.LINEAR : GradientContainer.TYPES.RADIAL; } WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype); diff --git a/tests/cases/background/linear-gradient.html b/tests/cases/background/linear-gradient.html index 912307f..6e1fc80 100644 --- a/tests/cases/background/linear-gradient.html +++ b/tests/cases/background/linear-gradient.html @@ -112,10 +112,13 @@ .linearGradient8 { background: linear-gradient(to top left, #fff 0%, #00263c 100%); } - .linearGradient9 { background: linear-gradient(to top left, white 0%, black 100%); } + + .linearGradient10 { + background: linear-gradient(to left top, #0000Ff, rgb(255, 0,0) 50px, green 199px, rgba(0, 0, 0, 0.5) 100.0%); + } @@ -130,6 +133,7 @@
 
 
 
+
 
diff --git a/tests/mocha/gradients.js b/tests/mocha/gradients.js index 51a054e..8f616f0 100644 --- a/tests/mocha/gradients.js +++ b/tests/mocha/gradients.js @@ -9,6 +9,15 @@ describe("Gradients", function() { " rgb(0, 255, 0)" ] }, + { + method: "linear-gradient", + args: [ + "left", + " red", + " rgb(255, 255, 0)", + " rgb(0, 255, 0)" + ] + }, { method: 'linear-gradient', args: [ @@ -23,6 +32,20 @@ describe("Gradients", function() { " rgb(38, 85, 139) 100%" ] }, + { + method: 'linear-gradient', + args: [ + "left", + " rgb(206, 219, 233) 0%", + " rgb(170, 197, 222) 17px", + " rgb(97, 153, 199) 50%", + " rgb(58, 132, 195) 51px", + " rgb(65, 154, 214) 59%", + " rgb(75, 184, 240) 71px", + " rgb(58, 139, 194) 84%", + " rgb(38, 85, 139) 100px" + ] + }, { method: "gradient", args: [ @@ -35,6 +58,20 @@ describe("Gradients", function() { " to(rgb(191, 110, 78))" ] }, + { + method: "gradient", + args: [ + "linear", + " 50% 0%", + " 50% 100%", + " from(rgb(255, 0, 0))", + " color-stop(0.314159, green)", + " color-stop(0.51, rgb(0, 0, 255))", + // temporary workaround for Blink/WebKit bug: crbug.com/453414 + //" to(rgba(0, 0, 0, 0.5))" + " to(rgba(0, 0, 0, 0))" + ] + }, { method: 'linear-gradient', args: [ diff --git a/tests/mocha/parsing.html b/tests/mocha/parsing.html index a31b2d6..36c84f4 100644 --- a/tests/mocha/parsing.html +++ b/tests/mocha/parsing.html @@ -1,3 +1,4 @@ + @@ -67,7 +68,8 @@
- -
+
+
-
-
+
+
+
+