[Pkg-javascript-commits] [less.js] 108/285: Merge branch 'reworked-guard-comparison' of github.com:seven-phases-max/less.js into 2_0_0

Jonas Smedegaard dr at jones.dk
Mon Oct 26 23:23:44 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 cf0ac697678d49e2d898bb9d5f9f1a92f804cee9
Merge: e31442c c4e2514
Author: Luke Page <luke.a.page at gmail.com>
Date:   Sat Sep 6 01:52:47 2014 +0100

    Merge branch 'reworked-guard-comparison' of github.com:seven-phases-max/less.js into 2_0_0
    
    Conflicts:
    	lib/less/tree.js
    	lib/less/tree/anonymous.js
    	lib/less/tree/color.js
    	lib/less/tree/condition.js
    	lib/less/tree/dimension.js
    	lib/less/tree/keyword.js
    	lib/less/tree/quoted.js

 lib/less/tree/anonymous.js   | 15 ++-------
 lib/less/tree/color.js       |  9 ++----
 lib/less/tree/condition.js   | 28 +++++------------
 lib/less/tree/dimension.js   | 37 +++++++++-------------
 lib/less/tree/keyword.js     |  7 -----
 lib/less/tree/node.js        | 39 +++++++++++++++++++++++
 lib/less/tree/quoted.js      | 25 ++++-----------
 lib/less/tree/unit.js        |  2 +-
 test/css/mixins-guards.css   | 74 ++++++++++++++++++++++++++++++++++++++++++++
 test/less/mixins-guards.less | 43 +++++++++++++++++++++++++
 10 files changed, 189 insertions(+), 90 deletions(-)

diff --cc lib/less/tree/anonymous.js
index 4c1695e,a960de6..d68d5df
--- a/lib/less/tree/anonymous.js
+++ b/lib/less/tree/anonymous.js
@@@ -7,29 -7,21 +7,18 @@@ var Anonymous = function (value, index
      this.currentFileInfo = currentFileInfo;
      this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike;
  };
 -tree.Anonymous.prototype = {
 -    type: "Anonymous",
 -    eval: function () { 
 -        return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
 -    },
 -    compare: function (other) {
 -        return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
 -    },
 -    isRulesetLike: function() {
 -        return this.rulesetLike;
 -    },
 -    genCSS: function (env, output) {
 -        output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
 -    },
 -    toCSS: tree.toCSS
 +Anonymous.prototype = new Node();
 +Anonymous.prototype.type = "Anonymous";
 +Anonymous.prototype.eval = function () {
 +    return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
  };
- Anonymous.prototype.compare = function (x) {
-     if (!x.toCSS) {
-         return -1;
-     }
- 
-     var left = this.toCSS(),
-         right = x.toCSS();
- 
-     if (left === right) {
-         return 0;
-     }
--
-     return left < right ? -1 : 1;
 -})(require('../tree'));
++Anonymous.prototype.compare = function (other) {
++    return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
 +};
 +Anonymous.prototype.isRulesetLike = function() {
 +    return this.rulesetLike;
 +};
 +Anonymous.prototype.genCSS = function (env, output) {
 +    output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
 +};
 +module.exports = Anonymous;
