Comparing version 0.10.0 to 0.11.0
0.11.0 / 2011-04-01 | ||
================== | ||
* Added `HSLA#add(h,s,l,a)` | ||
* Added `HSLA#sub(h,s,l,a)` | ||
* Added `RGBA#add(r,g,b,a)` | ||
* Added `RGBA#sub(r,g,b,a)` | ||
* Added `RGBA#multiply(n)` | ||
* Added `RGBA#divide(n)` | ||
* Added `HSLA#adjustHue(deg)` | ||
* Added `HSLA#adjustLightness(percent)` | ||
* Added `HSLA#adjustSaturation(percent)` | ||
* Added `linear-gradient()` example | ||
* Added `s(fmt, ...)` built-in; sprintf-like | ||
* Added `%` sprintf-like string operator, ex: `'%s %s' % (1 2)` | ||
* Added `current-property` local variable | ||
* Added `add-property(name, val)` | ||
* Added the ability for functions to duplicate the property they are invoked within | ||
* Added `[]=` operator support. Ex: `fonts[1] = arial`, `nums[1..3] = 2` | ||
* Added `-I, --include <path>` to stylus(1). Closes #206 | ||
* Changed; null now outputs "null" instead of "[Null]" | ||
within | ||
* Added support for `50 + 25% == 75` | ||
* Added support for `rgba + 25%` to lighten | ||
* Added support for `rgba - 25%` to darken | ||
* Added support for `rgba - 25` to adjust rgb values | ||
* Fixed hsl operation support, all operations are equivalent on rgba/hsla nodes | ||
* Fixed degree rotation | ||
0.10.0 / 2011-03-29 | ||
@@ -3,0 +33,0 @@ ================== |
@@ -12,3 +12,4 @@ | ||
var nodes = require('../nodes') | ||
var Compiler = require('../visitor/compiler') | ||
, nodes = require('../nodes') | ||
, utils = require('../utils') | ||
@@ -75,3 +76,3 @@ , Image = require('./image'); | ||
exports.hsla = function(hue, saturation, lightness, alpha){ | ||
exports.hsla = function hsla(hue, saturation, lightness, alpha){ | ||
switch (arguments.length) { | ||
@@ -113,3 +114,3 @@ case 1: | ||
exports.hsl = function(hue, saturation, lightness){ | ||
exports.hsl = function hsl(hue, saturation, lightness){ | ||
if (1 == arguments.length) { | ||
@@ -153,3 +154,3 @@ utils.assertColor(hue, 'color'); | ||
exports.typeof = | ||
exports['type-of'] = function(node){ | ||
exports['type-of'] = function type(node){ | ||
utils.assertPresent(node, 'expression'); | ||
@@ -169,3 +170,3 @@ var type = node.nodeName; | ||
exports.component = function(color, name) { | ||
exports.component = function component(color, name) { | ||
utils.assertColor(color, 'color'); | ||
@@ -194,3 +195,3 @@ utils.assertString(name, 'name'); | ||
exports.red = function(color){ | ||
exports.red = function red(color){ | ||
return exports.component(color, new nodes.String('red')); | ||
@@ -212,3 +213,3 @@ }; | ||
exports.green = function(color){ | ||
exports.green = function green(color){ | ||
return exports.component(color, new nodes.String('green')); | ||
@@ -230,3 +231,3 @@ }; | ||
exports.blue = function(color){ | ||
exports.blue = function blue(color){ | ||
return exports.component(color, new nodes.String('blue')); | ||
@@ -257,3 +258,3 @@ }; | ||
exports.rgba = function(red, green, blue, alpha){ | ||
exports.rgba = function rgba(red, green, blue, alpha){ | ||
switch (arguments.length) { | ||
@@ -308,3 +309,3 @@ case 1: | ||
exports.rgb = function(red, green, blue){ | ||
exports.rgb = function rgb(red, green, blue){ | ||
switch (arguments.length) { | ||
@@ -344,3 +345,3 @@ case 1: | ||
exports.unquote = function(string){ | ||
exports.unquote = function unquote(string){ | ||
utils.assertString(string, 'string'); | ||
@@ -359,3 +360,3 @@ return new nodes.Literal(string.string); | ||
exports.unit = function(unit, type){ | ||
exports.unit = function unit(unit, type){ | ||
utils.assertType(unit, nodes.Unit, 'unit'); | ||
@@ -380,3 +381,3 @@ | ||
exports.lookup = function(name){ | ||
exports.lookup = function lookup(name){ | ||
utils.assertType(name, nodes.String, 'name'); | ||
@@ -398,3 +399,3 @@ var node = this.lookup(name.val); | ||
exports.operate = function(op, left, right){ | ||
exports.operate = function operate(op, left, right){ | ||
utils.assertType(op, nodes.String, 'op'); | ||
@@ -426,3 +427,3 @@ utils.assertPresent(left, 'left'); | ||
exports.match = function(pattern, val){ | ||
exports.match = function match(pattern, val){ | ||
utils.assertType(pattern, nodes.String, 'pattern'); | ||
@@ -442,3 +443,3 @@ utils.assertString(val, 'val'); | ||
(exports.length = function(expr){ | ||
(exports.length = function length(expr){ | ||
if (expr) { | ||
@@ -461,3 +462,3 @@ if (expr.nodes) { | ||
(exports.p = function(expr){ | ||
(exports.p = function p(expr){ | ||
expr = utils.unwrap(expr); | ||
@@ -476,3 +477,3 @@ console.log('\033[90minspect:\033[0m %s' | ||
exports.error = function(msg){ | ||
exports.error = function error(msg){ | ||
utils.assertType(msg, nodes.String, 'msg'); | ||
@@ -489,3 +490,3 @@ throw new Error(msg.val); | ||
exports.warn = function(msg){ | ||
exports.warn = function warn(msg){ | ||
utils.assertType(msg, nodes.String, 'msg'); | ||
@@ -502,3 +503,3 @@ console.warn('Warning: %s', msg.val); | ||
exports.trace = function(){ | ||
exports.trace = function trace(){ | ||
console.log(this.stack); | ||
@@ -509,2 +510,36 @@ return nodes.null; | ||
/** | ||
* Return a `Literal` with the given `fmt`, and | ||
* variable number of arguments. | ||
* | ||
* @param {String} fmt | ||
* @param {Node} ... | ||
* @return {Literal} | ||
* @api public | ||
*/ | ||
(exports.s = function s(fmt){ | ||
fmt = utils.unwrap(fmt).nodes[0]; | ||
utils.assertString(fmt); | ||
var self = this | ||
, str = fmt.string | ||
, args = arguments | ||
, i = 1; | ||
// format | ||
str = str.replace(/%(s|d)/g, function(_, specifier){ | ||
var arg = args[i++] || nodes.null; | ||
switch (specifier) { | ||
case 's': | ||
return new Compiler(arg, self.options).compile(); | ||
case 'd': | ||
arg = utils.unwrap(arg).first; | ||
if ('unit' != arg.nodeName) throw new Error('%d requires a unit'); | ||
return arg.val; | ||
} | ||
}); | ||
return new nodes.Literal(str); | ||
}).raw = true; | ||
/** | ||
* Return the opposites of the given `positions`. | ||
@@ -522,3 +557,3 @@ * | ||
(exports['opposite-position'] = function(positions){ | ||
(exports['opposite-position'] = function oppositePosition(positions){ | ||
var expr = new nodes.Expression; | ||
@@ -558,3 +593,3 @@ utils.unwrap(positions).nodes.forEach(function(pos, i){ | ||
exports['image-size'] = function(img) { | ||
exports['image-size'] = function imageSize(img) { | ||
utils.assertType(img, nodes.String, 'img'); | ||
@@ -585,3 +620,3 @@ var img = new Image(this, img.string); | ||
exports['-math'] = function(n, fn){ | ||
exports['-math'] = function math(n, fn){ | ||
return new nodes.Unit(Math[fn.string](n.val), n.type); | ||
@@ -600,3 +635,3 @@ }; | ||
exports['-adjust'] = function(color, prop, amount){ | ||
exports['-adjust'] = function adjust(color, prop, amount){ | ||
var hsl = color.hsla.clone(); | ||
@@ -610,1 +645,41 @@ prop = { hue: 'h', saturation: 's', lightness: 'l' }[prop.string]; | ||
}; | ||
/** | ||
* Return a clone of the given `expr`. | ||
* | ||
* @param {Expression} expr | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
(exports.clone = function clone(expr){ | ||
utils.assertPresent(expr, 'expr'); | ||
return expr.clone(); | ||
}).raw = true; | ||
/** | ||
* Add property `name` with the given `expr` | ||
* to the mixin-able block. | ||
* | ||
* @param {String|Ident|Literal} name | ||
* @param {Expression} expr | ||
* @return {Property} | ||
* @api public | ||
*/ | ||
(exports['add-property'] = function addProperty(name, expr){ | ||
utils.assertType(name, nodes.Expression, 'name'); | ||
name = utils.unwrap(name).first; | ||
utils.assertString(name, 'name'); | ||
utils.assertType(expr, nodes.Expression, 'expr'); | ||
var prop = new nodes.Property([name], expr); | ||
var block = this.closestBlock; | ||
var len = block.nodes.length | ||
, head = block.nodes.slice(0, block.index) | ||
, tail = block.nodes.slice(block.index++, len); | ||
head.push(prop); | ||
block.nodes = head.concat(tail); | ||
return prop; | ||
}).raw = true; |
@@ -34,15 +34,2 @@ | ||
/** | ||
* Return "Arguments(<a> <b> <c>)" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Arguments.prototype.toString = function(){ | ||
return 'Arguments(' + this.nodes.map(function(node){ | ||
return node.toString(); | ||
}).join(this.isList ? ', ' : ' ') + ')'; | ||
}; | ||
/** | ||
* Resolve keyword arguments for the given `fn`. | ||
@@ -49,0 +36,0 @@ * |
@@ -51,3 +51,4 @@ | ||
clone.lineno = this.lineno; | ||
if (this.val) clone.val = this.val.clone(); | ||
return clone; | ||
}; |
@@ -56,2 +56,2 @@ | ||
return this.name + '()'; | ||
}; | ||
}; |
@@ -109,4 +109,18 @@ | ||
Expression.prototype.operate = function(op, right){ | ||
Expression.prototype.operate = function(op, right, val){ | ||
switch (op) { | ||
case '[]=': | ||
var self = this | ||
, range = utils.unwrap(right).nodes | ||
, val = utils.unwrap(val) | ||
, len; | ||
range.forEach(function(unit){ | ||
len = self.nodes.length; | ||
if ('unit' == unit.nodeName) { | ||
var i = unit.val; | ||
while (i-- > len) self.nodes[i] = nodes.null; | ||
self.nodes[unit.val] = val; | ||
} | ||
}); | ||
return val; | ||
case '[]': | ||
@@ -124,5 +138,7 @@ var expr = new nodes.Expression | ||
? nodes.null | ||
: expr; | ||
: utils.unwrap(expr); | ||
case 'in': | ||
return Node.prototype.operate.call(this, op, right); | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
return this.first.operate(op, right, val); | ||
} | ||
@@ -145,2 +161,1 @@ }; | ||
@@ -49,2 +49,13 @@ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Function.prototype.__defineGetter__('hash', function(){ | ||
return 'function ' + this.name; | ||
}); | ||
/** | ||
* Return a clone of this node. | ||
@@ -51,0 +62,0 @@ * |
@@ -50,4 +50,4 @@ | ||
+ this.h + ',' | ||
+ this.s + ',' | ||
+ this.l + ',' | ||
+ this.s.toFixed(0) + ',' | ||
+ this.l.toFixed(0) + ',' | ||
+ this.a + ')'; | ||
@@ -96,24 +96,31 @@ }; | ||
/** | ||
* Coerce RGBA to HSLA. | ||
* Add h,s,l to the current component values. | ||
* | ||
* @param {Node} other | ||
* @return {Node} | ||
* @param {Number} h | ||
* @param {Number} s | ||
* @param {Number} l | ||
* @return {HSLA} new node | ||
* @api public | ||
*/ | ||
HSLA.prototype.coerce = function(other){ | ||
if (other instanceof nodes.RGBA) { | ||
return other.hsla; | ||
} else if (other instanceof nodes.Unit) { | ||
var n = other.val; | ||
HSLA.prototype.add = function(h,s,l){ | ||
return new HSLA( | ||
this.h + h | ||
, this.s + s | ||
, this.l + l | ||
, this.a); | ||
}; | ||
// unit type | ||
switch (other.type) { | ||
case 'deg': return new HSLA(n,0,0,1); | ||
} | ||
/** | ||
* Subtract h,s,l from the current component values. | ||
* | ||
* @param {Number} h | ||
* @param {Number} s | ||
* @param {Number} l | ||
* @return {HSLA} new node | ||
* @api public | ||
*/ | ||
return new HSLA(n,n,n,1); | ||
} else { | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
HSLA.prototype.sub = function(h,s,l){ | ||
return this.add(-h, -s, -l); | ||
}; | ||
@@ -131,34 +138,3 @@ | ||
HSLA.prototype.operate = function(op, right){ | ||
switch (op) { | ||
case '+': | ||
return new HSLA( | ||
this.h + right.h | ||
, this.s + right.s | ||
, this.l + right.l | ||
, 1 == right.a ? this.a : (this.a + right.a) | ||
); | ||
case '-': | ||
return new HSLA( | ||
this.h - right.h | ||
, this.s - right.s | ||
, this.l - right.l | ||
, 1 == right.a ? this.a : (this.a - right.a) | ||
); | ||
case '*': | ||
return new HSLA( | ||
this.h * right.h | ||
, this.s * right.s | ||
, this.l * right.l | ||
, this.a * right.a | ||
); | ||
case '/': | ||
return new HSLA( | ||
this.h / right.h | ||
, this.s / right.s | ||
, this.l / right.l | ||
, this.a / right.a | ||
); | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
} | ||
return this.rgba.operate(op, right).hsla; | ||
}; | ||
@@ -209,2 +185,28 @@ | ||
/** | ||
* Adjust lightness by `percent`. | ||
* | ||
* @param {Number} percent | ||
* @return {HSLA} for chaining | ||
* @api public | ||
*/ | ||
HSLA.prototype.adjustLightness = function(percent){ | ||
this.l = clampPercentage(this.l + this.l * (percent / 100)); | ||
return this; | ||
}; | ||
/** | ||
* Adjust hue by `deg`. | ||
* | ||
* @param {Number} deg | ||
* @return {HSLA} for chaining | ||
* @api public | ||
*/ | ||
HSLA.prototype.adjustHue = function(deg){ | ||
this.h = clampDegrees(this.h + deg); | ||
return this; | ||
}; | ||
/** | ||
* Clamp degree `n` >= 0 and <= 360. | ||
@@ -218,4 +220,4 @@ * | ||
function clampDegrees(n) { | ||
if (n > 360) return n - 360; | ||
if (n < 0) return clampDegrees(Math.abs(n)); | ||
if (n > 360) return clampDegrees(n - 360); | ||
if (n < 0) return clampDegrees(360 + n); | ||
return n; | ||
@@ -222,0 +224,0 @@ } |
@@ -91,8 +91,36 @@ | ||
Ident.prototype.coerce = function(other){ | ||
if (other instanceof nodes.Literal || | ||
other instanceof Ident) { | ||
return other; | ||
} else { | ||
return Node.prototype.coerce.call(this, other); | ||
switch (other.nodeName) { | ||
case 'ident': | ||
case 'string': | ||
case 'literal': | ||
return new Ident(other.string); | ||
default: | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Ident.prototype.operate = function(op, right){ | ||
var val = right.first; | ||
switch (op) { | ||
case '-': | ||
if ('unit' == val.nodeName) { | ||
var expr = new nodes.Expression; | ||
val.val = -val.val; | ||
expr.push(this); | ||
expr.push(val); | ||
return expr; | ||
} | ||
case '+': | ||
return new nodes.Ident(this.string + this.coerce(val).string); | ||
} | ||
return Node.prototype.operate.call(this, op, right); | ||
}; |
@@ -25,2 +25,3 @@ | ||
this.val = str; | ||
this.string = str; | ||
}; | ||
@@ -65,9 +66,29 @@ | ||
Literal.prototype.coerce = function(other){ | ||
if (other instanceof Literal || | ||
other instanceof nodes.Ident || | ||
other instanceof nodes.String) { | ||
return new Literal(other.string); | ||
} else { | ||
return Node.prototype.coerce.call(this, other); | ||
switch (other.nodeName) { | ||
case 'ident': | ||
case 'string': | ||
case 'literal': | ||
return new Literal(other.string); | ||
default: | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Literal.prototype.operate = function(op, right){ | ||
var val = right.first; | ||
switch (op) { | ||
case '+': | ||
return new nodes.Literal(this.string + this.coerce(val).string); | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
} | ||
}; |
@@ -95,2 +95,22 @@ | ||
/** | ||
* Return false if `op` is generally not coerced. | ||
* | ||
* @param {String} op | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
Node.prototype.shouldCoerce = function(op){ | ||
switch (op) { | ||
case 'is a': | ||
case 'in': | ||
case '||': | ||
case '&&': | ||
return false; | ||
default: | ||
return true; | ||
} | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
@@ -108,9 +128,5 @@ * | ||
if ('string' == right.nodeName) { | ||
if ('color' == right.string) { | ||
return nodes.Boolean('rgba' == this.nodeName || 'hsla' == this.nodeName); | ||
} else { | ||
return nodes.Boolean(this.nodeName == right.val); | ||
} | ||
return nodes.Boolean(this.nodeName == right.val); | ||
} else { | ||
throw new Error('"is a" expects a string, got ' + right.nodeName); | ||
throw new Error('"is a" expects a string, got ' + right.toString()); | ||
} | ||
@@ -167,2 +183,21 @@ case '==': | ||
/** | ||
* Initialize a new `CoercionError` with the given `msg`. | ||
* | ||
* @param {String} msg | ||
* @api private | ||
*/ | ||
function CoercionError(msg) { | ||
this.name = 'CoercionError' | ||
this.message = msg | ||
Error.captureStackTrace(this, CoercionError); | ||
} | ||
/** | ||
* Inherit from `Error.prototype`. | ||
*/ | ||
CoercionError.prototype.__proto__ = Error.prototype; | ||
/** | ||
* Default coercion throws. | ||
@@ -177,3 +212,3 @@ * | ||
if (other.nodeName == this.nodeName) return other; | ||
throw new Error('cannot coerce ' + other + ' to ' + this.nodeName); | ||
throw new CoercionError('cannot coerce ' + other + ' to ' + this.nodeName); | ||
}; |
@@ -38,3 +38,3 @@ | ||
Null.prototype.toString = function(){ | ||
return '[Null]'; | ||
return 'null'; | ||
}; | ||
@@ -41,0 +41,0 @@ |
@@ -48,1 +48,25 @@ | ||
}; | ||
/** | ||
* Return string representation of this node. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Property.prototype.toString = function(){ | ||
return 'property(' + this.name + ', ' + this.expr + ')'; | ||
}; | ||
/** | ||
* Operate on the property expression. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Property.prototype.operate = function(op, right, val){ | ||
return this.expr.operate(op, right, val); | ||
}; |
@@ -112,31 +112,72 @@ | ||
/** | ||
* Coerce HSLA and Unit to RGBA. | ||
* Add r,g,b,a to the current component values. | ||
* | ||
* @param {Node} other | ||
* @return {Node} | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.coerce = function(other){ | ||
if (other instanceof nodes.HSLA) { | ||
return other.rgba; | ||
} else if (other instanceof nodes.Unit) { | ||
var n = other.val; | ||
if ('%' == other.type) { | ||
n /= 100; | ||
return new RGBA( | ||
this.r * n | ||
, this.g * n | ||
, this.b * n | ||
, 1 | ||
); | ||
} else { | ||
return RGBA.withoutClamping(n,n,n,1); | ||
} | ||
} else { | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
RGBA.prototype.add = function(r,g,b,a){ | ||
return new RGBA( | ||
this.r + r | ||
, this.g + g | ||
, this.b + b | ||
, this.a + a); | ||
}; | ||
/** | ||
* Subtract r,g,b,a from the current component values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.sub = function(r,g,b,a){ | ||
return new RGBA( | ||
this.r - r | ||
, this.g - g | ||
, this.b - b | ||
, a == 1 ? this.a : this.a - a); | ||
}; | ||
/** | ||
* Multiply rgb components by `n`. | ||
* | ||
* @param {String} n | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.multiply = function(n){ | ||
return new RGBA( | ||
this.r * n | ||
, this.g * n | ||
, this.b * n | ||
, this.a); | ||
}; | ||
/** | ||
* Divide rgb components by `n`. | ||
* | ||
* @param {String} n | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.divide = function(n){ | ||
return new RGBA( | ||
this.r / n | ||
, this.g / n | ||
, this.b / n | ||
, this.a); | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
@@ -151,34 +192,54 @@ * | ||
RGBA.prototype.operate = function(op, right){ | ||
right = right.first; | ||
switch (op) { | ||
case 'is a': | ||
if ('string' == right.nodeName && 'color' == right.string) { | ||
return nodes.true; | ||
} | ||
break; | ||
case '+': | ||
return new RGBA( | ||
this.r + right.r | ||
, this.g + right.g | ||
, this.b + right.b | ||
, 1 == right.a ? this.a : (this.a + right.a) | ||
); | ||
switch (right.nodeName) { | ||
case 'unit': | ||
var n = right.val; | ||
switch (right.type) { | ||
case '%': return this.hsla.adjustLightness(n).rgba; | ||
case 'deg': return this.hsla.adjustHue(n).rgba; | ||
default: return this.add(n,n,n,0); | ||
} | ||
case 'rgba': | ||
return this.add(right.r, right.g, right.b, right.a); | ||
case 'hsla': | ||
return this.hsla.add(right.h, right.s, right.l); | ||
} | ||
break; | ||
case '-': | ||
return new RGBA( | ||
this.r - right.r | ||
, this.g - right.g | ||
, this.b - right.b | ||
, 1 == right.a ? this.a : (this.a - right.a) | ||
); | ||
switch (right.nodeName) { | ||
case 'unit': | ||
var n = right.val; | ||
switch (right.type) { | ||
case '%': return this.hsla.adjustLightness(-n).rgba; | ||
case 'deg': return this.hsla.adjustHue(-n).rgba; | ||
default: return this.sub(n,n,n,0); | ||
} | ||
case 'rgba': | ||
return this.sub(right.r, right.g, right.b, right.a); | ||
case 'hsla': | ||
return this.hsla.sub(right.h, right.s, right.l); | ||
} | ||
break; | ||
case '*': | ||
return new RGBA( | ||
this.r * right.r | ||
, this.g * right.g | ||
, this.b * right.b | ||
, this.a * right.a | ||
); | ||
switch (right.nodeName) { | ||
case 'unit': | ||
return this.multiply(right.val); | ||
} | ||
break; | ||
case '/': | ||
return new RGBA( | ||
this.r / right.r | ||
, this.g / right.g | ||
, this.b / right.b | ||
, this.a / right.a | ||
); | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
switch (right.nodeName) { | ||
case 'unit': | ||
return this.divide(right.val); | ||
} | ||
break; | ||
} | ||
return Node.prototype.operate.call(this, op, right); | ||
}; | ||
@@ -185,0 +246,0 @@ |
@@ -13,2 +13,4 @@ | ||
var Node = require('./node') | ||
, sprintf = require('../functions').s | ||
, utils = require('../utils') | ||
, nodes = require('./'); | ||
@@ -79,7 +81,5 @@ | ||
String.prototype.coerce = function(other){ | ||
if (other instanceof String) { | ||
return other; | ||
} else { | ||
return new String(other.toString()); | ||
} | ||
return other instanceof String | ||
? other | ||
: new String(other.toString()); | ||
}; | ||
@@ -98,4 +98,15 @@ | ||
switch (op) { | ||
case '%': | ||
var expr = new nodes.Expression; | ||
expr.push(this); | ||
// constructargs | ||
var args = 'expression' == right.nodeName | ||
? utils.unwrap(right).nodes | ||
: [right]; | ||
// apply | ||
return sprintf.apply(null, [expr].concat(args)); | ||
case '+': | ||
return new String(this.val + right.val); | ||
return new String(this.val + this.coerce(right.first).val); | ||
default: | ||
@@ -102,0 +113,0 @@ return Node.prototype.operate.call(this, op, right); |
@@ -85,28 +85,43 @@ | ||
Unit.prototype.operate = function(op, right){ | ||
switch (op) { | ||
case '-': | ||
return new Unit(this.val - right.val, this.type); | ||
case '+': | ||
return new Unit(this.val + right.val, this.type); | ||
case '/': | ||
return new Unit(this.val / right.val, this.type); | ||
case '*': | ||
return new Unit(this.val * right.val, this.type); | ||
case '%': | ||
return new Unit(this.val % right.val, this.type); | ||
case '**': | ||
return new Unit(Math.pow(this.val, right.val), this.type); | ||
case '..': | ||
case '...': | ||
var start = this.val | ||
, end = right.val | ||
, expr = new nodes.Expression | ||
, inclusive = '..' == op; | ||
do { | ||
expr.push(new nodes.Unit(start)); | ||
} while (inclusive ? ++start <= end : ++start < end); | ||
return expr; | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
// swap color | ||
if ('rgba' == right.nodeName || 'hsla' == right.nodeName) { | ||
return right.operate(op, this); | ||
} | ||
// operate | ||
if (this.shouldCoerce(op)) { | ||
// percentages | ||
if (('-' == op || '+' == op) && '%' == right.type) { | ||
right.val = this.val * (right.val / 100); | ||
} else { | ||
right = this.coerce(right.first); | ||
} | ||
switch (op) { | ||
case '-': | ||
return new Unit(this.val - right.val, this.type); | ||
case '+': | ||
return new Unit(this.val + right.val, this.type); | ||
case '/': | ||
return new Unit(this.val / right.val, this.type); | ||
case '*': | ||
return new Unit(this.val * right.val, this.type); | ||
case '%': | ||
return new Unit(this.val % right.val, this.type); | ||
case '**': | ||
return new Unit(Math.pow(this.val, right.val), this.type); | ||
case '..': | ||
case '...': | ||
var start = this.val | ||
, end = right.val | ||
, expr = new nodes.Expression | ||
, inclusive = '..' == op; | ||
do { | ||
expr.push(new nodes.Unit(start)); | ||
} while (inclusive ? ++start <= end : ++start < end); | ||
return expr; | ||
} | ||
} | ||
return Node.prototype.operate.call(this, op, right); | ||
}; | ||
@@ -180,4 +195,5 @@ | ||
} | ||
default: | ||
return new nodes.Unit(b.val, a.type); | ||
} | ||
return new nodes.Unit(b.val, a.type); | ||
} else if (other instanceof nodes.String) { | ||
@@ -184,0 +200,0 @@ var val = parseInt(other.val, 10); |
@@ -737,2 +737,10 @@ | ||
return this.assignment(); | ||
// Assignment []= | ||
case '[': | ||
if (this._ident == this.peek()) return this.id(); | ||
while (']' != this.lookahead(i++).type) ; | ||
if ('=' == this.lookahead(i).type) { | ||
this._ident = this.peek(); | ||
return this.expression(); | ||
} | ||
// Operation | ||
@@ -755,3 +763,2 @@ case '-': | ||
case '==': | ||
case '[': | ||
case '?': | ||
@@ -1324,3 +1331,3 @@ case 'in': | ||
/** | ||
* primary ('[' expression ']')+ | ||
* primary ('[' expression ']' '='?)+ | ||
* | primary | ||
@@ -1334,2 +1341,7 @@ */ | ||
this.expect(']'); | ||
// TODO: TernaryOp :) | ||
if (this.accept('=')) { | ||
node.op += '='; | ||
node.val = this.expression(); | ||
} | ||
} | ||
@@ -1336,0 +1348,0 @@ return node; |
@@ -49,3 +49,3 @@ | ||
ast = this.evaluator.evaluate(); | ||
new Compiler(ast, this.options).compile(fn); | ||
fn(null, new Compiler(ast, this.options).compile()); | ||
} catch (err) { | ||
@@ -52,0 +52,0 @@ fn(utils.formatException( |
@@ -26,3 +26,3 @@ | ||
exports.version = '0.10.0'; | ||
exports.version = '0.11.0'; | ||
@@ -29,0 +29,0 @@ /** |
@@ -122,4 +122,5 @@ | ||
if (node instanceof nodes.Ident) return; | ||
if (node instanceof nodes.Literal) return; | ||
var actual = node.constructor.name | ||
, msg = 'expected String or Ident, but got ' + actual + ':' + node; | ||
, msg = 'expected String, Ident or Literal, but got ' + actual + ':' + node; | ||
throw new Error('TypeError: ' + msg); | ||
@@ -126,0 +127,0 @@ }; |
@@ -42,12 +42,10 @@ | ||
/** | ||
* Compile to css, and callback `fn(err, css)`. | ||
* Compile to css, and return a string of CSS. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Compiler.prototype.compile = function(fn){ | ||
this.callback = fn; | ||
this.css = this.visit(this.root); | ||
fn(null, this.css); | ||
Compiler.prototype.compile = function(){ | ||
return this.visit(this.root); | ||
}; | ||
@@ -54,0 +52,0 @@ |
@@ -302,3 +302,2 @@ | ||
var op = binop.op | ||
, ident = 'ident' == binop.left.nodeName | ||
, left = this.visit(binop.left) | ||
@@ -308,53 +307,23 @@ , right = this.visit(binop.right); | ||
// Swap <Unit> OP <RGBA|HSLA> | ||
if ('unit' == left.nodeName) { | ||
if ('rgba' == right.nodeName || 'hsla' == right.nodeName) { | ||
var tmp = left; | ||
left = right; | ||
right = tmp; | ||
} | ||
} | ||
// HACK: ternary | ||
var val = binop.val | ||
? this.visit(binop.val) | ||
: null; | ||
// First node in expression | ||
if (!~['[]', 'in'].indexOf(op)) { | ||
left = left.first; | ||
right = right.first; | ||
} | ||
// Coercion | ||
switch (op) { | ||
case '[]': | ||
case 'in': | ||
case '||': | ||
case '&&': | ||
case 'is a': | ||
break; | ||
default: | ||
// Special-case '-' against ident | ||
if ('-' == op | ||
&& 'ident' == left.nodeName | ||
&& 'unit' == right.nodeName) { | ||
var expr = new nodes.Expression; | ||
right.val = -right.val; | ||
expr.push(left); | ||
expr.push(right); | ||
return expr; | ||
} | ||
// Attempt coercion | ||
try { | ||
right = left.coerce(right); | ||
} catch (err) { | ||
// Disgregard coercion issues | ||
// and simply return false | ||
if ('==' == op || '!=' == op) { | ||
// Operate | ||
try { | ||
return this.visit(left.operate(op, right, val)); | ||
} catch (err) { | ||
// disregard coercion issues in equality | ||
// checks, and simply return false | ||
if ('CoercionError' == err.name) { | ||
switch (op) { | ||
case '==': | ||
return nodes.false; | ||
} else { | ||
throw err; | ||
} | ||
case '!=': | ||
return nodes.true; | ||
} | ||
} | ||
throw err; | ||
} | ||
// Operate | ||
return this.visit(left.operate(op, right)); | ||
}; | ||
@@ -438,5 +407,7 @@ | ||
this.return = true; | ||
prop.expr = this.visit(prop.expr); | ||
prop.name = name; | ||
prop.literal = true; | ||
this.property = prop; | ||
prop.expr = this.visit(prop.expr); | ||
delete this.property; | ||
this.return = _; | ||
@@ -465,6 +436,5 @@ return prop; | ||
this.stack.push(new Frame(block)); | ||
for (var i = 0; i < block.nodes.length; ++i) { | ||
block.index = i; | ||
for (block.index = 0; block.index < block.nodes.length; ++block.index) { | ||
try { | ||
block.nodes[i] = this.visit(block.nodes[i]); | ||
block.nodes[block.index] = this.visit(block.nodes[block.index]); | ||
} catch (err) { | ||
@@ -476,3 +446,3 @@ if (err instanceof nodes.Return) { | ||
} else { | ||
block.nodes[i] = err; | ||
block.nodes[block.index] = err; | ||
break; | ||
@@ -624,2 +594,10 @@ } | ||
// current property | ||
if (this.property) { | ||
var prop = this.propertyExpression(this.property, fn.name); | ||
scope.add(new nodes.Ident('current-property', prop)); | ||
} else { | ||
scope.add(new nodes.Ident('current-property', nodes.null)); | ||
} | ||
// inject arguments as locals | ||
@@ -894,2 +872,32 @@ fn.params.nodes.forEach(function(node, i){ | ||
/** | ||
* Return `Expression` based on the given `prop`, | ||
* replacing cyclic calls to the given function `name` | ||
* with "__CALL__". | ||
* | ||
* @param {Property} prop | ||
* @param {String} name | ||
* @return {Expression} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.propertyExpression = function(prop, name){ | ||
var expr = new nodes.Expression | ||
, val = prop.expr.clone(); | ||
// name | ||
expr.push(new nodes.String(prop.name)); | ||
// replace cyclic call with __CALL__ | ||
val.nodes = val.nodes.map(function(node){ | ||
if (node instanceof nodes.Call && node.name == name) { | ||
return new nodes.Literal('__CALL__'); | ||
} | ||
return node; | ||
}); | ||
expr.push(val); | ||
return expr; | ||
}; | ||
/** | ||
* Warn with the given `msg`. | ||
@@ -918,2 +926,24 @@ * | ||
/** | ||
* Return the closest mixin-able `Block`. | ||
* | ||
* @return {Block} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.__defineGetter__('closestBlock', function(){ | ||
var i = this.stack.length | ||
, block; | ||
while (i--) { | ||
block = this.stack[i].block; | ||
if (block.node) { | ||
switch (block.node.nodeName) { | ||
case 'group': | ||
case 'function': | ||
return block; | ||
} | ||
} | ||
} | ||
}); | ||
/** | ||
* Return the current frame `Scope`. | ||
@@ -920,0 +950,0 @@ * |
{ "name": "stylus" | ||
, "description": "Robust, expressive language which compiles to CSS" | ||
, "version": "0.10.0" | ||
, "version": "0.11.0" | ||
, "author": "TJ Holowaychuk <tj@vision-media.ca>" | ||
@@ -5,0 +5,0 @@ , "keywords": ["css", "parser", "style", "stylesheets", "jade", "language"] |
@@ -77,2 +77,3 @@ | ||
- VIM [Syntax](https://github.com/wavded/vim-stylus) | ||
- transparent vendor-specific function expansion | ||
@@ -79,0 +80,0 @@ ### Framework Support |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
202677
7215
120