| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 | 
							- 'use strict';
 
- var colorString = require('color-string');
 
- var convert = require('color-convert');
 
- var _slice = [].slice;
 
- var skippedModels = [
 
- 	// to be honest, I don't really feel like keyword belongs in color convert, but eh.
 
- 	'keyword',
 
- 	// gray conflicts with some method names, and has its own method defined.
 
- 	'gray',
 
- 	// shouldn't really be in color-convert either...
 
- 	'hex'
 
- ];
 
- var hashedModelKeys = {};
 
- Object.keys(convert).forEach(function (model) {
 
- 	hashedModelKeys[_slice.call(convert[model].labels).sort().join('')] = model;
 
- });
 
- var limiters = {};
 
- function Color(obj, model) {
 
- 	if (!(this instanceof Color)) {
 
- 		return new Color(obj, model);
 
- 	}
 
- 	if (model && model in skippedModels) {
 
- 		model = null;
 
- 	}
 
- 	if (model && !(model in convert)) {
 
- 		throw new Error('Unknown model: ' + model);
 
- 	}
 
- 	var i;
 
- 	var channels;
 
- 	if (obj == null) { // eslint-disable-line no-eq-null,eqeqeq
 
- 		this.model = 'rgb';
 
- 		this.color = [0, 0, 0];
 
- 		this.valpha = 1;
 
- 	} else if (obj instanceof Color) {
 
- 		this.model = obj.model;
 
- 		this.color = obj.color.slice();
 
- 		this.valpha = obj.valpha;
 
- 	} else if (typeof obj === 'string') {
 
- 		var result = colorString.get(obj);
 
- 		if (result === null) {
 
- 			throw new Error('Unable to parse color from string: ' + obj);
 
- 		}
 
- 		this.model = result.model;
 
- 		channels = convert[this.model].channels;
 
- 		this.color = result.value.slice(0, channels);
 
- 		this.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1;
 
- 	} else if (obj.length) {
 
- 		this.model = model || 'rgb';
 
- 		channels = convert[this.model].channels;
 
- 		var newArr = _slice.call(obj, 0, channels);
 
- 		this.color = zeroArray(newArr, channels);
 
- 		this.valpha = typeof obj[channels] === 'number' ? obj[channels] : 1;
 
- 	} else if (typeof obj === 'number') {
 
- 		// this is always RGB - can be converted later on.
 
- 		obj &= 0xFFFFFF;
 
- 		this.model = 'rgb';
 
- 		this.color = [
 
- 			(obj >> 16) & 0xFF,
 
- 			(obj >> 8) & 0xFF,
 
- 			obj & 0xFF
 
- 		];
 
- 		this.valpha = 1;
 
- 	} else {
 
- 		this.valpha = 1;
 
- 		var keys = Object.keys(obj);
 
- 		if ('alpha' in obj) {
 
- 			keys.splice(keys.indexOf('alpha'), 1);
 
- 			this.valpha = typeof obj.alpha === 'number' ? obj.alpha : 0;
 
- 		}
 
- 		var hashedKeys = keys.sort().join('');
 
- 		if (!(hashedKeys in hashedModelKeys)) {
 
- 			throw new Error('Unable to parse color from object: ' + JSON.stringify(obj));
 
- 		}
 
- 		this.model = hashedModelKeys[hashedKeys];
 
- 		var labels = convert[this.model].labels;
 
- 		var color = [];
 
- 		for (i = 0; i < labels.length; i++) {
 
- 			color.push(obj[labels[i]]);
 
- 		}
 
- 		this.color = zeroArray(color);
 
- 	}
 
- 	// perform limitations (clamping, etc.)
 
- 	if (limiters[this.model]) {
 
- 		channels = convert[this.model].channels;
 
- 		for (i = 0; i < channels; i++) {
 
- 			var limit = limiters[this.model][i];
 
- 			if (limit) {
 
- 				this.color[i] = limit(this.color[i]);
 
- 			}
 
- 		}
 
- 	}
 
- 	this.valpha = Math.max(0, Math.min(1, this.valpha));
 
- 	if (Object.freeze) {
 
- 		Object.freeze(this);
 
- 	}
 
- }
 
