123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- /*!
- * Stylus - Expression
- * Copyright (c) Automattic <developer.wordpress.com>
- * MIT Licensed
- */
- /**
- * Module dependencies.
- */
- var Node = require('./node')
- , nodes = require('../nodes')
- , utils = require('../utils');
- /**
- * Initialize a new `Expression`.
- *
- * @param {Boolean} isList
- * @api public
- */
- var Expression = module.exports = function Expression(isList){
- Node.call(this);
- this.nodes = [];
- this.isList = isList;
- };
- /**
- * Check if the variable has a value.
- *
- * @return {Boolean}
- * @api public
- */
- Expression.prototype.__defineGetter__('isEmpty', function(){
- return !this.nodes.length;
- });
- /**
- * Return the first node in this expression.
- *
- * @return {Node}
- * @api public
- */
- Expression.prototype.__defineGetter__('first', function(){
- return this.nodes[0]
- ? this.nodes[0].first
- : nodes.null;
- });
- /**
- * Hash all the nodes in order.
- *
- * @return {String}
- * @api public
- */
- Expression.prototype.__defineGetter__('hash', function(){
- return this.nodes.map(function(node){
- return node.hash;
- }).join('::');
- });
- /**
- * Inherit from `Node.prototype`.
- */
- Expression.prototype.__proto__ = Node.prototype;
- /**
- * Return a clone of this node.
- *
- * @return {Node}
- * @api public
- */
- Expression.prototype.clone = function(parent){
- var clone = new this.constructor(this.isList);
- clone.preserve = this.preserve;
- clone.lineno = this.lineno;
- clone.column = this.column;
- clone.filename = this.filename;
- clone.nodes = this.nodes.map(function(node) {
- return node.clone(parent, clone);
- });
- return clone;
- };
- /**
- * Push the given `node`.
- *
- * @param {Node} node
- * @api public
- */
- Expression.prototype.push = function(node){
- this.nodes.push(node);
- };
- /**
- * Operate on `right` with the given `op`.
- *
- * @param {String} op
- * @param {Node} right
- * @return {Node}
- * @api public
- */
- Expression.prototype.operate = function(op, right, val){
- switch (op) {
- case '[]=':
- var self = this
- , range = utils.unwrap(right).nodes
- , val = utils.unwrap(val)
- , len
- , node;
- range.forEach(function(unit){
- len = self.nodes.length;
- if ('unit' == unit.nodeName) {
- var i = unit.val < 0 ? len + unit.val : unit.val
- , n = i;
- while (i-- > len) self.nodes[i] = nodes.null;
- self.nodes[n] = val;
- } else if (unit.string) {
- node = self.nodes[0];
- if (node && 'object' == node.nodeName) node.set(unit.string, val.clone());
- }
- });
- return val;
- case '[]':
- var expr = new nodes.Expression
- , vals = utils.unwrap(this).nodes
- , range = utils.unwrap(right).nodes
- , node;
- range.forEach(function(unit){
- if ('unit' == unit.nodeName) {
- node = vals[unit.val < 0 ? vals.length + unit.val : unit.val];
- } else if ('object' == vals[0].nodeName) {
- node = vals[0].get(unit.string);
- }
- if (node) expr.push(node);
- });
- return expr.isEmpty
- ? nodes.null
- : utils.unwrap(expr);
- case '||':
- return this.toBoolean().isTrue
- ? this
- : right;
- case 'in':
- return Node.prototype.operate.call(this, op, right);
- case '!=':
- return this.operate('==', right, val).negate();
- case '==':
- var len = this.nodes.length
- , right = right.toExpression()
- , a
- , b;
- if (len != right.nodes.length) return nodes.false;
- for (var i = 0; i < len; ++i) {
- a = this.nodes[i];
- b = right.nodes[i];
- if (a.operate(op, b).isTrue) continue;
- return nodes.false;
- }
- return nodes.true;
- break;
- default:
- return this.first.operate(op, right, val);
- }
- };
- /**
- * Expressions with length > 1 are truthy,
- * otherwise the first value's toBoolean()
- * method is invoked.
- *
- * @return {Boolean}
- * @api public
- */
- Expression.prototype.toBoolean = function(){
- if (this.nodes.length > 1) return nodes.true;
- return this.first.toBoolean();
- };
- /**
- * Return "<a> <b> <c>" or "<a>, <b>, <c>" if
- * the expression represents a list.
- *
- * @return {String}
- * @api public
- */
- Expression.prototype.toString = function(){
- return '(' + this.nodes.map(function(node){
- return node.toString();
- }).join(this.isList ? ', ' : ' ') + ')';
- };
- /**
- * Return a JSON representation of this node.
- *
- * @return {Object}
- * @api public
- */
- Expression.prototype.toJSON = function(){
- return {
- __type: 'Expression',
- isList: this.isList,
- preserve: this.preserve,
- lineno: this.lineno,
- column: this.column,
- filename: this.filename,
- nodes: this.nodes
- };
- };
|