/*!
* Stylus - Unit
* Copyright(c) 2010 LearnBoost <dev@learnboost.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Node = require('./node')
, nodes = require('./');
/**
* Initialize a new `Unit` with the given `val` and unit `type`
* such as "px", "pt", "in", etc.
*
* @param {String} val
* @param {String} type
* @api public
*/
var Unit = module.exports = function Unit(val, type){
Node.call(this);
this.val = val;
this.type = type;
};
/**
* Inherit from `Node.prototype`.
*/
Unit.prototype.__proto__ = Node.prototype;
/**
* Return Boolean based on the unit value.
*
* @return {Boolean}
* @api public
*/
Unit.prototype.toBoolean = function(){
return nodes.Boolean(this.type
? true
: this.val);
};
/**
* Return unit string.
*
* @return {String}
* @api public
*/
Unit.prototype.toString = function(){
var n = this.val;
if ('px' == this.type) n = n.toFixed(0);
return n + (this.type || '');
};
/**
* Return a clone of this node.
*
* @return {Node}
* @api public
*/
Unit.prototype.clone = function(){
var clone = new Unit(this.val, this.type);
clone.lineno = this.lineno;
clone.filename = this.filename;
return clone;
};
/**
* Operate on `right` with the given `op`.
*
* @param {String} op
* @param {Node} right
* @return {Node}
* @api public
*/
Unit.prototype.operate = function(op, right){
var type = this.type || right.first.type;
// 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, type);
case '+':
return new Unit(this.val + right.val, type);
case '/':
return new Unit(this.val / right.val, type);
case '*':
return new Unit(this.val * right.val, type);
case '%':
return new Unit(this.val % right.val, type);
case '**':
return new Unit(Math.pow(this.val, right.val), 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);
};
/**
* Coerce `other` unit to the same type as `this` unit.
*
* Supports:
*
* mm -> cm | in
* cm -> mm | in
* in -> mm | cm
*
* ms -> s
* s -> ms
*
* Hz -> kHz
* kHz -> Hz
*
* @param {Unit} other
* @return {Unit}
* @api public
*/
Unit.prototype.coerce = function(other){
if ('unit' == other.nodeName) {
var a = this
, b = other;
switch (a.type) {
case 'mm':
switch (b.type) {
case 'cm':
return new nodes.Unit(b.val * 2.54, 'mm');
case 'in':
return new nodes.Unit(b.val * 25.4, 'mm');
}
case 'cm':
switch (b.type) {
case 'mm':
return new nodes.Unit(b.val / 10, 'cm');
case 'in':
return new nodes.Unit(b.val * 2.54, 'cm');
}
case 'in':
switch (b.type) {
case 'mm':
return new nodes.Unit(b.val / 25.4, 'in');
case 'cm':
return new nodes.Unit(b.val / 2.54, 'in');
}
case 'ms':
switch (b.type) {
case 's':
return new nodes.Unit(b.val * 1000, 'ms');
}
case 's':
switch (b.type) {
case 'ms':
return new nodes.Unit(b.val / 1000, 's');
}
case 'Hz':
switch (b.type) {
case 'kHz':
return new nodes.Unit(b.val * 1000, 'Hz');
}
case 'kHz':
switch (b.type) {
case 'Hz':
return new nodes.Unit(b.val / 1000, 'kHz');
}
default:
return new nodes.Unit(b.val, a.type);
}
} else if ('string' == other.nodeName) {
var val = parseInt(other.val, 10);
if (isNaN(val)) Node.prototype.coerce.call(this, other);
return new nodes.Unit(val);
} else {
return Node.prototype.coerce.call(this, other);
}
};