- Color.prototype = {
 
- 	toString: function () {
 
- 		return this.string();
 
- 	},
 
- 	toJSON: function () {
 
- 		return this[this.model]();
 
- 	},
 
- 	string: function (places) {
 
- 		var self = this.model in colorString.to ? this : this.rgb();
 
- 		self = self.round(typeof places === 'number' ? places : 1);
 
- 		var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);
 
- 		return colorString.to[self.model](args);
 
- 	},
 
- 	percentString: function (places) {
 
- 		var self = this.rgb().round(typeof places === 'number' ? places : 1);
 
- 		var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);
 
- 		return colorString.to.rgb.percent(args);
 
- 	},
 
- 	array: function () {
 
- 		return this.valpha === 1 ? this.color.slice() : this.color.concat(this.valpha);
 
- 	},
 
- 	object: function () {
 
- 		var result = {};
 
- 		var channels = convert[this.model].channels;
 
- 		var labels = convert[this.model].labels;
 
- 		for (var i = 0; i < channels; i++) {
 
- 			result[labels[i]] = this.color[i];
 
- 		}
 
- 		if (this.valpha !== 1) {
 
- 			result.alpha = this.valpha;
 
- 		}
 
- 		return result;
 
- 	},
 
- 	unitArray: function () {
 
- 		var rgb = this.rgb().color;
 
- 		rgb[0] /= 255;
 
- 		rgb[1] /= 255;
 
- 		rgb[2] /= 255;
 
- 		if (this.valpha !== 1) {
 
- 			rgb.push(this.valpha);
 
- 		}
 
- 		return rgb;
 
- 	},
 
- 	unitObject: function () {
 
- 		var rgb = this.rgb().object();
 
- 		rgb.r /= 255;
 
- 		rgb.g /= 255;
 
- 		rgb.b /= 255;
 
- 		if (this.valpha !== 1) {
 
- 			rgb.alpha = this.valpha;
 
- 		}
 
- 		return rgb;
 
- 	},
 
- 	round: function (places) {
 
- 		places = Math.max(places || 0, 0);
 
- 		return new Color(this.color.map(roundToPlace(places)).concat(this.valpha), this.model);
 
- 	},
 
- 	alpha: function (val) {
 
- 		if (arguments.length) {
 
- 			return new Color(this.color.concat(Math.max(0, Math.min(1, val))), this.model);
 
- 		}
 
- 		return this.valpha;
 
- 	},
 
- 	// rgb
 
- 	red: getset('rgb', 0, maxfn(255)),
 
- 	green: getset('rgb', 1, maxfn(255)),
 
- 	blue: getset('rgb', 2, maxfn(255)),
 
- 	hue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, function (val) { return ((val % 360) + 360) % 360; }), // eslint-disable-line brace-style
 
- 	saturationl: getset('hsl', 1, maxfn(100)),
 
- 	lightness: getset('hsl', 2, maxfn(100)),
 
- 	saturationv: getset('hsv', 1, maxfn(100)),
 
- 	value: getset('hsv', 2, maxfn(100)),
 
- 	chroma: getset('hcg', 1, maxfn(100)),
 
- 	gray: getset('hcg', 2, maxfn(100)),
 
- 	white: getset('hwb', 1, maxfn(100)),
 
- 	wblack: getset('hwb', 2, maxfn(100)),
 
- 	cyan: getset('cmyk', 0, maxfn(100)),
 
- 	magenta: getset('cmyk', 1, maxfn(100)),
 
- 	yellow: getset('cmyk', 2, maxfn(100)),
 
- 	black: getset('cmyk', 3, maxfn(100)),
 
- 	x: getset('xyz', 0, maxfn(100)),
 
- 	y: getset('xyz', 1, maxfn(100)),
 
