Skip to content

Commit

Permalink
Fix #25 - prototype.constructor.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Feb 22, 2016
1 parent 963aba1 commit df3952a
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 126 deletions.
123 changes: 59 additions & 64 deletions src/color.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import define, {extend} from "./define";

export function Color() {}

export var darker = 0.7;
Expand Down Expand Up @@ -163,14 +165,14 @@ var named = {
yellowgreen: 0x9acd32
};

color.prototype = Color.prototype = {
define(Color, color, {
displayable: function() {
return this.rgb().displayable();
},
toString: function() {
return this.rgb() + "";
}
};
});

export default function color(format) {
var m;
Expand Down Expand Up @@ -215,37 +217,33 @@ export function Rgb(r, g, b, opacity) {
this.opacity = +opacity;
}

var _rgb = rgb.prototype = Rgb.prototype = new Color;

_rgb.brighter = function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
};

_rgb.darker = function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
};

_rgb.rgb = function() {
return this;
};

_rgb.displayable = function() {
return (0 <= this.r && this.r <= 255)
&& (0 <= this.g && this.g <= 255)
&& (0 <= this.b && this.b <= 255)
&& (0 <= this.opacity && this.opacity <= 1);
};

_rgb.toString = function() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "rgb(" : "rgba(")
+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
+ (a === 1 ? ")" : ", " + a + ")");
};
define(Rgb, rgb, extend(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
rgb: function() {
return this;
},
displayable: function() {
return (0 <= this.r && this.r <= 255)
&& (0 <= this.g && this.g <= 255)
&& (0 <= this.b && this.b <= 255)
&& (0 <= this.opacity && this.opacity <= 1);
},
toString: function() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "rgb(" : "rgba(")
+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
+ (a === 1 ? ")" : ", " + a + ")");
}
}));

function hsla(h, s, l, a) {
if (a <= 0) h = s = l = NaN;
Expand Down Expand Up @@ -291,37 +289,34 @@ function Hsl(h, s, l, opacity) {
this.opacity = +opacity;
}

var _hsl = hsl.prototype = Hsl.prototype = new Color;

_hsl.brighter = function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
};

_hsl.darker = function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
};

_hsl.rgb = function() {
var h = this.h % 360 + (this.h < 0) * 360,
s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
l = this.l,
m2 = l + (l < 0.5 ? l : 1 - l) * s,
m1 = 2 * l - m2;
return new Rgb(
hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
hsl2rgb(h, m1, m2),
hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
this.opacity
);
};

_hsl.displayable = function() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s))
&& (0 <= this.l && this.l <= 1)
&& (0 <= this.opacity && this.opacity <= 1);
};
define(Hsl, hsl, extend(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
rgb: function() {
var h = this.h % 360 + (this.h < 0) * 360,
s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
l = this.l,
m2 = l + (l < 0.5 ? l : 1 - l) * s,
m1 = 2 * l - m2;
return new Rgb(
hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
hsl2rgb(h, m1, m2),
hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
this.opacity
);
},
displayable: function() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s))
&& (0 <= this.l && this.l <= 1)
&& (0 <= this.opacity && this.opacity <= 1);
}
}));

/* From FvD 13.37, CSS Color Module Level 3 */
function hsl2rgb(h, m1, m2) {
Expand Down
49 changes: 24 additions & 25 deletions src/cubehelix.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import define, {extend} from "./define";
import {Color, rgbConvert, Rgb, darker, brighter} from "./color";
import {deg2rad, rad2deg} from "./math";

Expand Down Expand Up @@ -35,28 +36,26 @@ export function Cubehelix(h, s, l, opacity) {
this.opacity = +opacity;
}

var _cubehelix = cubehelix.prototype = Cubehelix.prototype = new Color;

_cubehelix.brighter = function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
};

_cubehelix.darker = function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
};

_cubehelix.rgb = function() {
var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
l = +this.l,
a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
cosh = Math.cos(h),
sinh = Math.sin(h);
return new Rgb(
255 * (l + a * (A * cosh + B * sinh)),
255 * (l + a * (C * cosh + D * sinh)),
255 * (l + a * (E * cosh)),
this.opacity
);
};
define(Cubehelix, cubehelix, extend(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
},
rgb: function() {
var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
l = +this.l,
a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
cosh = Math.cos(h),
sinh = Math.sin(h);
return new Rgb(
255 * (l + a * (A * cosh + B * sinh)),
255 * (l + a * (C * cosh + D * sinh)),
255 * (l + a * (E * cosh)),
this.opacity
);
}
}));
10 changes: 10 additions & 0 deletions src/define.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function(constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor;
}

