You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
13 KiB
377 lines
13 KiB
(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), '%'); |
|
}, |
|
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((0.2126 * (color.rgb[0]/255) + |
|
0.7152 * (color.rgb[1]/255) + |
|
0.0722 * (color.rgb[2]/255)) * |
|
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); |
|
} |
|
if (typeof threshold === 'undefined') { |
|
threshold = 0.43; |
|
} else { |
|
threshold = threshold.value; |
|
} |
|
if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * 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() : ""); |
|
}, |
|
round: function (n, f) { |
|
var fraction = typeof(f) === "undefined" ? 0 : f.value; |
|
return this._math(function(num) { return num.toFixed(fraction); }, n); |
|
}, |
|
ceil: function (n) { |
|
return this._math(Math.ceil, n); |
|
}, |
|
floor: function (n) { |
|
return this._math(Math.floor, n); |
|
}, |
|
_math: function (fn, n) { |
|
if (n instanceof tree.Dimension) { |
|
return new(tree.Dimension)(fn(parseFloat(n.value)), n.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 === 'px' ? tree.True : tree.False; |
|
}, |
|
ispercentage: function (n) { |
|
return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False; |
|
}, |
|
isem: function (n) { |
|
return (n instanceof tree.Dimension) && n.unit === '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); |
|
} |
|
}; |
|
|
|
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 == '%') { |
|
return parseFloat(n.value * size / 100); |
|
} else { |
|
return number(n); |
|
} |
|
} |
|
|
|
function number(n) { |
|
if (n instanceof tree.Dimension) { |
|
return parseFloat(n.unit == '%' ? 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)); |
|
} |
|
|
|
})(require('./tree'));
|
|
|