- 	z: getset('xyz', 2, maxfn(100)),
 
- 	l: getset('lab', 0, maxfn(100)),
 
- 	a: getset('lab', 1),
 
- 	b: getset('lab', 2),
 
- 	keyword: function (val) {
 
- 		if (arguments.length) {
 
- 			return new Color(val);
 
- 		}
 
- 		return convert[this.model].keyword(this.color);
 
- 	},
 
- 	hex: function (val) {
 
- 		if (arguments.length) {
 
- 			return new Color(val);
 
- 		}
 
- 		return colorString.to.hex(this.rgb().round().color);
 
- 	},
 
- 	rgbNumber: function () {
 
- 		var rgb = this.rgb().color;
 
- 		return ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF);
 
- 	},
 
- 	luminosity: function () {
 
- 		// http://www.w3.org/TR/WCAG20/#relativeluminancedef
 
- 		var rgb = this.rgb().color;
 
- 		var lum = [];
 
- 		for (var i = 0; i < rgb.length; i++) {
 
- 			var chan = rgb[i] / 255;
 
- 			lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
 
- 		}
 
- 		return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
 
- 	},
 
- 	contrast: function (color2) {
 
- 		// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
 
- 		var lum1 = this.luminosity();
 
- 		var lum2 = color2.luminosity();
 
- 		if (lum1 > lum2) {
 
- 			return (lum1 + 0.05) / (lum2 + 0.05);
 
- 		}
 
- 		return (lum2 + 0.05) / (lum1 + 0.05);
 
- 	},
 
- 	level: function (color2) {
 
- 		var contrastRatio = this.contrast(color2);
 
- 		if (contrastRatio >= 7.1) {
 
- 			return 'AAA';
 
- 		}
 
- 		return (contrastRatio >= 4.5) ? 'AA' : '';
 
- 	},
 
- 	isDark: function () {
 
- 		// YIQ equation from http://24ways.org/2010/calculating-color-contrast
 
- 		var rgb = this.rgb().color;
 
- 		var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
 
- 		return yiq < 128;
 
- 	},
 
- 	isLight: function () {
 
- 		return !this.isDark();
 
- 	},
 
- 	negate: function () {
 
- 		var rgb = this.rgb();
 
- 		for (var i = 0; i < 3; i++) {
 
- 			rgb.color[i] = 255 - rgb.color[i];
 
- 		}
 
- 		return rgb;
 
- 	},
 
- 	lighten: function (ratio) {
 
- 		var hsl = this.hsl();
 
- 		hsl.color[2] += hsl.color[2] * ratio;
 
- 		return hsl;
 
- 	},
 
- 	darken: function (ratio) {
 
- 		var hsl = this.hsl();
 
- 		hsl.color[2] -= hsl.color[2] * ratio;
 
- 		return hsl;
 
- 	},
 
- 	saturate: function (ratio) {
 
- 		var hsl = this.hsl();
 
- 		hsl.color[1] += hsl.color[1] * ratio;
 
- 		return hsl;
 
- 	},
 
- 	desaturate: function (ratio) {
 
- 		var hsl = this.hsl();
 
- 		hsl.color[1] -= hsl.color[1] * ratio;
 
- 		return hsl;
 
- 	},
 
- 	whiten: function (ratio) {
 
- 		var hwb = this.hwb();
 
- 		hwb.color[1] += hwb.color[1] * ratio;
 
- 		return hwb;
 
- 	},
 
- 	blacken: function (ratio) {
 
- 		var hwb = this.hwb();
 
- 		hwb.color[2] += hwb.color[2] * ratio;
 
- 		return hwb;
 
- 	},
 
- 	grayscale: function () {
 
- 		// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
 
- 		var rgb = this.rgb().color;
 
- 		var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
 
- 		return Color.rgb(val, val, val);
 
- 	},
 
- 	fade: function (ratio) {
 
- 		return this.alpha(this.valpha - (this.valpha * ratio));
 
- 	},
 