diff --cc lib/less/tree/color.js
index 5a51d1c,15303be..1de5809
--- a/lib/less/tree/color.js
+++ b/lib/less/tree/color.js
@@@ -25,165 -23,164 +25,162 @@@ var Color = function (rgb, a) 
      this.alpha = typeof(a) === 'number' ? a : 1;
  };
  
 -var transparentKeyword = "transparent";
 -
 -tree.Color.prototype = {
 -    type: "Color",
 -    eval: function () { return this; },
 -    luma: function () {
 -        var r = this.rgb[0] / 255,
 -            g = this.rgb[1] / 255,
 -            b = this.rgb[2] / 255;
 -
 -        r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
 -        g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
 -        b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
 -
 -        return 0.2126 * r + 0.7152 * g + 0.0722 * b;
 -    },
 -
 -    genCSS: function (env, output) {
 -        output.add(this.toCSS(env));
 -    },
 -    toCSS: function (env, doNotCompress) {
 -        var compress = env && env.compress && !doNotCompress,
 -            alpha = tree.fround(env, this.alpha);
 -
 -        // If we have some transparency, the only way to represent it
 -        // is via `rgba`. Otherwise, we use the hex representation,
 -        // which has better compatibility with older browsers.
 -        // Values are capped between `0` and `255`, rounded and zero-padded.
 -        if (alpha < 1) {
 -            if (alpha === 0 && this.isTransparentKeyword) {
 -                return transparentKeyword;
 -            }
 -            return "rgba(" + this.rgb.map(function (c) {
 -                return clamp(Math.round(c), 255);
 -            }).concat(clamp(alpha, 1))
 -                .join(',' + (compress ? '' : ' ')) + ")";
 -        } else {
 -            var color = this.toRGB();
 -
 -            if (compress) {
 -                var splitcolor = color.split('');
 -
 -                // Convert color to short format
 -                if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
 -                    color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
 -                }
 -            }
 -
 -            return color;
 -        }
 -    },
 +Color.prototype = new Node();
 +Color.prototype.type = "Color";
  
 -    //
 -    // Operations have to be done per-channel, if not,
 -    // channels will spill onto each other. Once we have
 -    // our result, in the form of an integer triplet,
 -    // we create a new Color node to hold the result.
 -    //
 -    operate: function (env, op, other) {
 -        var rgb = [];
 -        var alpha = this.alpha * (1 - other.alpha) + other.alpha;
 -        for (var c = 0; c < 3; c++) {
 -            rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]);
 -        }
 -        return new(tree.Color)(rgb, alpha);
 -    },
 -
 -    toRGB: function () {
 -        return toHex(this.rgb);
 -    },
 -
 -    toHSL: function () {
 -        var r = this.rgb[0] / 255,
 -            g = this.rgb[1] / 255,
 -            b = this.rgb[2] / 255,
 -            a = this.alpha;
 -
 -        var max = Math.max(r, g, b), min = Math.min(r, g, b);
 -        var h, s, l = (max + min) / 2, d = max - min;
 -
 -        if (max === min) {
 -            h = s = 0;
 -        } else {
 -            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
 -
 -            switch (max) {
 -                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
 -                case g: h = (b - r) / d + 2;               break;
 -                case b: h = (r - g) / d + 4;               break;
 -            }
 -            h /= 6;
 -        }
 -        return { h: h * 360, s: s, l: l, a: a };
 -    },
 -    //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
 -    toHSV: function () {
 -        var r = this.rgb[0] / 255,
 -            g = this.rgb[1] / 255,
 -            b = this.rgb[2] / 255,
 -            a = this.alpha;
 -
 -        var max = Math.max(r, g, b), min = Math.min(r, g, b);
 -        var h, s, v = max;
 -
 -        var d = max - min;
 -        if (max === 0) {
 -            s = 0;
 -        } else {
 -            s = d / max;
 -        }
 +function clamp(v, max) {
 +    return Math.min(Math.max(v, 0), max);
 +}
  
 -        if (max === min) {
 -            h = 0;
 -        } else {
 -            switch(max){
 -                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
 -                case g: h = (b - r) / d + 2; break;
 -                case b: h = (r - g) / d + 4; break;
 -            }
 -            h /= 6;
 +function toHex(v) {
 +    return '#' + v.map(function (c) {
 +        c = clamp(Math.round(c), 255);
 +        return (c < 16 ? '0' : '') + c.toString(16);
 +    }).join('');
 +}
 +
 +Color.prototype.luma = function () {
 +    var r = this.rgb[0] / 255,
 +        g = this.rgb[1] / 255,
 +        b = this.rgb[2] / 255;
 +
 +    r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
 +    g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
 +    b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
 +
 +    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
 +};
 +Color.prototype.genCSS = function (env, output) {
 +    output.add(this.toCSS(env));
 +};
 +Color.prototype.toCSS = function (env, doNotCompress) {
 +    var compress = env && env.compress && !doNotCompress, color, alpha;
 +
 +    // `keyword` is set if this color was originally
 +    // converted from a named color string so we need
 +    // to respect this and try to output named color too.
 +    if (this.keyword) {
 +        return this.keyword;
 +    }
 +
 +    // If we have some transparency, the only way to represent it
 +    // is via `rgba`. Otherwise, we use the hex representation,
 +    // which has better compatibility with older browsers.
 +    // Values are capped between `0` and `255`, rounded and zero-padded.
 +    alpha = this.fround(env, this.alpha);
 +    if (alpha < 1) {
 +        return "rgba(" + this.rgb.map(function (c) {
 +            return clamp(Math.round(c), 255);
 +        }).concat(clamp(alpha, 1))
 +            .join(',' + (compress ? '' : ' ')) + ")";
 +    }
 +
 +    color = this.toRGB();
 +
 +    if (compress) {
 +        var splitcolor = color.split('');
 +
 +        // Convert color to short format
 +        if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
 +            color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
          }
 -        return { h: h * 360, s: s, v: v, a: a };
 -    },
 -    toARGB: function () {
 -        return toHex([this.alpha * 255].concat(this.rgb));
 -    },
 -    compare: function (x) {
 -        return (x.rgb && 
 -            x.rgb[0] === this.rgb[0] &&
 -            x.rgb[1] === this.rgb[1] &&
 -            x.rgb[2] === this.rgb[2] &&
 -            x.alpha  === this.alpha) ? 0 : undefined;
      }
 -};
  
 -tree.Color.fromKeyword = function(keyword) {
 -    keyword = keyword.toLowerCase();
 +    return color;
 +};
  
 -    if (tree.colors.hasOwnProperty(keyword)) {
 -        // detect named color
 -        return new(tree.Color)(tree.colors[keyword].slice(1));
 +//
 +// Operations have to be done per-channel, if not,
 +// channels will spill onto each other. Once we have
 +// our result, in the form of an integer triplet,
 +// we create a new Color node to hold the result.
 +//
 +Color.prototype.operate = function (env, op, other) {
 +    var rgb = [];
 +    var alpha = this.alpha * (1 - other.alpha) + other.alpha;
 +    for (var c = 0; c < 3; c++) {
 +        rgb[c] = this._operate(env, op, this.rgb[c], other.rgb[c]);
      }
 -    if (keyword === transparentKeyword) {
 -        var transparent = new(tree.Color)([0, 0, 0], 0);
 -        transparent.isTransparentKeyword = true;
 -        return transparent;
 +    return new(Color)(rgb, alpha);
 +};
 +Color.prototype.toRGB = function () {
 +    return toHex(this.rgb);
 +};
 +Color.prototype.toHSL = function () {
 +    var r = this.rgb[0] / 255,
 +        g = this.rgb[1] / 255,
 +        b = this.rgb[2] / 255,
 +        a = this.alpha;
 +
 +    var max = Math.max(r, g, b), min = Math.min(r, g, b);
 +    var h, s, l = (max + min) / 2, d = max - min;
 +
 +    if (max === min) {
 +        h = s = 0;
 +    } else {
 +        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
 +
 +        switch (max) {
 +            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
 +            case g: h = (b - r) / d + 2;               break;
 +            case b: h = (r - g) / d + 4;               break;
 +        }
 +        h /= 6;
      }
 +    return { h: h * 360, s: s, l: l, a: a };
  };
 +//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
 +Color.prototype.toHSV = function () {
 +    var r = this.rgb[0] / 255,
 +        g = this.rgb[1] / 255,
 +        b = this.rgb[2] / 255,
 +        a = this.alpha;
 +
 +    var max = Math.max(r, g, b), min = Math.min(r, g, b);
 +    var h, s, v = max;
 +
 +    var d = max - min;
 +    if (max === 0) {
 +        s = 0;
 +    } else {
 +        s = d / max;
 +    }
  
 -function toHex(v) {
 -    return '#' + v.map(function (c) {
 -        c = clamp(Math.round(c), 255);
 -        return (c < 16 ? '0' : '') + c.toString(16);
 -    }).join('');
 -}
 +    if (max === min) {
 +        h = 0;
 +    } else {
 +        switch(max){
 +            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
 +            case g: h = (b - r) / d + 2; break;
 +            case b: h = (r - g) / d + 4; break;
 +        }
 +        h /= 6;
 +    }
 +    return { h: h * 360, s: s, v: v, a: a };
 +};
 +Color.prototype.toARGB = function () {
 +    return toHex([this.alpha * 255].concat(this.rgb));
 +};
 +Color.prototype.compare = function (x) {
-     if (!x.rgb) {
-         return -1;
-     }
- 
-     return (x.rgb[0] === this.rgb[0] &&
++    return (x.rgb && 
++        x.rgb[0] === this.rgb[0] &&
 +        x.rgb[1] === this.rgb[1] &&
 +        x.rgb[2] === this.rgb[2] &&
-         x.alpha === this.alpha) ? 0 : -1;
++        x.alpha  === this.alpha) ? 0 : undefined;
 +};
  
 -function clamp(v, max) {
 -    return Math.min(Math.max(v, 0), max); 
 -}
 +Color.fromKeyword = function(keyword) {
 +    var c, key = keyword.toLowerCase();
 +    if (colors.hasOwnProperty(key)) {
 +        c = new(Color)(colors[key].slice(1));
 +    }
 +    else if (key === "transparent") {
 +        c = new(Color)([0, 0, 0], 0);
 +    }
  
 -})(require('../tree'));
 +    if (c) {
 +        c.keyword = keyword;
 +        return c;
 +    }
 +};
 +module.exports = Color;
diff --cc lib/less/tree/condition.js
index 464c96c,c9ef56c..27b3be5
--- a/lib/less/tree/condition.js
+++ b/lib/less/tree/condition.js
@@@ -7,41 -7,29 +7,27 @@@ var Condition = function (op, l, r, i, 
      this.index = i;
      this.negate = negate;
  };
 -tree.Condition.prototype = {
 -    type: "Condition",
 -    accept: function (visitor) {
 -        this.lvalue = visitor.visit(this.lvalue);
 -        this.rvalue = visitor.visit(this.rvalue);
 -    },
 -    eval: function (env) {
 -        var result = (function (op, a, b) {
 -            switch (op) {
 -                case 'and': return a && b;
 -                case 'or':  return a || b;
 -                default:
 -                    switch (tree.compare(a, b)) {
 -                        case -1: return op === '<' || op === '=<' || op === '<=';
 -                        case  0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
 -                        case  1: return op === '>' || op === '>=';
 -                        default: return false;
 -                    }
 -            }
 -        }) (this.op, this.lvalue.eval(env), this.rvalue.eval(env));
 -        
 -        return this.negate ? !result : result;
 -    }
 +Condition.prototype = new Node();
 +Condition.prototype.type = "Condition";
 +Condition.prototype.accept = function (visitor) {
 +    this.lvalue = visitor.visit(this.lvalue);
 +    this.rvalue = visitor.visit(this.rvalue);
  };
 +Condition.prototype.eval = function (env) {
-     var a = this.lvalue.eval(env),
-         b = this.rvalue.eval(env);
- 
-     var i = this.index, result;
- 
-     result = (function (op) {
++    var result = (function (op, a, b) {
 +        switch (op) {
-             case 'and':
-                 return a && b;
-             case 'or':
-                 return a || b;
++            case 'and': return a && b;
++            case 'or':  return a || b;
 +            default:
-                 if (a.compare) {
-                     result = a.compare(b);
-                 } else if (b.compare) {
-                     result = b.compare(a);
-                 } else {
-                     throw { type: "Type",
-                             message: "Unable to perform comparison",
-                             index: i };
-                 }
-                 switch (result) {
++                switch (Node.compare(a, b)) {
 +                    case -1: return op === '<' || op === '=<' || op === '<=';
 +                    case  0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
 +                    case  1: return op === '>' || op === '>=';
++                        default: return false;
 +                }
 +        }
-     })(this.op);
++    }) (this.op, this.lvalue.eval(env), this.rvalue.eval(env));
+ 
 -})(require('../tree'));
 +    return this.negate ? !result : result;
 +};
 +module.exports = Condition;
diff --cc lib/less/tree/dimension.js
index 84b92c0,aabf337..dfd00cf
--- a/lib/less/tree/dimension.js
+++ b/lib/less/tree/dimension.js
@@@ -6,159 -3,313 +6,150 @@@ var Node = require("./node.js")
  //
  // A number with a unit
  //
 -tree.Dimension = function (value, unit) {
 +var Dimension = function (value, unit) {
      this.value = parseFloat(value);
 -    this.unit = (unit && unit instanceof tree.Unit) ? unit :
 -      new(tree.Unit)(unit ? [unit] : undefined);
 +    this.unit = (unit && unit instanceof Unit) ? unit :
 +      new(Unit)(unit ? [unit] : undefined);
  };
  
 -tree.Dimension.prototype = {
 -    type: "Dimension",
 -    accept: function (visitor) {
 -        this.unit = visitor.visit(this.unit);
 -    },
 -    eval: function (env) {
 -        return this;
 -    },
 -    toColor: function () {
 -        return new(tree.Color)([this.value, this.value, this.value]);
 -    },
 -    genCSS: function (env, output) {
 -        if ((env && env.strictUnits) && !this.unit.isSingular()) {
 -            throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
 -        }
 -
 -        var value = tree.fround(env, this.value),
 -            strValue = String(value);
 +Dimension.prototype = new Node();
 +Dimension.prototype.type = "Dimension";
 +Dimension.prototype.accept = function (visitor) {
 +    this.unit = visitor.visit(this.unit);
 +};
 +Dimension.prototype.eval = function (env) {
 +    return this;
 +};
 +Dimension.prototype.toColor = function () {
 +    return new(Color)([this.value, this.value, this.value]);
 +};
 +Dimension.prototype.genCSS = function (env, output) {
 +    if ((env && env.strictUnits) && !this.unit.isSingular()) {
 +        throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
 +    }
  
 -        if (value !== 0 && value < 0.000001 && value > -0.000001) {
 -            // would be output 1e-6 etc.
 -            strValue = value.toFixed(20).replace(/0+$/, "");
 -        }
 +    var value = this.fround(env, this.value),
 +        strValue = String(value);
  
 -        if (env && env.compress) {
 -            // Zero values doesn't need a unit
 -            if (value === 0 && this.unit.isLength()) {
 -                output.add(strValue);
 -                return;
 -            }
 +    if (value !== 0 && value < 0.000001 && value > -0.000001) {
 +        // would be output 1e-6 etc.
 +        strValue = value.toFixed(20).replace(/0+$/, "");
 +    }
  
 -            // Float values doesn't need a leading zero
 -            if (value > 0 && value < 1) {
 -                strValue = (strValue).substr(1);
 -            }
 +    if (env && env.compress) {
 +        // Zero values doesn't need a unit
 +        if (value === 0 && this.unit.isLength()) {
 +            output.add(strValue);
 +            return;
          }
  
 -        output.add(strValue);
 -        this.unit.genCSS(env, output);
 -    },
 -    toCSS: tree.toCSS,
 -
 -    // In an operation between two Dimensions,
 -    // we default to the first Dimension's unit,
 -    // so `1px + 2` will yield `3px`.
 -    operate: function (env, op, other) {
 -        /*jshint noempty:false */
 -        var value = tree.operate(env, op, this.value, other.value),
 -            unit = this.unit.clone();
 -
 -        if (op === '+' || op === '-') {
 -            if (unit.numerator.length === 0 && unit.denominator.length === 0) {
 -                unit.numerator = other.unit.numerator.slice(0);
 -                unit.denominator = other.unit.denominator.slice(0);
 -            } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
 -                // do nothing
 -            } else {
 -                other = other.convertTo(this.unit.usedUnits());
 -
 -                if(env.strictUnits && other.unit.toString() !== unit.toString()) {
 -                  throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
 -                    "' and '" + other.unit.toString() + "'.");
 -                }
 -
 -                value = tree.operate(env, op, this.value, other.value);
 -            }
 -        } else if (op === '*') {
 -            unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
 -            unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
 -            unit.cancel();
 -        } else if (op === '/') {
 -            unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
 -            unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
 -            unit.cancel();
 +        // Float values doesn't need a leading zero
 +        if (value > 0 && value < 1) {
 +            strValue = (strValue).substr(1);
          }
 -        return new(tree.Dimension)(value, unit);
 -    },
 -
 -    compare: function (other) {
 -        var a, b;
 -        
 -        if (!(other instanceof tree.Dimension)) {
 -            return undefined;
 -        }
 -        
 -        if (this.unit.isEmpty() || other.unit.isEmpty()) {
 -            a = this;
 -            b = other;
 -        } else {
 -            a = this.unify();
 -            b = other.unify();
 -            if (a.unit.compare(b.unit) !== 0) {
 -                return undefined;
 -            }                
 -        }
 -
 -        return tree.numericCompare(a.value, b.value);
 -    },
 -
 -    unify: function () {
 -        return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
 -    },
 +    }
  
 -    convertTo: function (conversions) {
 -        var value = this.value, unit = this.unit.clone(),
 -            i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
 +    output.add(strValue);
 +    this.unit.genCSS(env, output);
 +};
  
 -        if (typeof conversions === 'string') {
 -            for(i in tree.UnitConversions) {
 -                if (tree.UnitConversions[i].hasOwnProperty(conversions)) {
 -                    derivedConversions = {};
 -                    derivedConversions[i] = conversions;
 -                }
 -            }
 -            conversions = derivedConversions;
 -        }
 -        applyUnit = function (atomicUnit, denominator) {
 -          /*jshint loopfunc:true */
 -            if (group.hasOwnProperty(atomicUnit)) {
 -                if (denominator) {
 -                    value = value / (group[atomicUnit] / group[targetUnit]);
 -                } else {
 -                    value = value * (group[atomicUnit] / group[targetUnit]);
 -                }
 +// In an operation between two Dimensions,
 +// we default to the first Dimension's unit,
 +// so `1px + 2` will yield `3px`.
 +Dimension.prototype.operate = function (env, op, other) {
 +    /*jshint noempty:false */
 +    var value = this._operate(env, op, this.value, other.value),
 +        unit = this.unit.clone();
 +
 +    if (op === '+' || op === '-') {
 +        if (unit.numerator.length === 0 && unit.denominator.length === 0) {
 +            unit.numerator = other.unit.numerator.slice(0);
 +            unit.denominator = other.unit.denominator.slice(0);
 +        } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
 +            // do nothing
 +        } else {
 +            other = other.convertTo(this.unit.usedUnits());
  
 -                return targetUnit;
 +            if(env.strictUnits && other.unit.toString() !== unit.toString()) {
 +              throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
 +                "' and '" + other.unit.toString() + "'.");
              }
  
 -            return atomicUnit;
 -        };
 -
 -        for (groupName in conversions) {
 -            if (conversions.hasOwnProperty(groupName)) {
 -                targetUnit = conversions[groupName];
 -                group = tree.UnitConversions[groupName];
 -
 -                unit.map(applyUnit);
 -            }
 +            value = this._operate(env, op, this.value, other.value);
          }
 -
 +    } else if (op === '*') {
 +        unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
 +        unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
 +        unit.cancel();
 +    } else if (op === '/') {
 +        unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
 +        unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
          unit.cancel();
 -
 -        return new(tree.Dimension)(value, unit);
      }
 +    return new(Dimension)(value, unit);
  };
 +Dimension.prototype.compare = function (other) {
-     if (other instanceof Dimension) {
-         var a, b,
-             aValue, bValue;
++    var a, b;
  
-         if (this.unit.isEmpty() || other.unit.isEmpty()) {
-             a = this;
-             b = other;
-         } else {
-             a = this.unify();
-             b = other.unify();
-             if (a.unit.compare(b.unit) !== 0) {
-                 return -1;
-             }
-         }
-         aValue = a.value;
-         bValue = b.value;
 -// http://www.w3.org/TR/css3-values/#absolute-lengths
 -tree.UnitConversions = {
 -    length: {
 -         'm': 1,
 -        'cm': 0.01,
 -        'mm': 0.001,
 -        'in': 0.0254,
 -        'px': 0.0254 / 96,
 -        'pt': 0.0254 / 72,
 -        'pc': 0.0254 / 72 * 12
 -    },
 -    duration: {
 -        's': 1,
 -        'ms': 0.001
 -    },
 -    angle: {
 -        'rad': 1/(2*Math.PI),
 -        'deg': 1/360,
 -        'grad': 1/400,
 -        'turn': 1
++    if (!(other instanceof Dimension)) {
++        return undefined;
+     }
 -};
  
-         if (bValue > aValue) {
-             return -1;
-         } else if (bValue < aValue) {
-             return 1;
-         } else {
-             return 0;
 -tree.Unit = function (numerator, denominator, backupUnit) {
 -    this.numerator = numerator ? numerator.slice(0).sort() : [];
 -    this.denominator = denominator ? denominator.slice(0).sort() : [];
 -    this.backupUnit = backupUnit;
 -};
 -
 -tree.Unit.prototype = {
 -    type: "Unit",
 -    clone: function () {
 -        return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
 -    },
 -    genCSS: function (env, output) {
 -        if (this.numerator.length >= 1) {
 -            output.add(this.numerator[0]);
 -        } else
 -        if (this.denominator.length >= 1) {
 -            output.add(this.denominator[0]);
 -        } else
 -        if ((!env || !env.strictUnits) && this.backupUnit) {
 -            output.add(this.backupUnit);
 -        }
 -    },
 -    toCSS: tree.toCSS,
 -
 -    toString: function () {
 -      var i, returnStr = this.numerator.join("*");
 -      for (i = 0; i < this.denominator.length; i++) {
 -          returnStr += "/" + this.denominator[i];
 -      }
 -      return returnStr;
 -    },
 -
 -    compare: function (other) {
 -        return this.is(other.toString()) ? 0 : undefined;
 -    },
 -
 -    is: function (unitString) {
 -        return this.toString() === unitString;
 -    },
 -
 -    isLength: function () {
 -        return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
 -    },
 -
 -    isEmpty: function () {
 -        return this.numerator.length === 0 && this.denominator.length === 0;
 -    },
 -
 -    isSingular: function() {
 -        return this.numerator.length <= 1 && this.denominator.length === 0;
 -    },
 -
 -    map: function(callback) {
 -        var i;
 -
 -        for (i = 0; i < this.numerator.length; i++) {
 -            this.numerator[i] = callback(this.numerator[i], false);
--        }
 -
 -        for (i = 0; i < this.denominator.length; i++) {
 -            this.denominator[i] = callback(this.denominator[i], true);
++    if (this.unit.isEmpty() || other.unit.isEmpty()) {
++        a = this;
++        b = other;
 +    } else {
-         return -1;
++        a = this.unify();
++        b = other.unify();
++        if (a.unit.compare(b.unit) !== 0) {
++            return undefined;
+         }
 -    },
 -
 -    usedUnits: function() {
 -        var group, result = {}, mapUnit;
 -
 -        mapUnit = function (atomicUnit) {
 -        /*jshint loopfunc:true */
 -            if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
 -                result[groupName] = atomicUnit;
 -            }
 -
 -            return atomicUnit;
 -        };
 -
 -        for (var groupName in tree.UnitConversions) {
 -            if (tree.UnitConversions.hasOwnProperty(groupName)) {
 -                group = tree.UnitConversions[groupName];
 +    }
+ 
 -                this.map(mapUnit);
++    return Node.numericCompare(a.value, b.value);
 +};
 +Dimension.prototype.unify = function () {
 +    return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
 +};
 +Dimension.prototype.convertTo = function (conversions) {
 +    var value = this.value, unit = this.unit.clone(),
 +        i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
 +
 +    if (typeof conversions === 'string') {
 +        for(i in unitConversions) {
 +            if (unitConversions[i].hasOwnProperty(conversions)) {
 +                derivedConversions = {};
 +                derivedConversions[i] = conversions;
              }
          }
 -
 -        return result;
 -    },
 -
 -    cancel: function () {
 -        var counter = {}, atomicUnit, i, backup;
 -
 -        for (i = 0; i < this.numerator.length; i++) {
 -            atomicUnit = this.numerator[i];
 -            if (!backup) {
 -                backup = atomicUnit;
 +        conversions = derivedConversions;
 +    }
 +    applyUnit = function (atomicUnit, denominator) {
 +      /*jshint loopfunc:true */
 +        if (group.hasOwnProperty(atomicUnit)) {
 +            if (denominator) {
 +                value = value / (group[atomicUnit] / group[targetUnit]);
 +            } else {
 +                value = value * (group[atomicUnit] / group[targetUnit]);
              }
 -            counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
 -        }
  
 -        for (i = 0; i < this.denominator.length; i++) {
 -            atomicUnit = this.denominator[i];
 -            if (!backup) {
 -                backup = atomicUnit;
 -            }
 -            counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
 +            return targetUnit;
          }
  
 -        this.numerator = [];
 -        this.denominator = [];
 +        return atomicUnit;
 +    };
  
 -        for (atomicUnit in counter) {
 -            if (counter.hasOwnProperty(atomicUnit)) {
 -                var count = counter[atomicUnit];
 +    for (groupName in conversions) {
 +        if (conversions.hasOwnProperty(groupName)) {
 +            targetUnit = conversions[groupName];
 +            group = unitConversions[groupName];
  
 -                if (count > 0) {
 -                    for (i = 0; i < count; i++) {
 -                        this.numerator.push(atomicUnit);
 -                    }
 -                } else if (count < 0) {
 -                    for (i = 0; i < -count; i++) {
 -                        this.denominator.push(atomicUnit);
 -                    }
 -                }
 -            }
 +            unit.map(applyUnit);
          }
 +    }
  
 -        if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
 -            this.backupUnit = backup;
 -        }
 +    unit.cancel();
  
 -        this.numerator.sort();
 -        this.denominator.sort();
 -    }
 +    return new(Dimension)(value, unit);
  };
 -
 -})(require('../tree'));
 +module.exports = Dimension;
diff --cc lib/less/tree/keyword.js
index 7f99a71,4b633b7..c800692
--- a/lib/less/tree/keyword.js
+++ b/lib/less/tree/keyword.js
@@@ -1,21 -1,17 +1,14 @@@
 -(function (tree) {
 +var Node = require("./node.js");
  
 -tree.Keyword = function (value) { this.value = value; };
 -tree.Keyword.prototype = {
 -    type: "Keyword",
 -    eval: function () { return this; },
 -    genCSS: function (env, output) {
 -        if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
 -        output.add(this.value);
 -    },
 -    toCSS: tree.toCSS
 +var Keyword = function (value) { this.value = value; };
 +Keyword.prototype = new Node();
 +Keyword.prototype.type = "Keyword";
 +Keyword.prototype.genCSS = function (env, output) {
 +    if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
 +    output.add(this.value);
  };
- Keyword.prototype.compare = function (other) {
-     if (other instanceof Keyword) {
-         return other.value === this.value ? 0 : 1;
-     } else {
-         return -1;
-     }
- };
  
 -tree.True = new(tree.Keyword)('true');
 -tree.False = new(tree.Keyword)('false');
 +Keyword.True = new(Keyword)('true');
 +Keyword.False = new(Keyword)('false');
  
 -})(require('../tree'));
 +module.exports = Keyword;
diff --cc lib/less/tree/node.js
index c6bdd57,0000000..dc14c65
mode 100644,000000..100644
--- a/lib/less/tree/node.js
+++ b/lib/less/tree/node.js
@@@ -1,35 -1,0 +1,74 @@@
 +var Node = function() {
 +};
 +Node.prototype.toCSS = function (env) {
 +    var strs = [];
 +    this.genCSS(env, {
 +        add: function(chunk, fileInfo, index) {
 +            strs.push(chunk);
 +        },
 +        isEmpty: function () {
 +            return strs.length === 0;
 +        }
 +    });
 +    return strs.join('');
 +};
 +Node.prototype.genCSS = function (env, output) {
 +    output.add(this.value);
 +};
 +Node.prototype.accept = function (visitor) {
 +    this.value = visitor.visit(this.value);
 +};
 +Node.prototype.eval = function () { return this; };
 +Node.prototype._operate = function (env, op, a, b) {
 +    switch (op) {
 +        case '+': return a + b;
 +        case '-': return a - b;
 +        case '*': return a * b;
 +        case '/': return a / b;
 +    }
 +};
 +Node.prototype.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));
 +};
++Node.compare = function (a, b) {
++    /* returns:
++     -1: a < b
++     0: a = b
++     1: a > b
++     and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */
++
++    if ((a.compare) &&
++        // for "symmetric results" force toCSS-based comparison
++        // of Quoted or Anonymous if either value is one of those
++        !(b.type === "Quoted" || b.type === "Anonymous")) {
++        return a.compare(b);
++    } else if (b.compare) {
++        return -b.compare(a);
++    } else if (a.type !== b.type) {
++        return undefined;
++    }
++
++    a = a.value;
++    b = b.value;
++    if (!Array.isArray(a)) {
++        return a === b ? 0 : undefined;
++    }
++    if (a.length !== b.length) {
++        return undefined;
++    }
++    for (var i = 0; i < a.length; i++) {
++        if (Node.compare(a[i], b[i]) !== 0) {
++            return undefined;
++        }
++    }
++    return 0;
++};
++
++Node.numericCompare = function (a, b) {
++    return a  <  b ? -1
++        : a === b ?  0
++        : a  >  b ?  1 : undefined;
++};
 +module.exports = Node;
diff --cc lib/less/tree/quoted.js
index c46f8d5,d20939e..3c599a9
--- a/lib/less/tree/quoted.js
+++ b/lib/less/tree/quoted.js
@@@ -1,54 -1,42 +1,41 @@@
- var JsEvalNode = require("./js-eval-node.js"),
 -(function (tree) {
++var Node = require("./node.js"),
++    JsEvalNode = require("./js-eval-node.js"),
 +    Variable = require("./variable.js");
  
 -tree.Quoted = function (str, content, escaped, index, currentFileInfo) {
 +var Quoted = function (str, content, escaped, index, currentFileInfo) {
      this.escaped = escaped;
      this.value = content || '';
      this.quote = str.charAt(0);
      this.index = index;
      this.currentFileInfo = currentFileInfo;
  };
 -tree.Quoted.prototype = {
 -    type: "Quoted",
 -    genCSS: function (env, output) {
 -        if (!this.escaped) {
 -            output.add(this.quote, this.currentFileInfo, this.index);
 -        }
 -        output.add(this.value);
 -        if (!this.escaped) {
 -            output.add(this.quote);
 -        }
 -    },
 -    toCSS: tree.toCSS,
 -    eval: function (env) {
 -        var that = this;
 -        var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
 -            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
 -        }).replace(/@\{([\w-]+)\}/g, function (_, name) {
 -            var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
 -            return (v instanceof tree.Quoted) ? v.value : v.toCSS();
 -        });
 -        return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
 -    },
 -    compare: function (other) {
 -        // when comparing quoted strings allow the quote to differ
 -        if (other.type === "Quoted" && !this.escaped && !other.escaped) {
 -            return tree.numericCompare(this.value, other.value);
 -        } else {
 -            return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
 -        }
 +Quoted.prototype = new JsEvalNode();
 +Quoted.prototype.type = "Quoted";
 +Quoted.prototype.genCSS = function (env, output) {
 +    if (!this.escaped) {
 +        output.add(this.quote, this.currentFileInfo, this.index);
 +    }
 +    output.add(this.value);
 +    if (!this.escaped) {
 +        output.add(this.quote);
      }
  };
 -
 -})(require('../tree'));
 +Quoted.prototype.eval = function (env) {
 +    var that = this;
 +    var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
 +        return String(that.evaluateJavaScript(exp, env));
 +    }).replace(/@\{([\w-]+)\}/g, function (_, name) {
 +        var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
 +        return (v instanceof Quoted) ? v.value : v.toCSS();
 +    });
 +    return new(Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
 +};
- Quoted.prototype.compare = function (x) {
-     if (!x.toCSS) {
-         return -1;
-     }
- 
-     var left, right;
- 
++Quoted.prototype.compare = function (other) {
 +    // when comparing quoted strings allow the quote to differ
-     if (x.type === "Quoted" && !this.escaped && !x.escaped) {
-         left = x.value;
-         right = this.value;
++    if (other.type === "Quoted" && !this.escaped && !other.escaped) {
++        return Node.numericCompare(this.value, other.value);
 +    } else {
-         left = this.toCSS();
-         right = x.toCSS();
++        return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
 +    }
- 
-     if (left === right) {
-         return 0;
-     }
- 
-     return left < right ? -1 : 1;
 +};
 +module.exports = Quoted;
diff --cc lib/less/tree/unit.js
index b0f7740,0000000..64b6b4f
mode 100644,000000..100644
--- a/lib/less/tree/unit.js
+++ b/lib/less/tree/unit.js
@@@ -1,126 -1,0 +1,126 @@@
 +var Node = require("./node.js"),
 +    unitConversions = require("../data/unit-conversions.js");
 +
 +var Unit = function (numerator, denominator, backupUnit) {
 +    this.numerator = numerator ? numerator.slice(0).sort() : [];
 +    this.denominator = denominator ? denominator.slice(0).sort() : [];
 +    this.backupUnit = backupUnit;
 +};
 +
 +Unit.prototype = new Node();
 +Unit.prototype.type = "Unit";
 +Unit.prototype.clone = function () {
 +    return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
 +};
 +Unit.prototype.genCSS = function (env, output) {
 +    if (this.numerator.length >= 1) {
 +        output.add(this.numerator[0]);
 +    } else
 +    if (this.denominator.length >= 1) {
 +        output.add(this.denominator[0]);
 +    } else
 +    if ((!env || !env.strictUnits) && this.backupUnit) {
 +        output.add(this.backupUnit);
 +    }
 +};
 +Unit.prototype.toString = function () {
 +    var i, returnStr = this.numerator.join("*");
 +    for (i = 0; i < this.denominator.length; i++) {
 +        returnStr += "/" + this.denominator[i];
 +    }
 +    return returnStr;
 +};
 +Unit.prototype.compare = function (other) {
-     return this.is(other.toString()) ? 0 : -1;
++    return this.is(other.toString()) ? 0 : undefined;
 +};
 +Unit.prototype.is = function (unitString) {
 +    return this.toString().toUpperCase() === unitString.toUpperCase();
 +};
 +Unit.prototype.isLength = function () {
 +    return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
 +};
 +Unit.prototype.isEmpty = function () {
 +    return this.numerator.length === 0 && this.denominator.length === 0;
 +};
 +Unit.prototype.isSingular = function() {
 +    return this.numerator.length <= 1 && this.denominator.length === 0;
 +};
 +Unit.prototype.map = function(callback) {
 +    var i;
 +
 +    for (i = 0; i < this.numerator.length; i++) {
 +        this.numerator[i] = callback(this.numerator[i], false);
 +    }
 +
 +    for (i = 0; i < this.denominator.length; i++) {
 +        this.denominator[i] = callback(this.denominator[i], true);
 +    }
 +};
 +Unit.prototype.usedUnits = function() {
 +    var group, result = {}, mapUnit;
 +
 +    mapUnit = function (atomicUnit) {
 +        /*jshint loopfunc:true */
 +        if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
 +            result[groupName] = atomicUnit;
 +        }
 +
 +        return atomicUnit;
 +    };
 +
 +    for (var groupName in unitConversions) {
 +        if (unitConversions.hasOwnProperty(groupName)) {
 +            group = unitConversions[groupName];
 +
 +            this.map(mapUnit);
 +        }
 +    }
 +
 +    return result;
 +};
 +Unit.prototype.cancel = function () {
 +    var counter = {}, atomicUnit, i, backup;
 +
 +    for (i = 0; i < this.numerator.length; i++) {
 +        atomicUnit = this.numerator[i];
 +        if (!backup) {
 +            backup = atomicUnit;
 +        }
 +        counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
 +    }
 +
 +    for (i = 0; i < this.denominator.length; i++) {
 +        atomicUnit = this.denominator[i];
 +        if (!backup) {
 +            backup = atomicUnit;
 +        }
 +        counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
 +    }
 +
 +    this.numerator = [];
 +    this.denominator = [];
 +
 +    for (atomicUnit in counter) {
 +        if (counter.hasOwnProperty(atomicUnit)) {
 +            var count = counter[atomicUnit];
 +
 +            if (count > 0) {
 +                for (i = 0; i < count; i++) {
 +                    this.numerator.push(atomicUnit);
 +                }
 +            } else if (count < 0) {
 +                for (i = 0; i < -count; i++) {
 +                    this.denominator.push(atomicUnit);
 +                }
 +            }
 +        }
 +    }
 +
 +    if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
 +        this.backupUnit = backup;
 +    }
 +
 +    this.numerator.sort();
 +    this.denominator.sort();
 +};
 +module.exports = Unit;
diff --cc test/less/mixins-guards.less
index 525b815,8d1ad0e..1b27417
--- a/test/less/mixins-guards.less
+++ b/test/less/mixins-guards.less
@@@ -121,15 -121,58 +121,58 @@@
  .stringguard(@str) when (@str = 'theme1')					{ content: @str is 'theme1'; }
  .stringguard(@str) when not ('theme2' = @str)				{ content: @str is not 'theme2'; }
  .stringguard(@str) when (~"theme1" = @str)					{ content: @str is theme1; }
 -.stringguard(@str)											{}
 +.stringguard(@str)                                          {}
  .stringguardtest {
      .stringguard("theme1");
 -	.stringguard("theme2");
 +    .stringguard("theme2");
      .stringguard('theme1');
      .stringguard('theme2');
 -	.stringguard(theme1);
 +    .stringguard(theme1);
  }
  
+ .generic(@a, @b) {/**/}
+ .generic(@a, @b) when (@a = @b) {content: @a is equal to @b}
+ .generic(@a, @b) when (@b = @a) {content: @b is equal to @a too}
+ .generic(@a, @b) when (@a < @b) {content: @a is less than @b}
+ .generic(@a, @b) when (@b < @a) {content: @b is less than @a too}
+ .generic(@a, @b) when (@a > @b) {content: @a is greater than @b}
+ .generic(@a, @b) when (@b > @a) {content: @b is greater than @a too}
+ .generic(@a, @b) when not(@a = @b) {content: @a is not equal to @b}
+ .generic(@a, @b) when not(@b = @a) {content: @b is not equal to @a too}
+ 
+ .variouse-types-comparision {
+     .generic(true, false);
+     .generic(1, true);
+     .generic(2, 2px);
+     .generic(3, ~"3");
+     .generic(5, ~"4");
+     .generic(abc, ~"abc");
+     .generic(abc, "abc");
+     .generic('abc', "abd");
+     .generic(6, e("6"));
+     .generic(`9`, 8);
+     .generic(a, b);
+     .generic(1 2, 3);
+ }
+ 
+ .list-comparision {
+     .generic(a b c, a b c);
+     .generic(a b c, a b d);
+     .generic(a, b, c; a, b, c);
+     .generic(a, b, c; a, b, d);
+     .generic(1 2px 300ms, 1em 2 .3s);
+     
+     @space-list: 1 2 3;
+     @comma-list: 1, 2, 3;
+     @compound: @space-list @comma-list;
+     
+     .generic(@space-list, @comma-list);
+     .generic(@comma-list, ~"1, 2, 3");
+     .generic(@compound, @space-list @comma-list);
+     .generic(@compound, @comma-list @space-list);
+     .generic(@compound 4, ~"1 2 3 1, 2, 3 4");
+ }
+ 
  .mixin(...) {
    catch:all;
  }

-- 
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