Files
less.js/lib/less/functions.js
Marcus Bointon 26d35c98fe Merge pull request #1148 from Synchro/luma
Better implementation of luma
2013-01-23 01:30:26 -08:00

466 lines
16 KiB
JavaScript

(function (tree) {
tree.functions = {
rgb: function (r, g, b) {
return this.rgba(r, g, b, 1.0);
},
rgba: function (r, g, b, a) {
var rgb = [r, g, b].map(function (c) { return scaled(c, 256); });
a = number(a);
return new(tree.Color)(rgb, a);
},
hsl: function (h, s, l) {
return this.hsla(h, s, l, 1.0);
},
hsla: function (h, s, l, a) {
h = (number(h) % 360) / 360;
s = number(s); l = number(l); a = number(a);
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
var m1 = l * 2 - m2;
return this.rgba(hue(h + 1/3) * 255,
hue(h) * 255,
hue(h - 1/3) * 255,
a);
function hue(h) {
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
else if (h * 2 < 1) return m2;
else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
else return m1;
}
},
hsv: function(h, s, v) {
return this.hsva(h, s, v, 1.0);
},
hsva: function(h, s, v, a) {
h = ((number(h) % 360) / 360) * 360;
s = number(s); v = number(v); a = number(a);
var i, f;
i = Math.floor((h / 60) % 6);
f = (h / 60) - i;
var vs = [v,
v * (1 - s),
v * (1 - f * s),
v * (1 - (1 - f) * s)];
var perm = [[0, 3, 1],
[2, 0, 1],
[1, 0, 3],
[1, 2, 0],
[3, 1, 0],
[0, 1, 2]];
return this.rgba(vs[perm[i][0]] * 255,
vs[perm[i][1]] * 255,
vs[perm[i][2]] * 255,
a);
},
hue: function (color) {
return new(tree.Dimension)(Math.round(color.toHSL().h));
},
saturation: function (color) {
return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');
},
lightness: function (color) {
return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
},
hsvhue: function(color) {
return new(tree.Dimension)(Math.round(color.toHSV().h));
},
hsvsaturation: function (color) {
return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%');
},
hsvvalue: function (color) {
return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%');
},
red: function (color) {
return new(tree.Dimension)(color.rgb[0]);
},
green: function (color) {
return new(tree.Dimension)(color.rgb[1]);
},
blue: function (color) {
return new(tree.Dimension)(color.rgb[2]);
},
alpha: function (color) {
return new(tree.Dimension)(color.toHSL().a);
},
luma: function (color) {
return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%');
},
saturate: function (color, amount) {
var hsl = color.toHSL();
hsl.s += amount.value / 100;
hsl.s = clamp(hsl.s);
return hsla(hsl);
},
desaturate: function (color, amount) {
var hsl = color.toHSL();
hsl.s -= amount.value / 100;
hsl.s = clamp(hsl.s);
return hsla(hsl);
},
lighten: function (color, amount) {
var hsl = color.toHSL();
hsl.l += amount.value / 100;
hsl.l = clamp(hsl.l);
return hsla(hsl);
},
darken: function (color, amount) {
var hsl = color.toHSL();
hsl.l -= amount.value / 100;
hsl.l = clamp(hsl.l);
return hsla(hsl);
},
fadein: function (color, amount) {
var hsl = color.toHSL();
hsl.a += amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
},
fadeout: function (color, amount) {
var hsl = color.toHSL();
hsl.a -= amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
},
fade: function (color, amount) {
var hsl = color.toHSL();
hsl.a = amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
},
spin: function (color, amount) {
var hsl = color.toHSL();
var hue = (hsl.h + amount.value) % 360;
hsl.h = hue < 0 ? 360 + hue : hue;
return hsla(hsl);
},
//
// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
// http://sass-lang.com
//
mix: function (color1, color2, weight) {
if (!weight) {
weight = new(tree.Dimension)(50);
}
var p = weight.value / 100.0;
var w = p * 2 - 1;
var a = color1.toHSL().a - color2.toHSL().a;
var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
var w2 = 1 - w1;
var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
color1.rgb[1] * w1 + color2.rgb[1] * w2,
color1.rgb[2] * w1 + color2.rgb[2] * w2];
var alpha = color1.alpha * p + color2.alpha * (1 - p);
return new(tree.Color)(rgb, alpha);
},
greyscale: function (color) {
return this.desaturate(color, new(tree.Dimension)(100));
},
contrast: function (color, dark, light, threshold) {
// filter: contrast(3.2);
// should be kept as is, so check for color
if (!color.rgb) {
return null;
}
if (typeof light === 'undefined') {
light = this.rgba(255, 255, 255, 1.0);
}
if (typeof dark === 'undefined') {
dark = this.rgba(0, 0, 0, 1.0);
}
//Figure out which is actually light and dark!
if (dark.luma() > light.luma()) {
var t = light;
light = dark;
dark = t;
}
if (typeof threshold === 'undefined') {
threshold = 0.43;
} else {
threshold = number(threshold);
}
if ((color.luma() * color.alpha) < threshold) {
return light;
} else {
return dark;
}
},
e: function (str) {
return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
},
escape: function (str) {
return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
},
'%': function (quoted /* arg, arg, ...*/) {
var args = Array.prototype.slice.call(arguments, 1),
str = quoted.value;
for (var i = 0; i < args.length; i++) {
str = str.replace(/%[sda]/i, function(token) {
var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
});
}
str = str.replace(/%%/g, '%');
return new(tree.Quoted)('"' + str + '"', str);
},
unit: function (val, unit) {
return new(tree.Dimension)(val.value, unit ? unit.toCSS() : "");
},
convert: function (val, unit) {
return val.convertTo(unit.value);
},
round: function (n, f) {
var fraction = typeof(f) === "undefined" ? 0 : f.value;
return this._math(function(num) { return num.toFixed(fraction); }, null, n);
},
pi: function () {
return new(tree.Dimension)(Math.PI);
},
mod: function(a, b) {
return new(tree.Dimension)(a.value % b.value, a.unit);
},
pow: function(x, y) {
if (typeof x === "number" && typeof y === "number") {
x = new(tree.Dimension)(x);
y = new(tree.Dimension)(y);
} else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) {
throw { type: "Argument", message: "arguments must be numbers" };
}
return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit);
},
_math: function (fn, unit, n) {
if (n instanceof tree.Dimension) {
return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit);
} else if (typeof(n) === 'number') {
return fn(n);
} else {
throw { type: "Argument", message: "argument must be a number" };
}
},
argb: function (color) {
return new(tree.Anonymous)(color.toARGB());
},
percentage: function (n) {
return new(tree.Dimension)(n.value * 100, '%');
},
color: function (n) {
if (n instanceof tree.Quoted) {
return new(tree.Color)(n.value.slice(1));
} else {
throw { type: "Argument", message: "argument must be a string" };
}
},
iscolor: function (n) {
return this._isa(n, tree.Color);
},
isnumber: function (n) {
return this._isa(n, tree.Dimension);
},
isstring: function (n) {
return this._isa(n, tree.Quoted);
},
iskeyword: function (n) {
return this._isa(n, tree.Keyword);
},
isurl: function (n) {
return this._isa(n, tree.URL);
},
ispixel: function (n) {
return (n instanceof tree.Dimension) && n.unit.is('px') ? tree.True : tree.False;
},
ispercentage: function (n) {
return (n instanceof tree.Dimension) && n.unit.is('%') ? tree.True : tree.False;
},
isem: function (n) {
return (n instanceof tree.Dimension) && n.unit.is('em') ? tree.True : tree.False;
},
_isa: function (n, Type) {
return (n instanceof Type) ? tree.True : tree.False;
},
/* Blending modes */
multiply: function(color1, color2) {
var r = color1.rgb[0] * color2.rgb[0] / 255;
var g = color1.rgb[1] * color2.rgb[1] / 255;
var b = color1.rgb[2] * color2.rgb[2] / 255;
return this.rgb(r, g, b);
},
screen: function(color1, color2) {
var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
return this.rgb(r, g, b);
},
overlay: function(color1, color2) {
var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
return this.rgb(r, g, b);
},
softlight: function(color1, color2) {
var t = color2.rgb[0] * color1.rgb[0] / 255;
var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255;
t = color2.rgb[1] * color1.rgb[1] / 255;
var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255;
t = color2.rgb[2] * color1.rgb[2] / 255;
var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255;
return this.rgb(r, g, b);
},
hardlight: function(color1, color2) {
var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255;
var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255;
var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255;
return this.rgb(r, g, b);
},
difference: function(color1, color2) {
var r = Math.abs(color1.rgb[0] - color2.rgb[0]);
var g = Math.abs(color1.rgb[1] - color2.rgb[1]);
var b = Math.abs(color1.rgb[2] - color2.rgb[2]);
return this.rgb(r, g, b);
},
exclusion: function(color1, color2) {
var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255;
var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255;
var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255;
return this.rgb(r, g, b);
},
average: function(color1, color2) {
var r = (color1.rgb[0] + color2.rgb[0]) / 2;
var g = (color1.rgb[1] + color2.rgb[1]) / 2;
var b = (color1.rgb[2] + color2.rgb[2]) / 2;
return this.rgb(r, g, b);
},
negation: function(color1, color2) {
var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]);
var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]);
var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]);
return this.rgb(r, g, b);
},
tint: function(color, amount) {
return this.mix(this.rgb(255,255,255), color, amount);
},
shade: function(color, amount) {
return this.mix(this.rgb(0, 0, 0), color, amount);
},
extract: function(values, index) {
index = index.value - 1; // (1-based index)
return values.value[index];
},
"data-uri": function(mimetype, path) {
if (typeof window !== 'undefined') {
return new less.tree.URL(path || mimetype, this.rootpath).eval(this.env);
}
mimetype = mimetype.value;
path = (path && path.value);
var fs = require('fs');
var useBase64 = false;
// detect the mimetype if not given
if (arguments.length < 2) {
var mime = require('mime');
path = mimetype;
mimetype = mime.lookup(path);
// use base 64 unless it's an ASCII or UTF-8 format
var charset = mime.charsets.lookup(mimetype);
useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
if (useBase64) mimetype += ';base64';
}
else {
useBase64 = /;base64$/.test(mimetype)
}
var buf = fs.readFileSync(path);
buf = useBase64 ? buf.toString('base64')
: encodeURIComponent(buf);
var uri = "'data:"+mimetype+','+buf+"'";
return new(tree.URL)(new(tree.Anonymous)(uri));
}
};
var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"},
{name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""},
{name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}],
createMathFunction = function(name, unit) {
return function(n) {
if (unit != null) {
n = n.unify();
}
return this._math(Math[name], unit, n);
};
};
for(var i = 0; i < mathFunctions.length; i++) {
tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit);
}
function hsla(color) {
return tree.functions.hsla(color.h, color.s, color.l, color.a);
}
function scaled(n, size) {
if (n instanceof tree.Dimension && n.unit.is('%')) {
return parseFloat(n.value * size / 100);
} else {
return number(n);
}
}
function number(n) {
if (n instanceof tree.Dimension) {
return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
} else if (typeof(n) === 'number') {
return n;
} else {
throw {
error: "RuntimeError",
message: "color functions take numbers as parameters"
};
}
}
function clamp(val) {
return Math.min(1, Math.max(0, val));
}
tree.functionCall = function(env, rootpath) {
this.env = env;
this.rootpath = rootpath;
};
tree.functionCall.prototype = tree.functions;
})(require('./tree'));