- 	opaquer: function (ratio) {
 
- 		return this.alpha(this.valpha + (this.valpha * ratio));
 
- 	},
 
- 	rotate: function (degrees) {
 
- 		var hsl = this.hsl();
 
- 		var hue = hsl.color[0];
 
- 		hue = (hue + degrees) % 360;
 
- 		hue = hue < 0 ? 360 + hue : hue;
 
- 		hsl.color[0] = hue;
 
- 		return hsl;
 
- 	},
 
- 	mix: function (mixinColor, weight) {
 
- 		// ported from sass implementation in C
 
- 		// https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
 
- 		if (!mixinColor || !mixinColor.rgb) {
 
- 			throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor);
 
- 		}
 
- 		var color1 = mixinColor.rgb();
 
- 		var color2 = this.rgb();
 
- 		var p = weight === undefined ? 0.5 : weight;
 
- 		var w = 2 * p - 1;
 
- 		var a = color1.alpha() - color2.alpha();
 
- 		var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
 
- 		var w2 = 1 - w1;
 
- 		return Color.rgb(
 
- 				w1 * color1.red() + w2 * color2.red(),
 
- 				w1 * color1.green() + w2 * color2.green(),
 
- 				w1 * color1.blue() + w2 * color2.blue(),
 
- 				color1.alpha() * p + color2.alpha() * (1 - p));
 
- 	}
 
- };
 
- // model conversion methods and static constructors
 
- Object.keys(convert).forEach(function (model) {
 
- 	if (skippedModels.indexOf(model) !== -1) {
 
- 		return;
 
- 	}
 
- 	var channels = convert[model].channels;
 
- 	// conversion methods
 
- 	Color.prototype[model] = function () {
 
- 		if (this.model === model) {
 
- 			return new Color(this);
 
- 		}
 
- 		if (arguments.length) {
 
- 			return new Color(arguments, model);
 
- 		}
 
- 		var newAlpha = typeof arguments[channels] === 'number' ? channels : this.valpha;
 
- 		return new Color(assertArray(convert[this.model][model].raw(this.color)).concat(newAlpha), model);
 
- 	};
 
- 	// 'static' construction methods
 
- 	Color[model] = function (color) {
 
- 		if (typeof color === 'number') {
 
- 			color = zeroArray(_slice.call(arguments), channels);
 
- 		}
 
- 		return new Color(color, model);
 
- 	};
 
- });
 
- function roundTo(num, places) {
 
- 	return Number(num.toFixed(places));
 
- }
 
- function roundToPlace(places) {
 
- 	return function (num) {
 
- 		return roundTo(num, places);
 
- 	};
 
- }
 
- function getset(model, channel, modifier) {
 
- 	model = Array.isArray(model) ? model : [model];
 
- 	model.forEach(function (m) {
 
- 		(limiters[m] || (limiters[m] = []))[channel] = modifier;
 
- 	});
 
- 	model = model[0];
 
- 	return function (val) {
 
- 		var result;
 
- 		if (arguments.length) {
 
- 			if (modifier) {
 
- 				val = modifier(val);
 
- 			}
 
- 			result = this[model]();
 
- 			result.color[channel] = val;
 
- 			return result;
 
- 		}
 
- 		result = this[model]().color[channel];
 
- 		if (modifier) {
 
- 			result = modifier(result);
 
- 		}
 
- 		return result;
 
- 	};
 
- }
 
- function maxfn(max) {
 
- 	return function (v) {
 
- 		return Math.max(0, Math.min(max, v));
 
- 	};
 
- }
 
- function assertArray(val) {
 
- 	return Array.isArray(val) ? val : [val];
 
- }
 
- function zeroArray(arr, length) {
 
- 	for (var i = 0; i < length; i++) {
 
- 		if (typeof arr[i] !== 'number') {
 
- 			arr[i] = 0;
 
- 		}
 
- 	}
 
- 	return arr;
 
- }
 
- module.exports = Color;
 
 
  |