MediaWiki:Color.js

/*! * jQuery Color Animations v@VERSION * https://github.com/jquery/jquery-color * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * Date: @DATE */ (function (jQuery, undefined) {

var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",

// plusequals test for += 100 -= 100 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, // a set of RE's that can match strings and generate color tuples. stringParsers = [{ re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, parse: function (execResult) { return [ execResult[1], execResult[2], execResult[3], execResult[4]]; }		}, {			re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, parse: function (execResult) { return [ execResult[1] * 2.55, execResult[2] * 2.55, execResult[3] * 2.55, execResult[4]]; }		}, {			// this regex ignores A-F because it's compared against an already lowercased string re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/, parse: function (execResult) { return [ parseInt(execResult[1], 16), parseInt(execResult[2], 16), parseInt(execResult[3], 16)]; }		}, {			// this regex ignores A-F because it's compared against an already lowercased string re: /#([a-f0-9])([a-f0-9])([a-f0-9])/, parse: function (execResult) { return [ parseInt(execResult[1] + execResult[1], 16), parseInt(execResult[2] + execResult[2], 16), parseInt(execResult[3] + execResult[3], 16)]; }		}, {			re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/, space: "hsla", parse: function (execResult) { return [ execResult[1], execResult[2] / 100, execResult[3] / 100, execResult[4]]; }		}],

// jQuery.Color color = jQuery.Color = function (color, green, blue, alpha) { return new jQuery.Color.fn.parse(color, green, blue, alpha); },		spaces = { rgba: { props: { red: { idx: 0, type: "byte" },					green: { idx: 1, type: "byte" },					blue: { idx: 2, type: "byte" }				}			},

hsla: { props: { hue: { idx: 0, type: "degrees" },					saturation: { idx: 1, type: "percent" },					lightness: { idx: 2, type: "percent" }				}			}		},		propTypes = { "byte": { floor: true, max: 255 },				"percent": { max: 1 },				"degrees": { mod: 360, floor: true }		},		support = color.support = {},

// element for support tests supportElem = jQuery(" ")[0],

// colors = jQuery.Color.names colors,

// local aliases of functions called often each = jQuery.each;

// determine rgba support immediately supportElem.style.cssText = "background-color:rgba(1,1,1,.5)"; support.rgba = supportElem.style.backgroundColor.indexOf("rgba") > -1;

// define cache name and alpha properties // for rgba and hsla spaces each(spaces, function (spaceName, space) {		space.cache = "_" + spaceName;		space.props.alpha = {			idx: 3,			type: "percent",			def: 1		};	});

function clamp(value, prop, allowEmpty) { var type = propTypes[prop.type] || {};

if (value == null) { return (allowEmpty || !prop.def) ? null : prop.def; }

// is an short way of doing floor for positive numbers value = type.floor ? value : parseFloat(value);

// IE will pass in empty strings as value for alpha, // which will hit this case if (isNaN(value)) { return prop.def; }

if (type.mod) { // we add mod before modding to make sure that negatives values // get converted properly: -10 -> 350 return (value + type.mod) % type.mod; }

// for now all property types without mod have min and max return 0 > value ? 0 : type.max < value ? type.max : value; }

function stringParse(string) { var inst = color, rgba = inst._rgba = [];

string = string.toLowerCase;

each(stringParsers, function (i, parser) {			var parsed,			match = parser.re.exec(string),				values = match && parser.parse(match),				spaceName = parser.space || "rgba";

if (values) { parsed = inst[spaceName](values);

// if this was an rgba parse the assignment might happen twice // oh well.... inst[spaces[spaceName].cache] = parsed[spaces[spaceName].cache]; rgba = inst._rgba = parsed._rgba;

// exit each( stringParsers ) here because we matched return false; }		});

// Found a stringParser that handled it		if (rgba.length) {

// if this came from a parsed string, force "transparent" when alpha is 0 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) if (rgba.join === "0,0,0,0") { jQuery.extend(rgba, colors.transparent); }			return inst; }

// named colors return colors[string]; }

color.fn = jQuery.extend(color.prototype, {		parse: function (red, green, blue, alpha) {			if (red === undefined) {				this._rgba = [null, null, null, null];				return this;			}			if (red.jquery || red.nodeType) {				red = jQuery(red).css(green);				green = undefined;			}

var inst = this, type = jQuery.type(red), rgba = this._rgba = [];

// more than 1 argument specified - assume ( red, green, blue, alpha ) if (green !== undefined) { red = [red, green, blue, alpha]; type = "array"; }

if (type === "string") { return this.parse(stringParse(red) || colors._default); }

if (type === "array") { each(spaces.rgba.props, function (key, prop) {					rgba[prop.idx] = clamp(red[prop.idx], prop);				}); return this; }

if (type === "object") { if (red instanceof color) { each(spaces, function (spaceName, space) {						if (red[space.cache]) {							inst[space.cache] = red[space.cache].slice;						}					}); } else { each(spaces, function (spaceName, space) {						var cache = space.cache;						each(space.props, function (key, prop) {

// if the cache doesn't exist, and we know how to convert if (!inst[cache] && space.to) {

// if the value was null, we don't need to copy it								// if the key was alpha, we don't need to copy it either if (key === "alpha" || red[key] == null) { return; }								inst[cache] = space.to(inst._rgba); }

// this is the only case where we allow nulls for ALL properties. // call clamp with alwaysAllowEmpty inst[cache][prop.idx] = clamp(red[key], prop, true); });

// everything defined but alpha? if (inst[cache] && jQuery.inArray(null, inst[cache].slice(0, 3)) < 0) { // use the default of 1 inst[cache][3] = 1; if (space.from) { inst._rgba = space.from(inst[cache]); }						}					});				}				return this;			}		},		is: function (compare) {			var is = color(compare),				same = true,				inst = this;

each(spaces, function (_, space) {				var localCache,				isCache = is[space.cache];				if (isCache) {					localCache = inst[space.cache] || space.to && space.to(inst._rgba) || [];					each(space.props, function (_, prop) { if (isCache[prop.idx] != null) { same = (isCache[prop.idx] === localCache[prop.idx]); return same; }					});				}				return same;			}); return same; },		_space: function { var used = [], inst = this; each(spaces, function (spaceName, space) {				if (inst[space.cache]) {					used.push(spaceName);				}			}); return used.pop; },		transition: function (other, distance) { var end = color(other), spaceName = end._space, space = spaces[spaceName], startColor = this.alpha === 0 ? color("transparent") : this, start = startColor[space.cache] || space.to(startColor._rgba), result = start.slice;

end = end[space.cache]; each(space.props, function (key, prop) {				var index = prop.idx,					startValue = start[index],					endValue = end[index],					type = propTypes[prop.type] || {};

// if null, don't override start value if (endValue === null) { return; }				// if null - use end if (startValue === null) { result[index] = endValue; } else { if (type.mod) { if (endValue - startValue > type.mod / 2) { startValue += type.mod; } else if (startValue - endValue > type.mod / 2) { startValue -= type.mod; }					}					result[index] = clamp((endValue - startValue) * distance + startValue, prop); }			});			return this[spaceName](result);		},		blend: function (opaque) {			// if we are already opaque - return ourself			if (this._rgba[3] === 1) {				return this;			}

var rgb = this._rgba.slice, a = rgb.pop, blend = color(opaque)._rgba;

return color(jQuery.map(rgb, function (v, i) { return (1 - a) * blend[i] + a * v;			})); },		toRgbaString: function { var prefix = "rgba(",				rgba = jQuery.map(this._rgba, function (v, i) { return v == null ? (i > 2 ? 1 : 0) : v;				});

if (rgba[3] === 1) { rgba.pop; prefix = "rgb(";			}

return prefix + rgba.join + ")";		},		toHslaString: function {			var prefix = "hsla(", hsla = jQuery.map(this.hsla, function (v, i) {					if (v == null) {						v = i > 2 ? 1 : 0;					}

// catch 1 and 2 if (i && i < 3) { v = Math.round(v * 100) + "%"; }					return v;				});

if (hsla[3] === 1) { hsla.pop; prefix = "hsl(";			}			return prefix + hsla.join + ")"; },		toHexString: function (includeAlpha) { var rgba = this._rgba.slice, alpha = rgba.pop;

if (includeAlpha) { rgba.push( (alpha * 255)); }

return "#" + jQuery.map(rgba, function (v) {

// default to 0 when nulls exist v = (v || 0).toString(16); return v.length === 1 ? "0" + v : v;			}).join("");		},		toString: function {			return this._rgba[3] === 0 ? "transparent" : this.toRgbaString;		}	}); color.fn.parse.prototype = color.fn;

// hsla conversions adapted from: // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021

function hue2rgb(p, q, h) { h = (h + 1) % 1; if (h * 6 < 1) { return p + (q - p) * h * 6; }		if (h * 2 < 1) { return q;		} if (h * 3 < 2) { return p + (q - p) * ((2 / 3) - h) * 6; }		return p;	}

spaces.hsla.to = function (rgba) { if (rgba[0] == null || rgba[1] == null || rgba[2] == null) { return [null, null, null, rgba[3]]; }		var r = rgba[0] / 255, g = rgba[1] / 255, b = rgba[2] / 255, a = rgba[3], max = Math.max(r, g, b), min = Math.min(r, g, b), diff = max - min, add = max + min, l = add * 0.5, h, s;

if (min === max) { h = 0; } else if (r === max) { h = (60 * (g - b) / diff) + 360; } else if (g === max) { h = (60 * (b - r) / diff) + 120; } else { h = (60 * (r - g) / diff) + 240; }

// chroma (diff) == 0 means greyscale which, by definition, saturation = 0% // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add) if (diff === 0) { s = 0; } else if (l <= 0.5) { s = diff / add; } else { s = diff / (2 - add); }		return [Math.round(h) % 360, s, l, a == null ? 1 : a]; };

spaces.hsla.from = function (hsla) { if (hsla[0] == null || hsla[1] == null || hsla[2] == null) { return [null, null, null, hsla[3]]; }		var h = hsla[0] / 360, s = hsla[1], l = hsla[2], a = hsla[3], q = l <= 0.5 ? l * (1 + s) : l + s - l * s,			p = 2 * l - q;

return [ Math.round(hue2rgb(p, q, h + (1 / 3)) * 255), Math.round(hue2rgb(p, q, h) * 255), Math.round(hue2rgb(p, q, h - (1 / 3)) * 255), a]; };

each(spaces, function (spaceName, space) {		var props = space.props,			cache = space.cache,			to = space.to,			from = space.from;

// makes rgba and hsla color.fn[spaceName] = function (value) {

// generate a cache for this space if it doesn't exist if (to && !this[cache]) { this[cache] = to(this._rgba); }			if (value === undefined) { return this[cache].slice; }

var ret, type = jQuery.type(value), arr = (type === "array" || type === "object") ? value : arguments, local = this[cache].slice;

each(props, function (key, prop) {				var val = arr[type === "object" ? key : prop.idx];				if (val == null) {					val = local[prop.idx];				}				local[prop.idx] = clamp(val, prop);			});

if (from) { ret = color(from(local)); ret[cache] = local; return ret; } else { return color(local); }		};

// makes red green blue alpha hue saturation lightness each(props, function (key, prop) {			// alpha is included in more than one space			if (color.fn[key]) {				return;			}			color.fn[key] = function (value) {				var vtype = jQuery.type(value),					fn = (key === "alpha" ? (this._hsla ? "hsla" : "rgba") : spaceName),					local = this[fn],					cur = local[prop.idx],					match;

if (vtype === "undefined") { return cur; }

if (vtype === "function") { value = value.call(this, cur); vtype = jQuery.type(value); }				if (value == null && prop.empty) { return this; }				if (vtype === "string") { match = rplusequals.exec(value); if (match) { value = cur + parseFloat(match[2]) * (match[1] === "+" ? 1 : -1); }				}				local[prop.idx] = value; return this[fn](local); };		});	});

// add cssHook and .fx.step function for each named hook. // accept a space separated string of properties color.hook = function (hook) { var hooks = hook.split(" "); each(hooks, function (i, hook) {			jQuery.cssHooks[hook] = {				set: function (elem, value) {					var parsed, curElem,					backgroundColor = "";

if (jQuery.type(value) !== "string" || (parsed = stringParse(value))) { value = color(parsed || value); if (!support.rgba && value._rgba[3] !== 1) { curElem = hook === "backgroundColor" ? elem.parentNode : elem; while (							(backgroundColor === "" || backgroundColor === "transparent") && curElem && curElem.style) { try { backgroundColor = jQuery.css(curElem, "backgroundColor"); curElem = curElem.parentNode; } catch (e) {} }

value = value.blend(backgroundColor && backgroundColor !== "transparent" ? backgroundColor :								"_default"); }

value = value.toRgbaString; }					try { elem.style[hook] = value; } catch (e) { // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit' }				}			};			jQuery.fx.step[hook] = function (fx) { if (!fx.colorInit) { fx.start = color(fx.elem, hook); fx.end = color(fx.end); fx.colorInit = true; }				jQuery.cssHooks[hook].set(fx.elem, fx.start.transition(fx.end, fx.pos)); };		});

};

color.hook(stepHooks);

jQuery.cssHooks.borderColor = { expand: function (value) { var expanded = {};

each(["Top", "Right", "Bottom", "Left"], function (i, part) {				expanded["border" + part + "Color"] = value;			}); return expanded; }	};

// Basic color names only. // Usage of any of the other color names requires adding yourself or including // jquery.color.svg-names.js. colors = jQuery.Color.names = { // 4.1. Basic color keywords aqua: "#00ffff", black: "#000000", blue: "#0000ff", fuchsia: "#ff00ff", gray: "#808080", green: "#008000", lime: "#00ff00", maroon: "#800000", navy: "#000080", olive: "#808000", purple: "#800080", red: "#ff0000", silver: "#c0c0c0", teal: "#008080", white: "#ffffff", yellow: "#ffff00",

// 4.2.3. ‘transparent’ color keyword transparent: [null, null, null, 0],

_default: "#ffffff" };

})(jQuery);