[Pkg-javascript-commits] [less.js] 60/285: move functions into its own folder and give it a set interface
Jonas Smedegaard
dr at jones.dk
Mon Oct 26 23:23:38 UTC 2015
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag v2.0.0
in repository less.js.
commit d7e7ddba44bbb71daf7909d52b7921bde3e5bf67
Author: Luke Page <luke.a.page at gmail.com>
Date: Sat Aug 16 13:17:35 2014 +0100
move functions into its own folder and give it a set interface
---
lib/less/browser.js | 8 +-
lib/less/functions.js | 736 --------------------------------
lib/less/functions/color-blending.js | 74 ++++
lib/less/functions/color.js | 272 ++++++++++++
lib/less/functions/data-uri.js | 69 +++
lib/less/functions/default.js | 25 ++
lib/less/functions/function-caller.js | 15 +
lib/less/functions/function-registry.js | 18 +
lib/less/functions/index.js | 18 +
lib/less/functions/math.js | 42 ++
lib/less/functions/number.js | 74 ++++
lib/less/functions/string.js | 30 ++
lib/less/functions/svg.js | 77 ++++
lib/less/functions/types.js | 64 +++
lib/less/non-node-index.js | 4 +-
lib/less/tree.js | 12 +-
lib/less/tree/call.js | 12 +-
test/less-test.js | 20 +-
18 files changed, 807 insertions(+), 763 deletions(-)
diff --git a/lib/less/browser.js b/lib/less/browser.js
index 2023c54..aed3d99 100644
--- a/lib/less/browser.js
+++ b/lib/less/browser.js
@@ -54,11 +54,7 @@ less.poll = less.poll || (isFileProtocol ? 1000 : 1500);
//Setup user functions
if (options.functions) {
- for(var func in options.functions) {
- if (options.functions.hasOwnProperty(func)) {
- less.tree.functions[func] = options.functions[func];
- }
- }
+ less.functions.functionRegistry.addMultiple(options.functions);
}
var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);
@@ -352,7 +348,7 @@ function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
}
less.environment.loadFile(env, sheet.href, null, function loadInitialFileCallback(e, data, path, webInfo) {
-
+
var newFileInfo = {
currentDirectory: less.environment.getPath(env, path),
filename: path,
diff --git a/lib/less/functions.js b/lib/less/functions.js
deleted file mode 100644
index e55f6af..0000000
--- a/lib/less/functions.js
+++ /dev/null
@@ -1,736 +0,0 @@
-module.exports = function (less, tree) {
-
-var 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, 255); });
- 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) {
- 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; }
- }
-
- h = (number(h) % 360) / 360;
- s = clamp(number(s)); l = clamp(number(l)); a = clamp(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);
- },
-
- 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)(color.toHSL().h);
- },
- saturation: function (color) {
- return new(tree.Dimension)(color.toHSL().s * 100, '%');
- },
- lightness: function (color) {
- return new(tree.Dimension)(color.toHSL().l * 100, '%');
- },
- hsvhue: function(color) {
- return new(tree.Dimension)(color.toHSV().h);
- },
- hsvsaturation: function (color) {
- return new(tree.Dimension)(color.toHSV().s * 100, '%');
- },
- hsvvalue: function (color) {
- return new(tree.Dimension)(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)(color.luma() * color.alpha * 100, '%');
- },
- luminance: function (color) {
- var luminance =
- (0.2126 * color.rgb[0] / 255)
- + (0.7152 * color.rgb[1] / 255)
- + (0.0722 * color.rgb[2] / 255);
-
- return new(tree.Dimension)(luminance * color.alpha * 100, '%');
- },
- saturate: function (color, amount) {
- // filter: saturate(3.2);
- // should be kept as is, so check for color
- if (!color.rgb) {
- return null;
- }
- 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() < threshold) {
- return light;
- } else {
- return dark;
- }
- },
- e: function (str) {
- return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value);
- },
- 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"));
- },
- replace: function (string, pattern, replacement, flags) {
- var result = string.value;
-
- result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value);
- return new(tree.Quoted)(string.quote || '', result, string.escaped);
- },
- '%': function (string /* arg, arg, ...*/) {
- var args = Array.prototype.slice.call(arguments, 1),
- result = string.value;
-
- for (var i = 0; i < args.length; i++) {
- /*jshint loopfunc:true */
- result = result.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;
- });
- }
- result = result.replace(/%%/g, '%');
- return new(tree.Quoted)(string.quote || '', result, string.escaped);
- },
- unit: function (val, unit) {
- if(!(val instanceof tree.Dimension)) {
- throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") };
- }
- if (unit) {
- if (unit instanceof tree.Keyword) {
- unit = unit.value;
- } else {
- unit = unit.toCSS();
- }
- } else {
- unit = "";
- }
- return new(tree.Dimension)(val.value, unit);
- },
- convert: function (val, unit) {
- return val.convertTo(unit.value);
- },
- round: function (n, f) {
- var fraction = typeof(f) === "undefined" ? 0 : f.value;
- return _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);
- },
- _minmax: function (isMin, args) {
- args = Array.prototype.slice.call(args);
- switch(args.length) {
- case 0: throw { type: "Argument", message: "one or more arguments required" };
- }
- var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
- order = [], // elems only contains original argument values.
- values = {}; // key is the unit.toString() for unified tree.Dimension values,
- // value is the index into the order array.
- for (i = 0; i < args.length; i++) {
- current = args[i];
- if (!(current instanceof tree.Dimension)) {
- if(Array.isArray(args[i].value)) {
- Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
- }
- continue;
- }
- currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify();
- unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
- unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic;
- unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
- j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
- if (j === undefined) {
- if(unitStatic !== undefined && unit !== unitStatic) {
- throw{ type: "Argument", message: "incompatible types" };
- }
- values[unit] = order.length;
- order.push(current);
- continue;
- }
- referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify();
- if ( isMin && currentUnified.value < referenceUnified.value ||
- !isMin && currentUnified.value > referenceUnified.value) {
- order[j] = current;
- }
- }
- if (order.length == 1) {
- return order[0];
- }
- args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", ");
- return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")");
- },
- min: function () {
- return this._minmax(true, arguments);
- },
- max: function () {
- return this._minmax(false, arguments);
- },
- "get-unit": function (n) {
- return new(tree.Anonymous)(n.unit);
- },
- argb: function (color) {
- return new(tree.Anonymous)(color.toARGB());
- },
- percentage: function (n) {
- return new(tree.Dimension)(n.value * 100, '%');
- },
- color: function(c) {
- if ((c instanceof tree.Quoted) &&
- (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
- return new(tree.Color)(c.value.slice(1));
- }
- if ((c instanceof tree.Color) || (c = tree.Color.fromKeyword(c.value))) {
- c.keyword = undefined;
- return c;
- }
- throw {
- type: "Argument",
- message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF"
- };
- },
- 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 this.isunit(n, 'px');
- },
- ispercentage: function (n) {
- return this.isunit(n, '%');
- },
- isem: function (n) {
- return this.isunit(n, 'em');
- },
- isunit: function (n, unit) {
- return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;
- },
- _isa: function (n, Type) {
- return (n instanceof Type) ? tree.True : tree.False;
- },
- 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)
- // handle non-array values as an array of length 1
- // return 'undefined' if index is invalid
- return Array.isArray(values.value)
- ? values.value[index] : Array(values)[index];
- },
- length: function(values) {
- var n = Array.isArray(values.value) ? values.value.length : 1;
- return new tree.Dimension(n);
- },
-
- "data-uri": function(mimetypeNode, filePathNode) {
-
- if (!less.environment.supportsDataURI(this.env)) {
- return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
- }
-
- var mimetype = mimetypeNode.value;
- var filePath = (filePathNode && filePathNode.value);
-
- var useBase64 = false;
-
- if (arguments.length < 2) {
- filePath = mimetype;
- }
-
- var fragmentStart = filePath.indexOf('#');
- var fragment = '';
- if (fragmentStart!==-1) {
- fragment = filePath.slice(fragmentStart);
- filePath = filePath.slice(0, fragmentStart);
- }
-
- if (this.env.isPathRelative(filePath)) {
- if (this.currentFileInfo.relativeUrls) {
- filePath = less.environment.join(this.currentFileInfo.currentDirectory, filePath);
- } else {
- filePath = less.environment.join(this.currentFileInfo.entryPath, filePath);
- }
- }
-
- // detect the mimetype if not given
- if (arguments.length < 2) {
-
- mimetype = less.environment.mimeLookup(this.env, filePath);
-
- // use base 64 unless it's an ASCII or UTF-8 format
- var charset = less.environment.charsetLookup(this.env, mimetype);
- useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
- if (useBase64) { mimetype += ';base64'; }
- }
- else {
- useBase64 = /;base64$/.test(mimetype);
- }
-
- var buf = less.environment.readFileSync(filePath);
-
- // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
- // and the --ieCompat flag is enabled, return a normal url() instead.
- var DATA_URI_MAX_KB = 32,
- fileSizeInKB = parseInt((buf.length / 1024), 10);
- if (fileSizeInKB >= DATA_URI_MAX_KB) {
-
- if (this.env.ieCompat !== false) {
- if (!this.env.silent) {
- console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
- }
-
- return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
- }
- }
-
- buf = useBase64 ? buf.toString('base64')
- : encodeURIComponent(buf);
-
- var uri = "\"data:" + mimetype + ',' + buf + fragment + "\"";
- return new(tree.URL)(new(tree.Anonymous)(uri));
- },
-
- "svg-gradient": function(direction) {
-
- function throwArgumentDescriptor() {
- throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
- }
-
- if (arguments.length < 3) {
- throwArgumentDescriptor();
- }
- var stops = Array.prototype.slice.call(arguments, 1),
- gradientDirectionSvg,
- gradientType = "linear",
- rectangleDimension = 'x="0" y="0" width="1" height="1"',
- useBase64 = true,
- renderEnv = {compress: false},
- returner,
- directionValue = direction.toCSS(renderEnv),
- i, color, position, positionValue, alpha;
-
- switch (directionValue) {
- case "to bottom":
- gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
- break;
- case "to right":
- gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
- break;
- case "to bottom right":
- gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
- break;
- case "to top right":
- gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
- break;
- case "ellipse":
- case "ellipse at center":
- gradientType = "radial";
- gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
- rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
- break;
- default:
- throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
- }
- returner = '<?xml version="1.0" ?>' +
- '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
- '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
-
- for (i = 0; i < stops.length; i+= 1) {
- if (stops[i].value) {
- color = stops[i].value[0];
- position = stops[i].value[1];
- } else {
- color = stops[i];
- position = undefined;
- }
-
- if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) {
- throwArgumentDescriptor();
- }
- positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
- alpha = color.alpha;
- returner += '<stop offset="' + positionValue + '" stop-color="' + color.toRGB() + '"' + (alpha < 1 ? ' stop-opacity="' + alpha + '"' : '') + '/>';
- }
- returner += '</' + gradientType + 'Gradient>' +
- '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
-
- if (useBase64) {
- try {
- returner = less.environment.encodeBase64(this.env, returner);
- } catch(e) {
- useBase64 = false;
- }
- }
-
- returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
- return new(tree.URL)(new(tree.Anonymous)(returner));
- }
-};
-
-// Math
-
-var mathFunctions = {
- // name, unit
- ceil: null,
- floor: null,
- sqrt: null,
- abs: null,
- tan: "",
- sin: "",
- cos: "",
- atan: "rad",
- asin: "rad",
- acos: "rad"
-};
-
-function _math(fn, unit, n) {
- if (!(n instanceof tree.Dimension)) {
- throw { type: "Argument", message: "argument must be a number" };
- }
- if (unit == null) {
- unit = n.unit;
- } else {
- n = n.unify();
- }
- return new(tree.Dimension)(fn(parseFloat(n.value)), unit);
-}
-
-// ~ End of Math
-
-// Color Blending
-// ref: http://www.w3.org/TR/compositing-1
-
-function colorBlend(mode, color1, color2) {
- var ab = color1.alpha, cb, // backdrop
- as = color2.alpha, cs, // source
- ar, cr, r = []; // result
-
- ar = as + ab * (1 - as);
- for (var i = 0; i < 3; i++) {
- cb = color1.rgb[i] / 255;
- cs = color2.rgb[i] / 255;
- cr = mode(cb, cs);
- if (ar) {
- cr = (as * cs + ab * (cb
- - as * (cb + cs - cr))) / ar;
- }
- r[i] = cr * 255;
- }
-
- return new(tree.Color)(r, ar);
-}
-
-var colorBlendMode = {
- multiply: function(cb, cs) {
- return cb * cs;
- },
- screen: function(cb, cs) {
- return cb + cs - cb * cs;
- },
- overlay: function(cb, cs) {
- cb *= 2;
- return (cb <= 1)
- ? colorBlendMode.multiply(cb, cs)
- : colorBlendMode.screen(cb - 1, cs);
- },
- softlight: function(cb, cs) {
- var d = 1, e = cb;
- if (cs > 0.5) {
- e = 1;
- d = (cb > 0.25) ? Math.sqrt(cb)
- : ((16 * cb - 12) * cb + 4) * cb;
- }
- return cb - (1 - 2 * cs) * e * (d - cb);
- },
- hardlight: function(cb, cs) {
- return colorBlendMode.overlay(cs, cb);
- },
- difference: function(cb, cs) {
- return Math.abs(cb - cs);
- },
- exclusion: function(cb, cs) {
- return cb + cs - 2 * cb * cs;
- },
-
- // non-w3c functions:
- average: function(cb, cs) {
- return (cb + cs) / 2;
- },
- negation: function(cb, cs) {
- return 1 - Math.abs(cb + cs - 1);
- }
-};
-
-// ~ End of Color Blending
-
-tree.defaultFunc = {
- eval: function () {
- var v = this.value_, e = this.error_;
- if (e) {
- throw e;
- }
- if (v != null) {
- return v ? tree.True : tree.False;
- }
- },
- value: function (v) {
- this.value_ = v;
- },
- error: function (e) {
- this.error_ = e;
- },
- reset: function () {
- this.value_ = this.error_ = null;
- }
-};
-
-function initFunctions() {
- var f;
-
- // math
- for (f in mathFunctions) {
- if (mathFunctions.hasOwnProperty(f)) {
- functions[f] = _math.bind(null, Math[f], mathFunctions[f]);
- }
- }
-
- // color blending
- for (f in colorBlendMode) {
- if (colorBlendMode.hasOwnProperty(f)) {
- functions[f] = colorBlend.bind(null, colorBlendMode[f]);
- }
- }
-
- // default
- f = tree.defaultFunc;
- functions["default"] = f.eval.bind(f);
-
-} initFunctions();
-
-function hsla(color) {
- return 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.fround = function(env, value) {
- var p = env && env.numPrecision;
- //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
- return (p == null) ? value : Number((value + 2e-16).toFixed(p));
-};
-
-tree.functionCall = function(env, currentFileInfo, environment) {
- this.env = env;
- this.environment = environment;
- this.currentFileInfo = currentFileInfo;
-};
-
-tree.functionCall.prototype = functions;
-
-return functions;
-
-};
diff --git a/lib/less/functions/color-blending.js b/lib/less/functions/color-blending.js
new file mode 100644
index 0000000..c24b549
--- /dev/null
+++ b/lib/less/functions/color-blending.js
@@ -0,0 +1,74 @@
+module.exports = function(functions, tree) {
+
+ // Color Blending
+ // ref: http://www.w3.org/TR/compositing-1
+
+ function colorBlend(mode, color1, color2) {
+ var ab = color1.alpha, cb, // backdrop
+ as = color2.alpha, cs, // source
+ ar, cr, r = []; // result
+
+ ar = as + ab * (1 - as);
+ for (var i = 0; i < 3; i++) {
+ cb = color1.rgb[i] / 255;
+ cs = color2.rgb[i] / 255;
+ cr = mode(cb, cs);
+ if (ar) {
+ cr = (as * cs + ab * (cb
+ - as * (cb + cs - cr))) / ar;
+ }
+ r[i] = cr * 255;
+ }
+
+ return new(tree.Color)(r, ar);
+ }
+
+ var colorBlendModeFunctions = {
+ multiply: function(cb, cs) {
+ return cb * cs;
+ },
+ screen: function(cb, cs) {
+ return cb + cs - cb * cs;
+ },
+ overlay: function(cb, cs) {
+ cb *= 2;
+ return (cb <= 1)
+ ? colorBlendModeFunctions.multiply(cb, cs)
+ : colorBlendModeFunctions.screen(cb - 1, cs);
+ },
+ softlight: function(cb, cs) {
+ var d = 1, e = cb;
+ if (cs > 0.5) {
+ e = 1;
+ d = (cb > 0.25) ? Math.sqrt(cb)
+ : ((16 * cb - 12) * cb + 4) * cb;
+ }
+ return cb - (1 - 2 * cs) * e * (d - cb);
+ },
+ hardlight: function(cb, cs) {
+ return colorBlendModeFunctions.overlay(cs, cb);
+ },
+ difference: function(cb, cs) {
+ return Math.abs(cb - cs);
+ },
+ exclusion: function(cb, cs) {
+ return cb + cs - 2 * cb * cs;
+ },
+
+ // non-w3c functions:
+ average: function(cb, cs) {
+ return (cb + cs) / 2;
+ },
+ negation: function(cb, cs) {
+ return 1 - Math.abs(cb + cs - 1);
+ }
+ };
+
+ for (var f in colorBlendModeFunctions) {
+ if (colorBlendModeFunctions.hasOwnProperty(f)) {
+ colorBlend[f] = colorBlend.bind(null, colorBlendModeFunctions[f]);
+ }
+ }
+
+ functions.functionRegistry.addMultiple(colorBlend);
+};
diff --git a/lib/less/functions/color.js b/lib/less/functions/color.js
new file mode 100644
index 0000000..1b493bc
--- /dev/null
+++ b/lib/less/functions/color.js
@@ -0,0 +1,272 @@
+module.exports = function(functions, tree) {
+ function clamp(val) {
+ return Math.min(1, Math.max(0, val));
+ }
+ function hsla(color) {
+ return colorFunctions.hsla(color.h, color.s, color.l, color.a);
+ }
+ 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 scaled(n, size) {
+ if (n instanceof tree.Dimension && n.unit.is('%')) {
+ return parseFloat(n.value * size / 100);
+ } else {
+ return number(n);
+ }
+ }
+ var colorFunctions = {
+ rgb: function (r, g, b) {
+ return colorFunctions.rgba(r, g, b, 1.0);
+ },
+ rgba: function (r, g, b, a) {
+ var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
+ a = number(a);
+ return new(tree.Color)(rgb, a);
+ },
+ hsl: function (h, s, l) {
+ return colorFunctions.hsla(h, s, l, 1.0);
+ },
+ hsla: function (h, s, l, 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; }
+ }
+
+ h = (number(h) % 360) / 360;
+ s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
+
+ var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
+ var m1 = l * 2 - m2;
+
+ return colorFunctions.rgba(hue(h + 1/3) * 255,
+ hue(h) * 255,
+ hue(h - 1/3) * 255,
+ a);
+ },
+
+ hsv: function(h, s, v) {
+ return colorFunctions.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 colorFunctions.rgba(vs[perm[i][0]] * 255,
+ vs[perm[i][1]] * 255,
+ vs[perm[i][2]] * 255,
+ a);
+ },
+
+ hue: function (color) {
+ return new(tree.Dimension)(color.toHSL().h);
+ },
+ saturation: function (color) {
+ return new(tree.Dimension)(color.toHSL().s * 100, '%');
+ },
+ lightness: function (color) {
+ return new(tree.Dimension)(color.toHSL().l * 100, '%');
+ },
+ hsvhue: function(color) {
+ return new(tree.Dimension)(color.toHSV().h);
+ },
+ hsvsaturation: function (color) {
+ return new(tree.Dimension)(color.toHSV().s * 100, '%');
+ },
+ hsvvalue: function (color) {
+ return new(tree.Dimension)(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)(color.luma() * color.alpha * 100, '%');
+ },
+ luminance: function (color) {
+ var luminance =
+ (0.2126 * color.rgb[0] / 255)
+ + (0.7152 * color.rgb[1] / 255)
+ + (0.0722 * color.rgb[2] / 255);
+
+ return new(tree.Dimension)(luminance * color.alpha * 100, '%');
+ },
+ saturate: function (color, amount) {
+ // filter: saturate(3.2);
+ // should be kept as is, so check for color
+ if (!color.rgb) {
+ return null;
+ }
+ 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 colorFunctions.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 = colorFunctions.rgba(255, 255, 255, 1.0);
+ }
+ if (typeof dark === 'undefined') {
+ dark = colorFunctions.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() < threshold) {
+ return light;
+ } else {
+ return dark;
+ }
+ },
+ argb: function (color) {
+ return new(tree.Anonymous)(color.toARGB());
+ },
+ color: function(c) {
+ if ((c instanceof tree.Quoted) &&
+ (/^#([a-f0-9]{6}|[a-f0-9]{3})$/i.test(c.value))) {
+ return new(tree.Color)(c.value.slice(1));
+ }
+ if ((c instanceof tree.Color) || (c = tree.Color.fromKeyword(c.value))) {
+ c.keyword = undefined;
+ return c;
+ }
+ throw {
+ type: "Argument",
+ message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF"
+ };
+ },
+ tint: function(color, amount) {
+ return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount);
+ },
+ shade: function(color, amount) {
+ return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
+ }
+ };
+ functions.functionRegistry.addMultiple(colorFunctions);
+};
diff --git a/lib/less/functions/data-uri.js b/lib/less/functions/data-uri.js
new file mode 100644
index 0000000..c60be3a
--- /dev/null
+++ b/lib/less/functions/data-uri.js
@@ -0,0 +1,69 @@
+module.exports = function(functions, tree, less) {
+ functions.functionRegistry.add("data-uri", function(mimetypeNode, filePathNode) {
+
+ if (!less.environment.supportsDataURI(this.env)) {
+ return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
+ }
+
+ var mimetype = mimetypeNode.value;
+ var filePath = (filePathNode && filePathNode.value);
+
+ var useBase64 = false;
+
+ if (arguments.length < 2) {
+ filePath = mimetype;
+ }
+
+ var fragmentStart = filePath.indexOf('#');
+ var fragment = '';
+ if (fragmentStart!==-1) {
+ fragment = filePath.slice(fragmentStart);
+ filePath = filePath.slice(0, fragmentStart);
+ }
+
+ if (this.env.isPathRelative(filePath)) {
+ if (this.currentFileInfo.relativeUrls) {
+ filePath = less.environment.join(this.currentFileInfo.currentDirectory, filePath);
+ } else {
+ filePath = less.environment.join(this.currentFileInfo.entryPath, filePath);
+ }
+ }
+
+ // detect the mimetype if not given
+ if (arguments.length < 2) {
+
+ mimetype = less.environment.mimeLookup(this.env, filePath);
+
+ // use base 64 unless it's an ASCII or UTF-8 format
+ var charset = less.environment.charsetLookup(this.env, mimetype);
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
+ if (useBase64) { mimetype += ';base64'; }
+ }
+ else {
+ useBase64 = /;base64$/.test(mimetype);
+ }
+
+ var buf = less.environment.readFileSync(filePath);
+
+ // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
+ // and the --ieCompat flag is enabled, return a normal url() instead.
+ var DATA_URI_MAX_KB = 32,
+ fileSizeInKB = parseInt((buf.length / 1024), 10);
+ if (fileSizeInKB >= DATA_URI_MAX_KB) {
+
+ if (this.env.ieCompat !== false) {
+ if (!this.env.silent) {
+ console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
+ }
+
+ return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
+ }
+ }
+
+ buf = useBase64 ? buf.toString('base64')
+ : encodeURIComponent(buf);
+
+ var uri = "\"data:" + mimetype + ',' + buf + fragment + "\"";
+ return new(tree.URL)(new(tree.Anonymous)(uri));
+ });
+};
diff --git a/lib/less/functions/default.js b/lib/less/functions/default.js
new file mode 100644
index 0000000..6aa78aa
--- /dev/null
+++ b/lib/less/functions/default.js
@@ -0,0 +1,25 @@
+module.exports = function(functions, tree) {
+ var defaultFunc = {
+ eval: function () {
+ var v = this.value_, e = this.error_;
+ if (e) {
+ throw e;
+ }
+ if (v != null) {
+ return v ? tree.True : tree.False;
+ }
+ },
+ value: function (v) {
+ this.value_ = v;
+ },
+ error: function (e) {
+ this.error_ = e;
+ },
+ reset: function () {
+ this.value_ = this.error_ = null;
+ }
+ };
+
+ functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
+ tree.defaultFunc = defaultFunc;
+};
diff --git a/lib/less/functions/function-caller.js b/lib/less/functions/function-caller.js
new file mode 100644
index 0000000..da86d51
--- /dev/null
+++ b/lib/less/functions/function-caller.js
@@ -0,0 +1,15 @@
+module.exports = function(functions) {
+var functionCaller = function(name, env, currentFileInfo) {
+ this.name = name.toLowerCase();
+ this.function = functions.functionRegistry.get(this.name);
+ this.env = env;
+ this.currentFileInfo = currentFileInfo;
+};
+functionCaller.prototype.isValid = function() {
+ return Boolean(this.function);
+};
+functionCaller.prototype.call = function(args) {
+ return this.function.apply(this, args);
+};
+return functionCaller;
+};
diff --git a/lib/less/functions/function-registry.js b/lib/less/functions/function-registry.js
new file mode 100644
index 0000000..d39230d
--- /dev/null
+++ b/lib/less/functions/function-registry.js
@@ -0,0 +1,18 @@
+module.exports = {
+ _data: {},
+ add: function(name, func) {
+ if (this._data.hasOwnProperty(name)) {
+ //TODO warn
+ }
+ this._data[name] = func;
+ },
+ addMultiple: function(functions) {
+ Object.keys(functions).forEach(
+ function(name) {
+ this.add(name, functions[name]);
+ }.bind(this));
+ },
+ get: function(name) {
+ return this._data[name];
+ }
+};
diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js
new file mode 100644
index 0000000..36d7167
--- /dev/null
+++ b/lib/less/functions/index.js
@@ -0,0 +1,18 @@
+module.exports = function(less, tree) {
+ var functions = {};
+ functions.functionRegistry = require("./function-registry.js");
+ functions.functionCaller = require("./function-caller.js")(functions);
+
+ //register functions
+ require("./color.js")(functions, tree);
+ require("./color-blending.js")(functions, tree);
+ require("./data-uri.js")(functions, tree, less);
+ require("./default.js")(functions, tree);
+ require("./math.js")(functions, tree);
+ require("./number.js")(functions, tree);
+ require("./string.js")(functions, tree);
+ require("./svg.js")(functions, tree, less);
+ require("./types.js")(functions, tree);
+
+ return functions;
+};
diff --git a/lib/less/functions/math.js b/lib/less/functions/math.js
new file mode 100644
index 0000000..4e56d8d
--- /dev/null
+++ b/lib/less/functions/math.js
@@ -0,0 +1,42 @@
+module.exports = function(functions, tree) {
+
+ var mathFunctions = {
+ // name, unit
+ ceil: null,
+ floor: null,
+ sqrt: null,
+ abs: null,
+ tan: "",
+ sin: "",
+ cos: "",
+ atan: "rad",
+ asin: "rad",
+ acos: "rad"
+ };
+
+ function _math(fn, unit, n) {
+ if (!(n instanceof tree.Dimension)) {
+ throw { type: "Argument", message: "argument must be a number" };
+ }
+ if (unit == null) {
+ unit = n.unit;
+ } else {
+ n = n.unify();
+ }
+ return new(tree.Dimension)(fn(parseFloat(n.value)), unit);
+ }
+
+ for (var f in mathFunctions) {
+ if (mathFunctions.hasOwnProperty(f)) {
+ mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]);
+ }
+ }
+
+ mathFunctions.round = function (n, f) {
+ var fraction = typeof(f) === "undefined" ? 0 : f.value;
+ return _math(function(num) { return num.toFixed(fraction); }, null, n);
+ };
+
+ functions.functionRegistry.addMultiple(mathFunctions);
+
+};
diff --git a/lib/less/functions/number.js b/lib/less/functions/number.js
new file mode 100644
index 0000000..dc6fdfd
--- /dev/null
+++ b/lib/less/functions/number.js
@@ -0,0 +1,74 @@
+module.exports = function(functions, tree) {
+ var minMax = function (isMin, args) {
+ args = Array.prototype.slice.call(args);
+ switch(args.length) {
+ case 0: throw { type: "Argument", message: "one or more arguments required" };
+ }
+ var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
+ order = [], // elems only contains original argument values.
+ values = {}; // key is the unit.toString() for unified tree.Dimension values,
+ // value is the index into the order array.
+ for (i = 0; i < args.length; i++) {
+ current = args[i];
+ if (!(current instanceof tree.Dimension)) {
+ if(Array.isArray(args[i].value)) {
+ Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
+ }
+ continue;
+ }
+ currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify();
+ unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
+ unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic;
+ unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
+ j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
+ if (j === undefined) {
+ if(unitStatic !== undefined && unit !== unitStatic) {
+ throw{ type: "Argument", message: "incompatible types" };
+ }
+ values[unit] = order.length;
+ order.push(current);
+ continue;
+ }
+ referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify();
+ if ( isMin && currentUnified.value < referenceUnified.value ||
+ !isMin && currentUnified.value > referenceUnified.value) {
+ order[j] = current;
+ }
+ }
+ if (order.length == 1) {
+ return order[0];
+ }
+ args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", ");
+ return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")");
+ };
+ functions.functionRegistry.addMultiple({
+ min: function () {
+ return minMax(true, arguments);
+ },
+ max: function () {
+ return minMax(false, arguments);
+ },
+ convert: function (val, unit) {
+ return val.convertTo(unit.value);
+ },
+ 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);
+ },
+ percentage: function (n) {
+ return new(tree.Dimension)(n.value * 100, '%');
+ }
+ });
+};
diff --git a/lib/less/functions/string.js b/lib/less/functions/string.js
new file mode 100644
index 0000000..9851c89
--- /dev/null
+++ b/lib/less/functions/string.js
@@ -0,0 +1,30 @@
+module.exports = function(functions, tree) {
+ functions.functionRegistry.addMultiple({
+ e: function (str) {
+ return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str.value);
+ },
+ 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"));
+ },
+ replace: function (string, pattern, replacement, flags) {
+ var result = string.value;
+
+ result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value);
+ return new(tree.Quoted)(string.quote || '', result, string.escaped);
+ },
+ '%': function (string /* arg, arg, ...*/) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ result = string.value;
+
+ for (var i = 0; i < args.length; i++) {
+ /*jshint loopfunc:true */
+ result = result.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;
+ });
+ }
+ result = result.replace(/%%/g, '%');
+ return new(tree.Quoted)(string.quote || '', result, string.escaped);
+ }
+ });
+};
diff --git a/lib/less/functions/svg.js b/lib/less/functions/svg.js
new file mode 100644
index 0000000..0cd71d5
--- /dev/null
+++ b/lib/less/functions/svg.js
@@ -0,0 +1,77 @@
+module.exports = function(functions, tree, less) {
+ functions.functionRegistry.add("svg-gradient", function(direction) {
+
+ function throwArgumentDescriptor() {
+ throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
+ }
+
+ if (arguments.length < 3) {
+ throwArgumentDescriptor();
+ }
+ var stops = Array.prototype.slice.call(arguments, 1),
+ gradientDirectionSvg,
+ gradientType = "linear",
+ rectangleDimension = 'x="0" y="0" width="1" height="1"',
+ useBase64 = true,
+ renderEnv = {compress: false},
+ returner,
+ directionValue = direction.toCSS(renderEnv),
+ i, color, position, positionValue, alpha;
+
+ switch (directionValue) {
+ case "to bottom":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
+ break;
+ case "to right":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
+ break;
+ case "to bottom right":
+ gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
+ break;
+ case "to top right":
+ gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
+ break;
+ case "ellipse":
+ case "ellipse at center":
+ gradientType = "radial";
+ gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
+ rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
+ break;
+ default:
+ throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
+ }
+ returner = '<?xml version="1.0" ?>' +
+ '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
+ '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
+
+ for (i = 0; i < stops.length; i+= 1) {
+ if (stops[i].value) {
+ color = stops[i].value[0];
+ position = stops[i].value[1];
+ } else {
+ color = stops[i];
+ position = undefined;
+ }
+
+ if (!(color instanceof tree.Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof tree.Dimension))) {
+ throwArgumentDescriptor();
+ }
+ positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
+ alpha = color.alpha;
+ returner += '<stop offset="' + positionValue + '" stop-color="' + color.toRGB() + '"' + (alpha < 1 ? ' stop-opacity="' + alpha + '"' : '') + '/>';
+ }
+ returner += '</' + gradientType + 'Gradient>' +
+ '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
+
+ if (useBase64) {
+ try {
+ returner = less.environment.encodeBase64(this.env, returner);
+ } catch(e) {
+ useBase64 = false;
+ }
+ }
+
+ returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
+ return new(tree.URL)(new(tree.Anonymous)(returner));
+ });
+};
diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js
new file mode 100644
index 0000000..802d809
--- /dev/null
+++ b/lib/less/functions/types.js
@@ -0,0 +1,64 @@
+module.exports = function(functions, tree) {
+ var isa = function (n, Type) {
+ return (n instanceof Type) ? tree.True : tree.False;
+ },
+ isunit = function (n, unit) {
+ return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;
+ };
+ functions.functionRegistry.addMultiple({
+ iscolor: function (n) {
+ return isa(n, tree.Color);
+ },
+ isnumber: function (n) {
+ return isa(n, tree.Dimension);
+ },
+ isstring: function (n) {
+ return isa(n, tree.Quoted);
+ },
+ iskeyword: function (n) {
+ return isa(n, tree.Keyword);
+ },
+ isurl: function (n) {
+ return isa(n, tree.URL);
+ },
+ ispixel: function (n) {
+ return isunit(n, 'px');
+ },
+ ispercentage: function (n) {
+ return isunit(n, '%');
+ },
+ isem: function (n) {
+ return isunit(n, 'em');
+ },
+ isunit: isunit,
+ unit: function (val, unit) {
+ if(!(val instanceof tree.Dimension)) {
+ throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") };
+ }
+ if (unit) {
+ if (unit instanceof tree.Keyword) {
+ unit = unit.value;
+ } else {
+ unit = unit.toCSS();
+ }
+ } else {
+ unit = "";
+ }
+ return new(tree.Dimension)(val.value, unit);
+ },
+ "get-unit": function (n) {
+ return new(tree.Anonymous)(n.unit);
+ },
+ extract: function(values, index) {
+ index = index.value - 1; // (1-based index)
+ // handle non-array values as an array of length 1
+ // return 'undefined' if index is invalid
+ return Array.isArray(values.value) ?
+ values.value[index] : Array(values)[index];
+ },
+ length: function(values) {
+ var n = Array.isArray(values.value) ? values.value.length : 1;
+ return new tree.Dimension(n);
+ }
+ });
+};
diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js
index cacb207..c93846d 100644
--- a/lib/less/non-node-index.js
+++ b/lib/less/non-node-index.js
@@ -5,10 +5,10 @@ var less = {
}
};
-less.tree = (require('./tree'))(less);
+less.tree = (require('./tree'))(less, less.data);
less.visitor = require('./visitor/index.js')(less, less.tree);
less.Parser = (require('./parser'))(less, less.tree, less.visitor);
-less.tree.functions = (require('./functions'))(less, less.tree);
+less.functions = (require('./functions/index.js'))(less, less.tree);
require('./env')(less.tree);
less.tree.sourceMapOutput = require('./source-map-output.js')(less);
diff --git a/lib/less/tree.js b/lib/less/tree.js
index d771e3c..b3d7b1c 100644
--- a/lib/less/tree.js
+++ b/lib/less/tree.js
@@ -1,4 +1,4 @@
-module.exports = function (less) {
+module.exports = function (less, data) {
var tree = {};
@@ -97,7 +97,7 @@ tree.outputRuleset = function (env, output, rules) {
};
tree.Alpha = require('./tree/alpha')(tree);
-tree.Color = require('./tree/color')(less.data, tree);
+tree.Color = require('./tree/color')(data, tree);
tree.Directive = require('./tree/directive')(tree);
tree.DetachedRuleset = require('./tree/detached-ruleset')(tree);
tree.Operation = require('./tree/operation')(tree);
@@ -113,7 +113,7 @@ tree.Selector = require('./tree/selector')(tree);
tree.Quoted = require('./tree/quoted')(tree);
tree.Expression = require('./tree/expression')(tree);
tree.Rule = require('./tree/rule')(tree);
-tree.Call = require('./tree/call')(tree);
+tree.Call = require('./tree/call')(tree, less);
tree.URL = require('./tree/url')(tree);
tree.Import = require('./tree/import')(tree);
tree.mixin = {
@@ -133,6 +133,12 @@ tree.Negative = require('./tree/negative')(tree);
tree.Extend = require('./tree/extend')(tree);
tree.RulesetCall = require('./tree/ruleset-call')(tree);
+tree.fround = function(env, value) {
+ var precision = env && env.numPrecision;
+ //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
+ return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
+};
+
return tree;
};
diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js
index 951a083..08dc989 100644
--- a/lib/less/tree/call.js
+++ b/lib/less/tree/call.js
@@ -1,4 +1,4 @@
-module.exports = function (tree) {
+module.exports = function (tree, less) {
//
// A function call node.
@@ -18,7 +18,7 @@ Call.prototype = {
},
//
// When evaluating a function call,
- // we either find the function in `tree.functions` [1],
+ // we either find the function in `less.functions` [1],
// in which case we call it, passing the evaluated arguments,
// if this returns null or we cannot find the function, we
// simply print it out as it appeared originally [2].
@@ -31,13 +31,11 @@ Call.prototype = {
//
eval: function (env) {
var args = this.args.map(function (a) { return a.eval(env); }),
- nameLC = this.name.toLowerCase(),
- result, func;
+ result, funcCaller = new less.functions.functionCaller(this.name, env, this.currentFileInfo);
- if (nameLC in tree.functions) { // 1.
+ if (funcCaller.isValid()) { // 1.
try {
- func = new tree.functionCall(env, this.currentFileInfo);
- result = func[nameLC].apply(func, args);
+ result = funcCaller.call(args);
if (result != null) {
return result;
}
diff --git a/test/less-test.js b/test/less-test.js
index 4bba05b..ad046d1 100644
--- a/test/less-test.js
+++ b/test/less-test.js
@@ -18,15 +18,17 @@ module.exports = function() {
passedTests = 0;
- less.tree.functions.add = function (a, b) {
- return new(less.tree.Dimension)(a.value + b.value);
- };
- less.tree.functions.increment = function (a) {
- return new(less.tree.Dimension)(a.value + 1);
- };
- less.tree.functions._color = function (str) {
- if (str.value === "evil red") { return new(less.tree.Color)("600"); }
- };
+ less.functions.functionRegistry.addMultiple({
+ add: function (a, b) {
+ return new(less.tree.Dimension)(a.value + b.value);
+ },
+ increment: function (a) {
+ return new(less.tree.Dimension)(a.value + 1);
+ },
+ _color: function (str) {
+ if (str.value === "evil red") { return new(less.tree.Color)("600"); }
+ }
+ });
function testSourcemap(name, err, compiledLess, doReplacements, sourcemap) {
fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/less.js.git
More information about the Pkg-javascript-commits
mailing list