export function extend(parent, definition) {
var prototype = Object.create(parent.prototype);
for (var key in definition) prototype[key] = definition[key];
return prototype;
}
71 changes: 34 additions & 37 deletions src/lab.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import define, {extend} from "./define";
import {Color, rgbConvert, Rgb} from "./color";
import {deg2rad, rad2deg} from "./math";

Expand Down Expand Up @@ -37,30 +38,28 @@ export function Lab(l, a, b, opacity) {
this.opacity = +opacity;
}

var _lab = lab.prototype = Lab.prototype = new Color;

_lab.brighter = function(k) {
return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
};

_lab.darker = function(k) {
return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
};

_lab.rgb = function() {
var y = (this.l + 16) / 116,
x = isNaN(this.a) ? y : y + this.a / 500,
z = isNaN(this.b) ? y : y - this.b / 200;
y = Yn * lab2xyz(y);
x = Xn * lab2xyz(x);
z = Zn * lab2xyz(z);
return new Rgb(
xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB
xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z),
this.opacity
);
};
define(Lab, lab, extend(Color, {
brighter: function(k) {
return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
},
darker: function(k) {
return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);
},
rgb: function() {
var y = (this.l + 16) / 116,
x = isNaN(this.a) ? y : y + this.a / 500,
z = isNaN(this.b) ? y : y - this.b / 200;
y = Yn * lab2xyz(y);
x = Xn * lab2xyz(x);
z = Zn * lab2xyz(z);
return new Rgb(
xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB
xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),
xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z),
this.opacity
);
}
}));

function xyz2lab(t) {
return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
Expand Down Expand Up @@ -96,16 +95,14 @@ export function Hcl(h, c, l, opacity) {
this.opacity = +opacity;
}

var _hcl = hcl.prototype = Hcl.prototype = new Color;

_hcl.brighter = function(k) {
return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity);
};

_hcl.darker = function(k) {
return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity);
};

_hcl.rgb = function() {
return labConvert(this).rgb();
};
define(Hcl, hcl, extend(Color, {
brighter: function(k) {
return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity);
},
darker: function(k) {
return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity);
},
rgb: function() {
return labConvert(this).rgb();
}
}));
10 changes: 10 additions & 0 deletions test/cubehelix-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var tape = require("tape"),
color = require("../");

tape("cubehelix(…) returns an instance of cubehelix and color", function(test) {
var c = color.cubehelix("steelblue");
test.ok(c instanceof color.cubehelix);
test.ok(c instanceof color.color);
test.equal(c.constructor.name, "Cubehelix");
test.end();
});
1 change: 1 addition & 0 deletions test/hcl-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tape("hcl(…) returns an instance of hcl and color", function(test) {
var c = color.hcl(120, 40, 50);
test.ok(c instanceof color.hcl);
test.ok(c instanceof color.color);
test.equal(c.constructor.name, "Hcl");
test.end();
});

Expand Down
1 change: 1 addition & 0 deletions test/hsl-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tape("hsl(…) returns an instance of hsl and color", function(test) {
var c = color.hsl(120, 0.4, 0.5);
test.ok(c instanceof color.hsl);
test.ok(c instanceof color.color);
test.equal(c.constructor.name, "Hsl");
test.end();
});

Expand Down
1 change: 1 addition & 0 deletions test/lab-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ tape("lab(…) returns an instance of lab and color", function(test) {
var c = color.lab(120, 40, 50);
test.ok(c instanceof color.lab);
test.ok(c instanceof color.color);
test.equal(c.constructor.name, "Lab");
test.end();
});

Expand Down
1 change: 1 addition & 0 deletions test/rgb-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ tape("rgb(…) returns an instance of rgb and color", function(test) {
var c = color.rgb(70, 130, 180);
test.ok(c instanceof color.rgb);
test.ok(c instanceof color.color);
test.equal(c.constructor.name, "Rgb");
test.end();
});

Expand Down

0 comments on commit df3952a

Please sign in to comment.