stylus
Advanced tools
Comparing version 0.59.0 to 0.60.0
@@ -11,71 +11,75 @@ /** | ||
var FSCache = module.exports = function(options) { | ||
options = options || {}; | ||
this._location = options['cache location'] || '.styl-cache'; | ||
if (!fs.existsSync(this._location)) fs.mkdirSync(this._location); | ||
}; | ||
module.exports = class FSCache { | ||
constructor(options) { | ||
options = options || {}; | ||
this._location = options['cache location'] || '.styl-cache'; | ||
if (!fs.existsSync(this._location)) fs.mkdirSync(this._location); | ||
} | ||
/** | ||
* Set cache item with given `key` to `value`. | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api private | ||
*/ | ||
FSCache.prototype.set = function(key, value) { | ||
fs.writeFileSync(join(this._location, key), JSON.stringify(value)); | ||
}; | ||
/** | ||
* Set cache item with given `key` to `value`. | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api private | ||
*/ | ||
/** | ||
* Get cache item with given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
set(key, value) { | ||
fs.writeFileSync(join(this._location, key), JSON.stringify(value)); | ||
}; | ||
FSCache.prototype.get = function(key) { | ||
var data = fs.readFileSync(join(this._location, key), 'utf-8'); | ||
return JSON.parse(data, FSCache.fromJSON); | ||
}; | ||
/** | ||
* Get cache item with given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
/** | ||
* Check if cache has given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
get(key) { | ||
var data = fs.readFileSync(join(this._location, key), 'utf-8'); | ||
return JSON.parse(data, FSCache.fromJSON); | ||
}; | ||
FSCache.prototype.has = function(key) { | ||
return fs.existsSync(join(this._location, key)); | ||
}; | ||
/** | ||
* Check if cache has given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
/** | ||
* Generate key for the source `str` with `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @return {String} | ||
* @api private | ||
*/ | ||
has(key) { | ||
return fs.existsSync(join(this._location, key)); | ||
}; | ||
FSCache.prototype.key = function(str, options) { | ||
var hash = crypto.createHash('sha1'); | ||
hash.update(str + version + options.prefix); | ||
return hash.digest('hex'); | ||
}; | ||
/** | ||
* Generate key for the source `str` with `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* JSON to Stylus nodes converter. | ||
* | ||
* @api private | ||
*/ | ||
key(str, options) { | ||
var hash = crypto.createHash('sha1'); | ||
hash.update(str + version + options.prefix); | ||
return hash.digest('hex'); | ||
}; | ||
FSCache.fromJSON = function(key, val) { | ||
if (val && val.__type) { | ||
val.__proto__ = nodes[val.__type].prototype; | ||
} | ||
return val; | ||
/** | ||
* JSON to Stylus nodes converter. | ||
* | ||
* @api private | ||
*/ | ||
static fromJSON(key, val) { | ||
if (val && val.__type) { | ||
Object.setPrototypeOf(val, nodes[val.__type].prototype); | ||
} | ||
return val; | ||
}; | ||
}; |
@@ -8,110 +8,112 @@ /** | ||
var MemoryCache = module.exports = function(options) { | ||
options = options || {}; | ||
this.limit = options['cache limit'] || 256; | ||
this._cache = {}; | ||
this.length = 0; | ||
this.head = this.tail = null; | ||
}; | ||
module.exports = class MemoryCache { | ||
constructor(options) { | ||
options = options || {}; | ||
this.limit = options['cache limit'] || 256; | ||
this._cache = {}; | ||
this.length = 0; | ||
this.head = this.tail = null; | ||
} | ||
/** | ||
* Set cache item with given `key` to `value`. | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api private | ||
*/ | ||
/** | ||
* Set cache item with given `key` to `value`. | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api private | ||
*/ | ||
MemoryCache.prototype.set = function(key, value) { | ||
var clone = value.clone() | ||
, item; | ||
set(key, value) { | ||
var clone = value.clone() | ||
, item; | ||
clone.filename = nodes.filename; | ||
clone.lineno = nodes.lineno; | ||
clone.column = nodes.column; | ||
item = { key: key, value: clone }; | ||
this._cache[key] = item; | ||
clone.filename = nodes.filename; | ||
clone.lineno = nodes.lineno; | ||
clone.column = nodes.column; | ||
item = { key: key, value: clone }; | ||
this._cache[key] = item; | ||
if (this.tail) { | ||
this.tail.next = item; | ||
item.prev = this.tail; | ||
} else { | ||
this.head = item; | ||
} | ||
if (this.tail) { | ||
this.tail.next = item; | ||
item.prev = this.tail; | ||
} else { | ||
this.head = item; | ||
} | ||
this.tail = item; | ||
if (this.length++ == this.limit) this.purge(); | ||
}; | ||
this.tail = item; | ||
if (this.length++ == this.limit) this.purge(); | ||
}; | ||
/** | ||
* Get cache item with given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
/** | ||
* Get cache item with given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
MemoryCache.prototype.get = function(key) { | ||
var item = this._cache[key] | ||
, val = item.value.clone(); | ||
get(key) { | ||
var item = this._cache[key] | ||
, val = item.value.clone(); | ||
if (item == this.tail) return val; | ||
if (item.next) { | ||
if (item == this.head) this.head = item.next; | ||
item.next.prev = item.prev; | ||
} | ||
if (item.prev) item.prev.next = item.next; | ||
if (item == this.tail) return val; | ||
if (item.next) { | ||
if (item == this.head) this.head = item.next; | ||
item.next.prev = item.prev; | ||
} | ||
if (item.prev) item.prev.next = item.next; | ||
item.next = null; | ||
item.prev = this.tail; | ||
item.next = null; | ||
item.prev = this.tail; | ||
if (this.tail) this.tail.next = item; | ||
this.tail = item; | ||
if (this.tail) this.tail.next = item; | ||
this.tail = item; | ||
return val; | ||
}; | ||
return val; | ||
}; | ||
/** | ||
* Check if cache has given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
/** | ||
* Check if cache has given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
MemoryCache.prototype.has = function(key) { | ||
return !!this._cache[key]; | ||
}; | ||
has(key) { | ||
return !!this._cache[key]; | ||
}; | ||
/** | ||
* Generate key for the source `str` with `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Generate key for the source `str` with `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @return {String} | ||
* @api private | ||
*/ | ||
MemoryCache.prototype.key = function(str, options) { | ||
var hash = crypto.createHash('sha1'); | ||
hash.update(str + options.prefix); | ||
return hash.digest('hex'); | ||
}; | ||
key(str, options) { | ||
var hash = crypto.createHash('sha1'); | ||
hash.update(str + options.prefix); | ||
return hash.digest('hex'); | ||
}; | ||
/** | ||
* Remove the oldest item from the cache. | ||
* | ||
* @api private | ||
*/ | ||
/** | ||
* Remove the oldest item from the cache. | ||
* | ||
* @api private | ||
*/ | ||
MemoryCache.prototype.purge = function() { | ||
var item = this.head; | ||
purge() { | ||
var item = this.head; | ||
if (this.head.next) { | ||
this.head = this.head.next; | ||
this.head.prev = null; | ||
} | ||
if (this.head.next) { | ||
this.head = this.head.next; | ||
this.head.prev = null; | ||
} | ||
this._cache[item.key] = item.prev = item.next = null; | ||
this.length--; | ||
this._cache[item.key] = item.prev = item.next = null; | ||
this.length--; | ||
}; | ||
}; |
@@ -5,47 +5,48 @@ /** | ||
var NullCache = module.exports = function() {}; | ||
module.exports = class NullCache { | ||
/** | ||
* Set cache item with given `key` to `value`. | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api private | ||
*/ | ||
/** | ||
* Set cache item with given `key` to `value`. | ||
* | ||
* @param {String} key | ||
* @param {Object} value | ||
* @api private | ||
*/ | ||
NullCache.prototype.set = function(key, value) {}; | ||
set(key, value) { }; | ||
/** | ||
* Get cache item with given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
/** | ||
* Get cache item with given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
NullCache.prototype.get = function(key) {}; | ||
get(key) { }; | ||
/** | ||
* Check if cache has given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
/** | ||
* Check if cache has given `key`. | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
NullCache.prototype.has = function(key) { | ||
return false; | ||
}; | ||
has(key) { | ||
return false; | ||
}; | ||
/** | ||
* Generate key for the source `str` with `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Generate key for the source `str` with `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @return {String} | ||
* @api private | ||
*/ | ||
NullCache.prototype.key = function(str, options) { | ||
return ''; | ||
}; | ||
key(str, options) { | ||
return ''; | ||
}; | ||
} |
@@ -15,3 +15,3 @@ /*! | ||
module.exports = function(css){ | ||
module.exports = function (css) { | ||
return new Converter(css).stylus(); | ||
@@ -27,304 +27,307 @@ }; | ||
function Converter(css) { | ||
var { parse } = require('@adobe/css-tools'); | ||
this.css = css; | ||
this.root = parse(css, { position: false }); | ||
this.indents = 0; | ||
} | ||
class Converter { | ||
constructor(css) { | ||
var { parse } = require('@adobe/css-tools'); | ||
this.css = css; | ||
this.root = parse(css, { position: false }); | ||
this.indents = 0; | ||
} | ||
/** | ||
* Convert to Stylus. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.stylus = function(){ | ||
return this.visitRules(this.root.stylesheet.rules); | ||
}; | ||
/** | ||
* Convert to Stylus. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Return indent string. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
stylus() { | ||
return this.visitRules(this.root.stylesheet.rules); | ||
}; | ||
Converter.prototype.__defineGetter__('indent', function(){ | ||
return Array(this.indents + 1).join(' '); | ||
}); | ||
/** | ||
* Return indent string. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit `node`. | ||
* | ||
* @param {*} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
get indent() { | ||
return Array(this.indents + 1).join(' '); | ||
}; | ||
Converter.prototype.visit = function(node){ | ||
switch (node.type) { | ||
case 'rule': | ||
case 'comment': | ||
case 'charset': | ||
case 'namespace': | ||
case 'media': | ||
case 'import': | ||
case 'document': | ||
case 'keyframes': | ||
case 'page': | ||
case 'host': | ||
case 'supports': | ||
var name = node.type[0].toUpperCase() + node.type.slice(1); | ||
return this['visit' + name](node); | ||
case 'font-face': | ||
return this.visitFontFace(node); | ||
} | ||
}; | ||
/** | ||
* Visit `node`. | ||
* | ||
* @param {*} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit the rules on `node`. | ||
* | ||
* @param {Array} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
visit(node) { | ||
switch (node.type) { | ||
case 'rule': | ||
case 'comment': | ||
case 'charset': | ||
case 'namespace': | ||
case 'media': | ||
case 'import': | ||
case 'document': | ||
case 'keyframes': | ||
case 'page': | ||
case 'host': | ||
case 'supports': | ||
var name = node.type[0].toUpperCase() + node.type.slice(1); | ||
return this['visit' + name](node); | ||
case 'font-face': | ||
return this.visitFontFace(node); | ||
} | ||
}; | ||
Converter.prototype.visitRules = function(node){ | ||
var buf = ''; | ||
for (var i = 0, len = node.length; i < len; ++i) { | ||
buf += this.visit(node[i]); | ||
} | ||
return buf; | ||
}; | ||
/** | ||
* Visit the rules on `node`. | ||
* | ||
* @param {Array} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit FontFace `node`. | ||
* | ||
* @param {FontFace} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
visitRules(node) { | ||
var buf = ''; | ||
for (var i = 0, len = node.length; i < len; ++i) { | ||
buf += this.visit(node[i]); | ||
} | ||
return buf; | ||
}; | ||
Converter.prototype.visitFontFace = function(node){ | ||
var buf = this.indent + '@font-face'; | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit FontFace `node`. | ||
* | ||
* @param {FontFace} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Media `node`. | ||
* | ||
* @param {Media} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
visitFontFace(node) { | ||
var buf = this.indent + '@font-face'; | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
Converter.prototype.visitMedia = function(node){ | ||
var buf = this.indent + '@media ' + node.media; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit Media `node`. | ||
* | ||
* @param {Media} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Declaration `node`. | ||
* | ||
* @param {Declaration} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitDeclaration = function(node){ | ||
if ('comment' == node.type) { | ||
return this.visitComment(node); | ||
} else { | ||
var buf = this.indent + node.property + ': ' + node.value + '\n'; | ||
visitMedia(node) { | ||
var buf = this.indent + '@media ' + node.media; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
} | ||
}; | ||
}; | ||
/** | ||
* Visit Rule `node`.` | ||
* | ||
* @param {Rule} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Declaration `node`. | ||
* | ||
* @param {Declaration} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitRule = function(node){ | ||
var buf = this.indent + node.selectors.join(',\n' + this.indent) + '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf + '\n'; | ||
}; | ||
visitDeclaration(node) { | ||
if ('comment' == node.type) { | ||
return this.visitComment(node); | ||
} else { | ||
var buf = this.indent + node.property + ': ' + node.value + '\n'; | ||
return buf; | ||
} | ||
}; | ||
/** | ||
* Visit Comment `node`.` | ||
* | ||
* @param {Comment} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Rule `node`.` | ||
* | ||
* @param {Rule} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitComment = function(node){ | ||
var buf = this.indent + '/*' + node.comment + '*/'; | ||
return buf + '\n'; | ||
}; | ||
visitRule(node) { | ||
var buf = this.indent + node.selectors.join(',\n' + this.indent) + '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf + '\n'; | ||
}; | ||
/** | ||
* Visit Charset `node`.` | ||
* | ||
* @param {Charset} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Comment `node`.` | ||
* | ||
* @param {Comment} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitCharset = function(node){ | ||
var buf = this.indent + '@charset ' + node.charset; | ||
return buf + '\n'; | ||
}; | ||
visitComment(node) { | ||
var buf = this.indent + '/*' + node.comment + '*/'; | ||
return buf + '\n'; | ||
}; | ||
/** | ||
* Visit Namespace `node`.` | ||
* | ||
* @param {Namespace} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Charset `node`.` | ||
* | ||
* @param {Charset} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitNamespace = function(node){ | ||
var buf = this.indent + '@namespace ' + node.namespace; | ||
return buf + '\n'; | ||
}; | ||
visitCharset(node) { | ||
var buf = this.indent + '@charset ' + node.charset; | ||
return buf + '\n'; | ||
}; | ||
/** | ||
* Visit Import `node`.` | ||
* | ||
* @param {Import} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Namespace `node`.` | ||
* | ||
* @param {Namespace} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitImport = function(node){ | ||
var buf = this.indent + '@import ' + node.import; | ||
return buf + '\n'; | ||
}; | ||
visitNamespace(node) { | ||
var buf = this.indent + '@namespace ' + node.namespace; | ||
return buf + '\n'; | ||
}; | ||
/** | ||
* Visit Document `node`.` | ||
* | ||
* @param {Document} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Import `node`.` | ||
* | ||
* @param {Import} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitDocument = function(node){ | ||
var buf = this.indent + '@' + node.vendor + 'document ' + node.document; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
visitImport(node) { | ||
var buf = this.indent + '@import ' + node.import; | ||
return buf + '\n'; | ||
}; | ||
/** | ||
* Visit Keyframes `node`.` | ||
* | ||
* @param {Keyframes} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Document `node`.` | ||
* | ||
* @param {Document} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitKeyframes = function(node){ | ||
var buf = this.indent + '@keyframes ' + node.name; | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.keyframes.length; i < len; ++i) { | ||
buf += this.visitKeyframe(node.keyframes[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
visitDocument(node) { | ||
var buf = this.indent + '@' + node.vendor + 'document ' + node.document; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit Keyframe `node`.` | ||
* | ||
* @param {Keyframe} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Keyframes `node`.` | ||
* | ||
* @param {Keyframes} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitKeyframe = function(node){ | ||
var buf = this.indent + node.values.join(', '); | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
visitKeyframes(node) { | ||
var buf = this.indent + '@keyframes ' + node.name; | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.keyframes.length; i < len; ++i) { | ||
buf += this.visitKeyframe(node.keyframes[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit Page `node`.` | ||
* | ||
* @param {Page} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Keyframe `node`.` | ||
* | ||
* @param {Keyframe} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitPage = function(node){ | ||
var buf = this.indent + '@page' + (node.selectors.length ? ' ' + node.selectors.join(', ') : ''); | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
visitKeyframe(node) { | ||
var buf = this.indent + node.values.join(', '); | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit Supports `node`.` | ||
* | ||
* @param {Supports} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Page `node`.` | ||
* | ||
* @param {Page} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitSupports = function(node){ | ||
var buf = this.indent + '@supports ' + node.supports; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
visitPage(node) { | ||
var buf = this.indent + '@page' + (node.selectors.length ? ' ' + node.selectors.join(', ') : ''); | ||
buf += '\n'; | ||
++this.indents; | ||
for (var i = 0, len = node.declarations.length; i < len; ++i) { | ||
buf += this.visitDeclaration(node.declarations[i]); | ||
} | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit Host `node`.` | ||
* | ||
* @param {Host} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Supports `node`.` | ||
* | ||
* @param {Supports} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Converter.prototype.visitHost = function(node){ | ||
var buf = this.indent + '@host'; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
visitSupports(node) { | ||
var buf = this.indent + '@supports ' + node.supports; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
/** | ||
* Visit Host `node`.` | ||
* | ||
* @param {Host} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
visitHost(node) { | ||
var buf = this.indent + '@host'; | ||
buf += '\n'; | ||
++this.indents; | ||
buf += this.visitRules(node.rules); | ||
--this.indents; | ||
return buf; | ||
}; | ||
} |
@@ -9,9 +9,2 @@ | ||
/** | ||
* Expose constructors. | ||
*/ | ||
exports.ParseError = ParseError; | ||
exports.SyntaxError = SyntaxError; | ||
/** | ||
* Initialize a new `ParseError` with the given `msg`. | ||
@@ -23,7 +16,10 @@ * | ||
function ParseError(msg) { | ||
this.name = 'ParseError'; | ||
this.message = msg; | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, ParseError); | ||
class ParseError extends Error { | ||
constructor(msg) { | ||
super(); | ||
this.name = 'ParseError'; | ||
this.message = msg; | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, ParseError); | ||
} | ||
} | ||
@@ -33,8 +29,2 @@ } | ||
/** | ||
* Inherit from `Error.prototype`. | ||
*/ | ||
ParseError.prototype.__proto__ = Error.prototype; | ||
/** | ||
* Initialize a new `SyntaxError` with the given `msg`. | ||
@@ -46,7 +36,10 @@ * | ||
function SyntaxError(msg) { | ||
this.name = 'SyntaxError'; | ||
this.message = msg; | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, ParseError); | ||
class SyntaxError extends Error { | ||
constructor(msg) { | ||
super(); | ||
this.name = 'SyntaxError'; | ||
this.message = msg; | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, ParseError); | ||
} | ||
} | ||
@@ -56,5 +49,6 @@ } | ||
/** | ||
* Inherit from `Error.prototype`. | ||
* Expose constructors. | ||
*/ | ||
SyntaxError.prototype.__proto__ = Error.prototype; | ||
exports.ParseError = ParseError; | ||
exports.SyntaxError = SyntaxError; |
@@ -19,145 +19,148 @@ | ||
/** | ||
* Initialize a new `Image` with the given `ctx` and `path. | ||
* | ||
* @param {Evaluator} ctx | ||
* @param {String} path | ||
* @api private | ||
*/ | ||
module.exports = class Image { | ||
/** | ||
* Initialize a new `Image` with the given `ctx` and `path. | ||
* | ||
* @param {Evaluator} ctx | ||
* @param {String} path | ||
* @api private | ||
*/ | ||
var Image = module.exports = function Image(ctx, path) { | ||
this.ctx = ctx; | ||
this.path = utils.lookup(path, ctx.paths); | ||
if (!this.path) throw new Error('failed to locate file ' + path); | ||
}; | ||
constructor(ctx, path) { | ||
this.ctx = ctx; | ||
this.path = utils.lookup(path, ctx.paths); | ||
if (!this.path) throw new Error('failed to locate file ' + path); | ||
} | ||
/** | ||
* Open the image for reading. | ||
* | ||
* @api private | ||
*/ | ||
Image.prototype.open = function(){ | ||
this.fd = fs.openSync(this.path, 'r'); | ||
this.length = fs.fstatSync(this.fd).size; | ||
this.extname = path.extname(this.path).slice(1); | ||
}; | ||
/** | ||
* Open the image for reading. | ||
* | ||
* @api private | ||
*/ | ||
/** | ||
* Close the file. | ||
* | ||
* @api private | ||
*/ | ||
open() { | ||
this.fd = fs.openSync(this.path, 'r'); | ||
this.length = fs.fstatSync(this.fd).size; | ||
this.extname = path.extname(this.path).slice(1); | ||
}; | ||
Image.prototype.close = function(){ | ||
if (this.fd) fs.closeSync(this.fd); | ||
}; | ||
/** | ||
* Close the file. | ||
* | ||
* @api private | ||
*/ | ||
/** | ||
* Return the type of image, supports: | ||
* | ||
* - gif | ||
* - png | ||
* - jpeg | ||
* - svg | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
close() { | ||
if (this.fd) fs.closeSync(this.fd); | ||
}; | ||
Image.prototype.type = function(){ | ||
var type | ||
, buf = Buffer.alloc(4); | ||
fs.readSync(this.fd, buf, 0, 4, 0); | ||
/** | ||
* Return the type of image, supports: | ||
* | ||
* - gif | ||
* - png | ||
* - jpeg | ||
* - svg | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
// GIF | ||
if (0x47 == buf[0] && 0x49 == buf[1] && 0x46 == buf[2]) type = 'gif'; | ||
type() { | ||
var type | ||
, buf = Buffer.alloc(4); | ||
// PNG | ||
else if (0x50 == buf[1] && 0x4E == buf[2] && 0x47 == buf[3]) type = 'png'; | ||
fs.readSync(this.fd, buf, 0, 4, 0); | ||
// JPEG | ||
else if (0xff == buf[0] && 0xd8 == buf[1]) type = 'jpeg'; | ||
// GIF | ||
if (0x47 == buf[0] && 0x49 == buf[1] && 0x46 == buf[2]) type = 'gif'; | ||
// SVG | ||
else if ('svg' == this.extname) type = this.extname; | ||
// PNG | ||
else if (0x50 == buf[1] && 0x4E == buf[2] && 0x47 == buf[3]) type = 'png'; | ||
return type; | ||
}; | ||
// JPEG | ||
else if (0xff == buf[0] && 0xd8 == buf[1]) type = 'jpeg'; | ||
/** | ||
* Return image dimensions `[width, height]`. | ||
* | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
// SVG | ||
else if ('svg' == this.extname) type = this.extname; | ||
Image.prototype.size = function(){ | ||
var type = this.type() | ||
, width | ||
, height | ||
, buf | ||
, offset | ||
, blockSize | ||
, parser; | ||
return type; | ||
}; | ||
function uint16(b) { return b[1] << 8 | b[0]; } | ||
function uint32(b) { return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; } | ||
/** | ||
* Return image dimensions `[width, height]`. | ||
* | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
// Determine dimensions | ||
switch (type) { | ||
case 'jpeg': | ||
buf = Buffer.alloc(this.length); | ||
fs.readSync(this.fd, buf, 0, this.length, 0); | ||
offset = 4; | ||
blockSize = buf[offset] << 8 | buf[offset + 1]; | ||
size() { | ||
var type = this.type() | ||
, width | ||
, height | ||
, buf | ||
, offset | ||
, blockSize | ||
, parser; | ||
while (offset < this.length) { | ||
offset += blockSize; | ||
if (offset >= this.length || 0xff != buf[offset]) break; | ||
// SOF0 or SOF2 (progressive) | ||
if (0xc0 == buf[offset + 1] || 0xc2 == buf[offset + 1]) { | ||
height = buf[offset + 5] << 8 | buf[offset + 6]; | ||
width = buf[offset + 7] << 8 | buf[offset + 8]; | ||
} else { | ||
offset += 2; | ||
blockSize = buf[offset] << 8 | buf[offset + 1]; | ||
function uint16(b) { return b[1] << 8 | b[0]; } | ||
function uint32(b) { return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; } | ||
// Determine dimensions | ||
switch (type) { | ||
case 'jpeg': | ||
buf = Buffer.alloc(this.length); | ||
fs.readSync(this.fd, buf, 0, this.length, 0); | ||
offset = 4; | ||
blockSize = buf[offset] << 8 | buf[offset + 1]; | ||
while (offset < this.length) { | ||
offset += blockSize; | ||
if (offset >= this.length || 0xff != buf[offset]) break; | ||
// SOF0 or SOF2 (progressive) | ||
if (0xc0 == buf[offset + 1] || 0xc2 == buf[offset + 1]) { | ||
height = buf[offset + 5] << 8 | buf[offset + 6]; | ||
width = buf[offset + 7] << 8 | buf[offset + 8]; | ||
} else { | ||
offset += 2; | ||
blockSize = buf[offset] << 8 | buf[offset + 1]; | ||
} | ||
} | ||
} | ||
break; | ||
case 'png': | ||
buf = Buffer.alloc(8); | ||
// IHDR chunk width / height uint32_t big-endian | ||
fs.readSync(this.fd, buf, 0, 8, 16); | ||
width = uint32(buf); | ||
height = uint32(buf.slice(4, 8)); | ||
break; | ||
case 'gif': | ||
buf = Buffer.alloc(4); | ||
// width / height uint16_t little-endian | ||
fs.readSync(this.fd, buf, 0, 4, 6); | ||
width = uint16(buf); | ||
height = uint16(buf.slice(2, 4)); | ||
break; | ||
case 'svg': | ||
offset = Math.min(this.length, 1024); | ||
buf = Buffer.alloc(offset); | ||
fs.readSync(this.fd, buf, 0, offset, 0); | ||
buf = buf.toString('utf8'); | ||
parser = sax.parser(true); | ||
parser.onopentag = function(node) { | ||
if ('svg' == node.name && node.attributes.width && node.attributes.height) { | ||
width = parseInt(node.attributes.width, 10); | ||
height = parseInt(node.attributes.height, 10); | ||
} | ||
}; | ||
parser.write(buf).close(); | ||
break; | ||
} | ||
break; | ||
case 'png': | ||
buf = Buffer.alloc(8); | ||
// IHDR chunk width / height uint32_t big-endian | ||
fs.readSync(this.fd, buf, 0, 8, 16); | ||
width = uint32(buf); | ||
height = uint32(buf.slice(4, 8)); | ||
break; | ||
case 'gif': | ||
buf = Buffer.alloc(4); | ||
// width / height uint16_t little-endian | ||
fs.readSync(this.fd, buf, 0, 4, 6); | ||
width = uint16(buf); | ||
height = uint16(buf.slice(2, 4)); | ||
break; | ||
case 'svg': | ||
offset = Math.min(this.length, 1024); | ||
buf = Buffer.alloc(offset); | ||
fs.readSync(this.fd, buf, 0, offset, 0); | ||
buf = buf.toString('utf8'); | ||
parser = sax.parser(true); | ||
parser.onopentag = function (node) { | ||
if ('svg' == node.name && node.attributes.width && node.attributes.height) { | ||
width = parseInt(node.attributes.width, 10); | ||
height = parseInt(node.attributes.height, 10); | ||
} | ||
}; | ||
parser.write(buf).close(); | ||
break; | ||
} | ||
if ('number' != typeof width) throw new Error('failed to find width of "' + this.path + '"'); | ||
if ('number' != typeof height) throw new Error('failed to find height of "' + this.path + '"'); | ||
if ('number' != typeof width) throw new Error('failed to find width of "' + this.path + '"'); | ||
if ('number' != typeof height) throw new Error('failed to find height of "' + this.path + '"'); | ||
return [width, height]; | ||
return [width, height]; | ||
}; | ||
}; |
349
lib/lexer.js
@@ -17,8 +17,2 @@ | ||
/** | ||
* Expose `Lexer`. | ||
*/ | ||
exports = module.exports = Lexer; | ||
/** | ||
* Operator aliases. | ||
@@ -28,3 +22,3 @@ */ | ||
var alias = { | ||
'and': '&&' | ||
'and': '&&' | ||
, 'or': '||' | ||
@@ -37,66 +31,61 @@ , 'is': '==' | ||
/** | ||
* Initialize a new `Lexer` with the given `str` and `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @api private | ||
*/ | ||
exports = module.exports = class Lexer { | ||
/** | ||
* Initialize a new `Lexer` with the given `str` and `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @api private | ||
*/ | ||
function Lexer(str, options) { | ||
options = options || {}; | ||
this.stash = []; | ||
this.indentStack = []; | ||
this.indentRe = null; | ||
this.lineno = 1; | ||
this.column = 1; | ||
constructor(str, options) { | ||
options = options || {}; | ||
this.stash = []; | ||
this.indentStack = []; | ||
this.indentRe = null; | ||
this.lineno = 1; | ||
this.column = 1; | ||
// HACK! | ||
function comment(str, val, offset, s) { | ||
var inComment = s.lastIndexOf('/*', offset) > s.lastIndexOf('*/', offset) | ||
, commentIdx = s.lastIndexOf('//', offset) | ||
, i = s.lastIndexOf('\n', offset) | ||
, double = 0 | ||
, single = 0; | ||
// HACK! | ||
function comment(str, val, offset, s) { | ||
var inComment = s.lastIndexOf('/*', offset) > s.lastIndexOf('*/', offset) | ||
, commentIdx = s.lastIndexOf('//', offset) | ||
, i = s.lastIndexOf('\n', offset) | ||
, double = 0 | ||
, single = 0; | ||
if (~commentIdx && commentIdx > i) { | ||
while (i != offset) { | ||
if ("'" == s[i]) single ? single-- : single++; | ||
if ('"' == s[i]) double ? double-- : double++; | ||
if (~commentIdx && commentIdx > i) { | ||
while (i != offset) { | ||
if ("'" == s[i]) single ? single-- : single++; | ||
if ('"' == s[i]) double ? double-- : double++; | ||
if ('/' == s[i] && '/' == s[i + 1]) { | ||
inComment = !single && !double; | ||
break; | ||
if ('/' == s[i] && '/' == s[i + 1]) { | ||
inComment = !single && !double; | ||
break; | ||
} | ||
++i; | ||
} | ||
++i; | ||
} | ||
} | ||
return inComment | ||
? str | ||
: ((val === ',' && /^[,\t\n]+$/.test(str)) ? str.replace(/\n/, '\r') : val + '\r'); | ||
}; | ||
return inComment | ||
? str | ||
: ((val === ',' && /^[,\t\n]+$/.test(str)) ? str.replace(/\n/, '\r') : val + '\r'); | ||
}; | ||
// Remove UTF-8 BOM. | ||
if ('\uFEFF' == str.charAt(0)) str = str.slice(1); | ||
// Remove UTF-8 BOM. | ||
if ('\uFEFF' == str.charAt(0)) str = str.slice(1); | ||
this.str = str | ||
.replace(/\s+$/, '\n') | ||
.replace(/\r\n?/g, '\n') | ||
.replace(/\\ *\n/g, '\r') | ||
.replace(/([,(:](?!\/\/[^ ])) *(?:\/\/[^\n]*|\/\*.*?\*\/)?\n\s*/g, comment) | ||
.replace(/\s*\n[ \t]*([,)])/g, comment); | ||
}; | ||
this.str = str | ||
.replace(/\s+$/, '\n') | ||
.replace(/\r\n?/g, '\n') | ||
.replace(/\\ *\n/g, '\r') | ||
.replace(/([,(:](?!\/\/[^ ])) *(?:\/\/[^\n]*|\/\*.*?\*\/)?\n\s*/g, comment) | ||
.replace(/\s*\n[ \t]*([,)])/g, comment); | ||
}; | ||
/** | ||
* Lexer prototype. | ||
*/ | ||
Lexer.prototype = { | ||
/** | ||
* Custom inspect. | ||
*/ | ||
* Custom inspect. | ||
*/ | ||
inspect: function(){ | ||
inspect() { | ||
var tok | ||
@@ -110,3 +99,3 @@ , tmp = this.str | ||
return buf.concat(tok.inspect()).join('\n'); | ||
}, | ||
} | ||
@@ -121,7 +110,7 @@ /** | ||
lookahead: function(n){ | ||
lookahead(n) { | ||
var fetch = n - this.stash.length; | ||
while (fetch-- > 0) this.stash.push(this.advance()); | ||
return this.stash[--n]; | ||
}, | ||
} | ||
@@ -135,3 +124,3 @@ /** | ||
skip: function(len){ | ||
skip(len) { | ||
var chunk = len[0]; | ||
@@ -145,3 +134,3 @@ len = chunk ? chunk.length : len; | ||
} | ||
}, | ||
} | ||
@@ -155,3 +144,3 @@ /** | ||
move: function(str){ | ||
move(str) { | ||
var lines = str.match(/\n/g) | ||
@@ -164,3 +153,3 @@ , idx = str.lastIndexOf('\n'); | ||
: this.column + str.length; | ||
}, | ||
} | ||
@@ -174,7 +163,7 @@ /** | ||
next: function() { | ||
next() { | ||
var tok = this.stashed() || this.advance(); | ||
this.prev = tok; | ||
return tok; | ||
}, | ||
} | ||
@@ -188,3 +177,3 @@ /** | ||
isPartOfSelector: function() { | ||
isPartOfSelector() { | ||
var tok = this.stash[this.stash.length - 1] || this.prev; | ||
@@ -202,3 +191,3 @@ switch (tok && tok.type) { | ||
return false; | ||
}, | ||
} | ||
@@ -212,40 +201,40 @@ /** | ||
advance: function() { | ||
advance() { | ||
var column = this.column | ||
, line = this.lineno | ||
, tok = this.eos() | ||
|| this.null() | ||
|| this.sep() | ||
|| this.keyword() | ||
|| this.urlchars() | ||
|| this.comment() | ||
|| this.newline() | ||
|| this.escaped() | ||
|| this.important() | ||
|| this.literal() | ||
|| this.anonFunc() | ||
|| this.atrule() | ||
|| this.function() | ||
|| this.brace() | ||
|| this.paren() | ||
|| this.color() | ||
|| this.string() | ||
|| this.unit() | ||
|| this.namedop() | ||
|| this.boolean() | ||
|| this.unicode() | ||
|| this.ident() | ||
|| this.op() | ||
|| (function () { | ||
var token = this.eol(); | ||
|| this.null() | ||
|| this.sep() | ||
|| this.keyword() | ||
|| this.urlchars() | ||
|| this.comment() | ||
|| this.newline() | ||
|| this.escaped() | ||
|| this.important() | ||
|| this.literal() | ||
|| this.anonFunc() | ||
|| this.atrule() | ||
|| this.function() | ||
|| this.brace() | ||
|| this.paren() | ||
|| this.color() | ||
|| this.string() | ||
|| this.unit() | ||
|| this.namedop() | ||
|| this.boolean() | ||
|| this.unicode() | ||
|| this.ident() | ||
|| this.op() | ||
|| (function () { | ||
var token = this.eol(); | ||
if (token) { | ||
column = token.column; | ||
line = token.lineno; | ||
} | ||
if (token) { | ||
column = token.column; | ||
line = token.lineno; | ||
} | ||
return token; | ||
}).call(this) | ||
|| this.space() | ||
|| this.selector(); | ||
return token; | ||
}).call(this) | ||
|| this.space() | ||
|| this.selector(); | ||
@@ -256,3 +245,3 @@ tok.lineno = line; | ||
return tok; | ||
}, | ||
} | ||
@@ -266,5 +255,5 @@ /** | ||
peek: function() { | ||
peek() { | ||
return this.lookahead(1); | ||
}, | ||
} | ||
@@ -278,5 +267,5 @@ /** | ||
stashed: function() { | ||
stashed() { | ||
return this.stash.shift(); | ||
}, | ||
} | ||
@@ -287,3 +276,3 @@ /** | ||
eos: function() { | ||
eos() { | ||
if (this.str.length) return; | ||
@@ -296,3 +285,3 @@ if (this.indentStack.length) { | ||
} | ||
}, | ||
} | ||
@@ -303,3 +292,3 @@ /** | ||
urlchars: function() { | ||
urlchars() { | ||
var captures; | ||
@@ -311,3 +300,3 @@ if (!this.isURL) return; | ||
} | ||
}, | ||
} | ||
@@ -318,3 +307,3 @@ /** | ||
sep: function() { | ||
sep() { | ||
var captures; | ||
@@ -325,3 +314,3 @@ if (captures = /^;[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -332,3 +321,3 @@ /** | ||
eol: function() { | ||
eol() { | ||
if ('\r' == this.str[0]) { | ||
@@ -339,7 +328,7 @@ ++this.lineno; | ||
this.column = 1; | ||
while(this.space()); | ||
while (this.space()); | ||
return this.advance(); | ||
} | ||
}, | ||
} | ||
@@ -350,3 +339,3 @@ /** | ||
space: function() { | ||
space() { | ||
var captures; | ||
@@ -357,3 +346,3 @@ if (captures = /^([ \t]+)/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -364,3 +353,3 @@ /** | ||
escaped: function() { | ||
escaped() { | ||
var captures; | ||
@@ -372,3 +361,3 @@ if (captures = /^\\(.)[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -379,3 +368,3 @@ /** | ||
literal: function() { | ||
literal() { | ||
// HACK attack !!! | ||
@@ -407,3 +396,3 @@ var captures; | ||
} | ||
}, | ||
} | ||
@@ -414,3 +403,3 @@ /** | ||
important: function() { | ||
important() { | ||
var captures; | ||
@@ -421,3 +410,3 @@ if (captures = /^!important[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -428,3 +417,3 @@ /** | ||
brace: function() { | ||
brace() { | ||
var captures; | ||
@@ -436,3 +425,3 @@ if (captures = /^([{}])/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -443,3 +432,3 @@ /** | ||
paren: function() { | ||
paren() { | ||
var captures; | ||
@@ -454,3 +443,3 @@ if (captures = /^([()])([ \t]*)/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -461,3 +450,3 @@ /** | ||
null: function() { | ||
null() { | ||
var captures | ||
@@ -474,3 +463,3 @@ , tok; | ||
} | ||
}, | ||
} | ||
@@ -486,3 +475,3 @@ /** | ||
keyword: function() { | ||
keyword() { | ||
var captures | ||
@@ -500,3 +489,3 @@ , tok; | ||
} | ||
}, | ||
} | ||
@@ -514,3 +503,3 @@ /** | ||
namedop: function() { | ||
namedop() { | ||
var captures | ||
@@ -530,3 +519,3 @@ , tok; | ||
} | ||
}, | ||
} | ||
@@ -570,3 +559,3 @@ /** | ||
op: function() { | ||
op() { | ||
var captures; | ||
@@ -582,3 +571,3 @@ if (captures = /^([.]{1,3}|&&|\|\||[!<>=?:]=|\*\*|[-+*\/%]=?|[,=?:!~<>&\[\]])([ \t]*)/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -589,3 +578,3 @@ /** | ||
anonFunc: function() { | ||
anonFunc() { | ||
var tok; | ||
@@ -598,3 +587,3 @@ if ('@' == this.str[0] && '(' == this.str[1]) { | ||
} | ||
}, | ||
} | ||
@@ -605,3 +594,3 @@ /** | ||
atrule: function() { | ||
atrule() { | ||
var captures; | ||
@@ -635,3 +624,3 @@ if (captures = /^@(?!apply)(?:-(\w+)-)?([a-zA-Z0-9-_]+)[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -642,3 +631,3 @@ /** | ||
comment: function() { | ||
comment() { | ||
// Single line | ||
@@ -670,3 +659,3 @@ if ('/' == this.str[0] && '/' == this.str[1]) { | ||
} | ||
}, | ||
} | ||
@@ -677,6 +666,6 @@ /** | ||
boolean: function() { | ||
boolean() { | ||
var captures; | ||
if (captures = /^(true|false)\b([ \t]*)/.exec(this.str)) { | ||
var val = nodes.Boolean('true' == captures[1]); | ||
var val = new nodes.Boolean('true' == captures[1]); | ||
this.skip(captures); | ||
@@ -687,3 +676,3 @@ var tok = new Token('boolean', val); | ||
} | ||
}, | ||
} | ||
@@ -694,3 +683,3 @@ /** | ||
unicode: function() { | ||
unicode() { | ||
var captures; | ||
@@ -701,3 +690,3 @@ if (captures = /^u\+[0-9a-f?]{1,6}(?:-[0-9a-f]{1,6})?/i.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -708,3 +697,3 @@ /** | ||
function: function() { | ||
function() { | ||
var captures; | ||
@@ -719,3 +708,3 @@ if (captures = /^(-*[_a-zA-Z$][-\w\d$]*)\(([ \t]*)/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -726,3 +715,3 @@ /** | ||
ident: function() { | ||
ident() { | ||
var captures; | ||
@@ -733,3 +722,3 @@ if (captures = /^-*([_a-zA-Z$]|@apply)[-\w\d$]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -740,9 +729,9 @@ /** | ||
newline: function() { | ||
newline() { | ||
var captures, re; | ||
// we have established the indentation regexp | ||
if (this.indentRe){ | ||
if (this.indentRe) { | ||
captures = this.indentRe.exec(this.str); | ||
// figure out if we are using tabs or spaces | ||
// figure out if we are using tabs or spaces | ||
} else { | ||
@@ -783,7 +772,7 @@ // try tabs | ||
tok = this.stash.pop(); | ||
// Indent | ||
// Indent | ||
} else if (indents && indents != this.indentStack[0]) { | ||
this.indentStack.unshift(indents); | ||
tok = new Token('indent'); | ||
// Newline | ||
// Newline | ||
} else { | ||
@@ -795,3 +784,3 @@ tok = new Token('newline'); | ||
} | ||
}, | ||
} | ||
@@ -802,3 +791,3 @@ /** | ||
unit: function() { | ||
unit() { | ||
var captures; | ||
@@ -813,3 +802,3 @@ if (captures = /^(-)?(\d+\.\d+|\d+|\.\d+)(%|[a-zA-Z]+)?[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -820,3 +809,3 @@ /** | ||
string: function() { | ||
string() { | ||
var captures; | ||
@@ -827,6 +816,6 @@ if (captures = /^("[^"]*"|'[^']*')[ \t]*/.exec(this.str)) { | ||
this.skip(captures); | ||
str = str.slice(1,-1).replace(/\\n/g, '\n'); | ||
str = str.slice(1, -1).replace(/\\n/g, '\n'); | ||
return new Token('string', new nodes.String(str, quote)); | ||
} | ||
}, | ||
} | ||
@@ -837,3 +826,3 @@ /** | ||
color: function() { | ||
color() { | ||
return this.rrggbbaa() | ||
@@ -845,3 +834,3 @@ || this.rrggbb() | ||
|| this.n() | ||
}, | ||
} | ||
@@ -852,3 +841,3 @@ /** | ||
n: function() { | ||
n() { | ||
var captures; | ||
@@ -862,3 +851,3 @@ if (captures = /^#([a-fA-F0-9]{1})[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -869,3 +858,3 @@ /** | ||
nn: function() { | ||
nn() { | ||
var captures; | ||
@@ -879,3 +868,3 @@ if (captures = /^#([a-fA-F0-9]{2})[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -886,3 +875,3 @@ /** | ||
rgb: function() { | ||
rgb() { | ||
var captures; | ||
@@ -899,3 +888,3 @@ if (captures = /^#([a-fA-F0-9]{3})[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -906,3 +895,3 @@ /** | ||
rgba: function() { | ||
rgba() { | ||
var captures; | ||
@@ -916,7 +905,7 @@ if (captures = /^#([a-fA-F0-9]{4})[ \t]*/.exec(this.str)) { | ||
, a = parseInt(rgb[3] + rgb[3], 16) | ||
, color = new nodes.RGBA(r, g, b, a/255); | ||
, color = new nodes.RGBA(r, g, b, a / 255); | ||
color.raw = captures[0]; | ||
return new Token('color', color); | ||
} | ||
}, | ||
} | ||
@@ -927,3 +916,3 @@ /** | ||
rrggbb: function() { | ||
rrggbb() { | ||
var captures; | ||
@@ -940,3 +929,3 @@ if (captures = /^#([a-fA-F0-9]{6})[ \t]*/.exec(this.str)) { | ||
} | ||
}, | ||
} | ||
@@ -947,3 +936,3 @@ /** | ||
rrggbbaa: function() { | ||
rrggbbaa() { | ||
var captures; | ||
@@ -957,7 +946,7 @@ if (captures = /^#([a-fA-F0-9]{8})[ \t]*/.exec(this.str)) { | ||
, a = parseInt(rgb.substr(6, 2), 16) | ||
, color = new nodes.RGBA(r, g, b, a/255); | ||
, color = new nodes.RGBA(r, g, b, a / 255); | ||
color.raw = captures[0]; | ||
return new Token('color', color); | ||
} | ||
}, | ||
} | ||
@@ -968,3 +957,3 @@ /** | ||
selector: function() { | ||
selector() { | ||
var captures; | ||
@@ -971,0 +960,0 @@ if (captures = /^\^|.*?(?=\/\/(?![^\[]*\])|[,\n{])/.exec(this.str)) { |
@@ -14,78 +14,78 @@ | ||
/** | ||
* Initialize a new `Arguments`. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Arguments extends nodes.Expression { | ||
/** | ||
* Initialize a new `Arguments`. | ||
* | ||
* @api public | ||
*/ | ||
var Arguments = module.exports = function Arguments(){ | ||
nodes.Expression.call(this); | ||
this.map = {}; | ||
}; | ||
constructor() { | ||
super(); | ||
this.map = {}; | ||
} | ||
/** | ||
* Inherit from `nodes.Expression.prototype`. | ||
*/ | ||
/** | ||
* Initialize an `Arguments` object with the nodes | ||
* from the given `expr`. | ||
* | ||
* @param {Expression} expr | ||
* @return {Arguments} | ||
* @api public | ||
*/ | ||
Arguments.prototype.__proto__ = nodes.Expression.prototype; | ||
static fromExpression(expr) { | ||
var args = new Arguments | ||
, len = expr.nodes.length; | ||
args.lineno = expr.lineno; | ||
args.column = expr.column; | ||
args.isList = expr.isList; | ||
for (var i = 0; i < len; ++i) { | ||
args.push(expr.nodes[i]); | ||
} | ||
return args; | ||
}; | ||
/** | ||
* Initialize an `Arguments` object with the nodes | ||
* from the given `expr`. | ||
* | ||
* @param {Expression} expr | ||
* @return {Arguments} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Arguments.fromExpression = function(expr){ | ||
var args = new Arguments | ||
, len = expr.nodes.length; | ||
args.lineno = expr.lineno; | ||
args.column = expr.column; | ||
args.isList = expr.isList; | ||
for (var i = 0; i < len; ++i) { | ||
args.push(expr.nodes[i]); | ||
} | ||
return args; | ||
}; | ||
clone(parent) { | ||
var clone = super.clone(parent); | ||
clone.map = {}; | ||
for (var key in this.map) { | ||
clone.map[key] = this.map[key].clone(parent, clone); | ||
} | ||
clone.isList = this.isList; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Arguments.prototype.clone = function(parent){ | ||
var clone = nodes.Expression.prototype.clone.call(this, parent); | ||
clone.map = {}; | ||
for (var key in this.map) { | ||
clone.map[key] = this.map[key].clone(parent, clone); | ||
} | ||
clone.isList = this.isList; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
toJSON() { | ||
return { | ||
__type: 'Arguments', | ||
map: this.map, | ||
isList: this.isList, | ||
preserve: this.preserve, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename, | ||
nodes: this.nodes | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Arguments.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Arguments', | ||
map: this.map, | ||
isList: this.isList, | ||
preserve: this.preserve, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename, | ||
nodes: this.nodes | ||
}; | ||
}; | ||
@@ -13,68 +13,64 @@ /*! | ||
/** | ||
* Initialize a new `@block` node. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Atblock extends Node { | ||
/** | ||
* Initialize a new `@block` node. | ||
* | ||
* @api public | ||
*/ | ||
var Atblock = module.exports = function Atblock(){ | ||
Node.call(this); | ||
}; | ||
constructor() { | ||
super(); | ||
} | ||
/** | ||
* Return `block` nodes. | ||
*/ | ||
/** | ||
* Return `block` nodes. | ||
*/ | ||
Atblock.prototype.__defineGetter__('nodes', function(){ | ||
return this.block.nodes; | ||
}); | ||
get nodes() { | ||
return this.block.nodes; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Atblock.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Atblock; | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return @block. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Atblock.prototype.clone = function(parent){ | ||
var clone = new Atblock; | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toString() { | ||
return '@block'; | ||
}; | ||
/** | ||
* Return @block. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Atblock.prototype.toString = function(){ | ||
return '@block'; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Atblock.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Atblock', | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
fileno: this.fileno | ||
toJSON() { | ||
return { | ||
__type: 'Atblock', | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
fileno: this.fileno | ||
}; | ||
}; | ||
}; |
@@ -13,103 +13,99 @@ /*! | ||
/** | ||
* Initialize a new at-rule node. | ||
* | ||
* @param {String} type | ||
* @api public | ||
*/ | ||
module.exports = class Atrule extends Node { | ||
/** | ||
* Initialize a new at-rule node. | ||
* | ||
* @param {String} type | ||
* @api public | ||
*/ | ||
var Atrule = module.exports = function Atrule(type){ | ||
Node.call(this); | ||
this.type = type; | ||
}; | ||
constructor(type) { | ||
super() | ||
this.type = type; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Check if at-rule's block has only properties. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Atrule.prototype.__proto__ = Node.prototype; | ||
get hasOnlyProperties() { | ||
if (!this.block) return false; | ||
/** | ||
* Check if at-rule's block has only properties. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Atrule.prototype.__defineGetter__('hasOnlyProperties', function(){ | ||
if (!this.block) return false; | ||
var nodes = this.block.nodes; | ||
for (var i = 0, len = nodes.length; i < len; ++i) { | ||
var nodeName = nodes[i].nodeName; | ||
switch(nodes[i].nodeName) { | ||
case 'property': | ||
case 'expression': | ||
case 'comment': | ||
continue; | ||
default: | ||
return false; | ||
var nodes = this.block.nodes; | ||
for (var i = 0, len = nodes.length; i < len; ++i) { | ||
var nodeName = nodes[i].nodeName; | ||
switch (nodes[i].nodeName) { | ||
case 'property': | ||
case 'expression': | ||
case 'comment': | ||
continue; | ||
default: | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
return true; | ||
}); | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Atrule.prototype.clone = function(parent){ | ||
var clone = new Atrule(this.type); | ||
if (this.block) clone.block = this.block.clone(parent, clone); | ||
clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
clone(parent) { | ||
var clone = new Atrule(this.type); | ||
if (this.block) clone.block = this.block.clone(parent, clone); | ||
clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Atrule.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'Atrule', | ||
type: this.type, | ||
segments: this.segments, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
var json = { | ||
__type: 'Atrule', | ||
type: this.type, | ||
segments: this.segments, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.block) json.block = this.block; | ||
return json; | ||
}; | ||
if (this.block) json.block = this.block; | ||
return json; | ||
}; | ||
/** | ||
* Return @<type>. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return @<type>. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Atrule.prototype.toString = function(){ | ||
return '@' + this.type; | ||
}; | ||
toString() { | ||
return '@' + this.type; | ||
}; | ||
/** | ||
* Check if the at-rule's block has output nodes. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if the at-rule's block has output nodes. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Atrule.prototype.__defineGetter__('hasOutput', function(){ | ||
return !!this.block && hasOutput(this.block); | ||
}); | ||
get hasOutput() { | ||
return !!this.block && hasOutput(this.block); | ||
}; | ||
}; | ||
@@ -120,3 +116,3 @@ function hasOutput(block) { | ||
// only placeholder selectors | ||
if (nodes.every(function(node){ | ||
if (nodes.every(function (node) { | ||
return 'group' == node.nodeName && node.hasOnlyPlaceholders; | ||
@@ -126,3 +122,3 @@ })) return false; | ||
// something visible | ||
return nodes.some(function(node) { | ||
return nodes.some(function (node) { | ||
switch (node.nodeName) { | ||
@@ -129,0 +125,0 @@ case 'property': |
@@ -14,71 +14,68 @@ | ||
/** | ||
* Initialize a new `BinOp` with `op`, `left` and `right`. | ||
* | ||
* @param {String} op | ||
* @param {Node} left | ||
* @param {Node} right | ||
* @api public | ||
*/ | ||
module.exports = class BinOp extends Node { | ||
/** | ||
* Initialize a new `BinOp` with `op`, `left` and `right`. | ||
* | ||
* @param {String} op | ||
* @param {Node} left | ||
* @param {Node} right | ||
* @api public | ||
*/ | ||
var BinOp = module.exports = function BinOp(op, left, right){ | ||
Node.call(this); | ||
this.op = op; | ||
this.left = left; | ||
this.right = right; | ||
}; | ||
constructor(op, left, right) { | ||
super(); | ||
this.op = op; | ||
this.left = left; | ||
this.right = right; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
BinOp.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new BinOp(this.op); | ||
clone.left = this.left.clone(parent, clone); | ||
clone.right = this.right && this.right.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
if (this.val) clone.val = this.val.clone(parent, clone); | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return <left> <op> <right> | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
toString() { | ||
return this.left.toString() + ' ' + this.op + ' ' + this.right.toString(); | ||
}; | ||
BinOp.prototype.clone = function(parent){ | ||
var clone = new BinOp(this.op); | ||
clone.left = this.left.clone(parent, clone); | ||
clone.right = this.right && this.right.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
if (this.val) clone.val = this.val.clone(parent, clone); | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return <left> <op> <right> | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
BinOp.prototype.toString = function() { | ||
return this.left.toString() + ' ' + this.op + ' ' + this.right.toString(); | ||
}; | ||
toJSON() { | ||
var json = { | ||
__type: 'BinOp', | ||
left: this.left, | ||
right: this.right, | ||
op: this.op, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.val) json.val = this.val; | ||
return json; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
BinOp.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'BinOp', | ||
left: this.left, | ||
right: this.right, | ||
op: this.op, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.val) json.val = this.val; | ||
return json; | ||
}; |
@@ -14,115 +14,112 @@ | ||
/** | ||
* Initialize a new `Block` node with `parent` Block. | ||
* | ||
* @param {Block} parent | ||
* @api public | ||
*/ | ||
module.exports = class Block extends Node { | ||
/** | ||
* Initialize a new `Block` node with `parent` Block. | ||
* | ||
* @param {Block} parent | ||
* @api public | ||
*/ | ||
var Block = module.exports = function Block(parent, node){ | ||
Node.call(this); | ||
this.nodes = []; | ||
this.parent = parent; | ||
this.node = node; | ||
this.scope = true; | ||
}; | ||
constructor(parent, node) { | ||
super(); | ||
this.nodes = []; | ||
this.parent = parent; | ||
this.node = node; | ||
this.scope = true; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Check if this block has properties.. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Block.prototype.__proto__ = Node.prototype; | ||
/** | ||
* Check if this block has properties.. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Block.prototype.__defineGetter__('hasProperties', function(){ | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
if ('property' == this.nodes[i].nodeName) { | ||
return true; | ||
get hasProperties() { | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
if ('property' == this.nodes[i].nodeName) { | ||
return true; | ||
} | ||
} | ||
} | ||
}); | ||
}; | ||
/** | ||
* Check if this block has @media nodes. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if this block has @media nodes. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Block.prototype.__defineGetter__('hasMedia', function(){ | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
var nodeName = this.nodes[i].nodeName; | ||
if ('media' == nodeName) { | ||
return true; | ||
get hasMedia() { | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
var nodeName = this.nodes[i].nodeName; | ||
if ('media' == nodeName) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
}); | ||
return false; | ||
}; | ||
/** | ||
* Check if this block is empty. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if this block is empty. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Block.prototype.__defineGetter__('isEmpty', function(){ | ||
return !this.nodes.length || this.nodes.every(function(n){return n.nodeName == 'comment'}); | ||
}); | ||
get isEmpty() { | ||
return !this.nodes.length || this.nodes.every(function (n) { return n.nodeName == 'comment' }); | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Block.prototype.clone = function(parent, node){ | ||
parent = parent || this.parent; | ||
var clone = new Block(parent, node || this.node); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.scope = this.scope; | ||
this.nodes.forEach(function(node){ | ||
clone.push(node.clone(clone, clone)); | ||
}); | ||
return clone; | ||
}; | ||
clone(parent, node) { | ||
parent = parent || this.parent; | ||
var clone = new Block(parent, node || this.node); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.scope = this.scope; | ||
this.nodes.forEach(function (node) { | ||
clone.push(node.clone(clone, clone)); | ||
}); | ||
return clone; | ||
}; | ||
/** | ||
* Push a `node` to this block. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
/** | ||
* Push a `node` to this block. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
Block.prototype.push = function(node){ | ||
this.nodes.push(node); | ||
}; | ||
push(node) { | ||
this.nodes.push(node); | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Block.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Block', | ||
// parent: this.parent, | ||
// node: this.node, | ||
scope: this.scope, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename, | ||
nodes: this.nodes | ||
toJSON() { | ||
return { | ||
__type: 'Block', | ||
// parent: this.parent, | ||
// node: this.node, | ||
scope: this.scope, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename, | ||
nodes: this.nodes | ||
}; | ||
}; | ||
}; |
@@ -15,104 +15,100 @@ | ||
/** | ||
* Initialize a new `Boolean` node with the given `val`. | ||
* | ||
* @param {Boolean} val | ||
* @api public | ||
*/ | ||
module.exports = class Boolean extends Node { | ||
/** | ||
* Initialize a new `Boolean` node with the given `val`. | ||
* | ||
* @param {Boolean} val | ||
* @api public | ||
*/ | ||
var Boolean = module.exports = function Boolean(val){ | ||
Node.call(this); | ||
if (this.nodeName) { | ||
this.val = !!val; | ||
} else { | ||
return new Boolean(val); | ||
constructor(val) { | ||
super(); | ||
if (this.nodeName) { | ||
this.val = !!val; | ||
} else { | ||
return new Boolean(val); | ||
} | ||
} | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return `this` node. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Boolean.prototype.__proto__ = Node.prototype; | ||
toBoolean() { | ||
return this; | ||
}; | ||
/** | ||
* Return `this` node. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return `true` if this node represents `true`. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Boolean.prototype.toBoolean = function(){ | ||
return this; | ||
}; | ||
get isTrue() { | ||
return this.val; | ||
}; | ||
/** | ||
* Return `true` if this node represents `true`. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return `true` if this node represents `false`. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Boolean.prototype.__defineGetter__('isTrue', function(){ | ||
return this.val; | ||
}); | ||
get isFalse() { | ||
return !this.val; | ||
}; | ||
/** | ||
* Return `true` if this node represents `false`. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Negate the value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Boolean.prototype.__defineGetter__('isFalse', function(){ | ||
return ! this.val; | ||
}); | ||
negate() { | ||
return new Boolean(!this.val); | ||
}; | ||
/** | ||
* Negate the value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return 'Boolean'. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Boolean.prototype.negate = function(){ | ||
return new Boolean(!this.val); | ||
}; | ||
inspect() { | ||
return '[Boolean ' + this.val + ']'; | ||
}; | ||
/** | ||
* Return 'Boolean'. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return 'true' or 'false'. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Boolean.prototype.inspect = function(){ | ||
return '[Boolean ' + this.val + ']'; | ||
}; | ||
toString() { | ||
return this.val | ||
? 'true' | ||
: 'false'; | ||
}; | ||
/** | ||
* Return 'true' or 'false'. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representaiton of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Boolean.prototype.toString = function(){ | ||
return this.val | ||
? 'true' | ||
: 'false'; | ||
}; | ||
/** | ||
* Return a JSON representaiton of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Boolean.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Boolean', | ||
val: this.val | ||
toJSON() { | ||
return { | ||
__type: 'Boolean', | ||
val: this.val | ||
}; | ||
}; | ||
}; |
@@ -14,73 +14,69 @@ | ||
/** | ||
* Initialize a new `Call` with `name` and `args`. | ||
* | ||
* @param {String} name | ||
* @param {Expression} args | ||
* @api public | ||
*/ | ||
module.exports = class Call extends Node { | ||
/** | ||
* Initialize a new `Call` with `name` and `args`. | ||
* | ||
* @param {String} name | ||
* @param {Expression} args | ||
* @api public | ||
*/ | ||
var Call = module.exports = function Call(name, args){ | ||
Node.call(this); | ||
this.name = name; | ||
this.args = args; | ||
}; | ||
constructor(name, args) { | ||
super(); | ||
this.name = name; | ||
this.args = args; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Call.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Call(this.name); | ||
clone.args = this.args.clone(parent, clone); | ||
if (this.block) clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return <name>(param1, param2, ...). | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Call.prototype.clone = function(parent){ | ||
var clone = new Call(this.name); | ||
clone.args = this.args.clone(parent, clone); | ||
if (this.block) clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toString() { | ||
var args = this.args.nodes.map(function (node) { | ||
var str = node.toString(); | ||
return str.slice(1, str.length - 1); | ||
}).join(', '); | ||
/** | ||
* Return <name>(param1, param2, ...). | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
return this.name + '(' + args + ')'; | ||
}; | ||
Call.prototype.toString = function(){ | ||
var args = this.args.nodes.map(function(node) { | ||
var str = node.toString(); | ||
return str.slice(1, str.length - 1); | ||
}).join(', '); | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
return this.name + '(' + args + ')'; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Call.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'Call', | ||
name: this.name, | ||
args: this.args, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
var json = { | ||
__type: 'Call', | ||
name: this.name, | ||
args: this.args, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.block) json.block = this.block; | ||
return json; | ||
}; | ||
if (this.block) json.block = this.block; | ||
return json; | ||
}; |
@@ -14,46 +14,43 @@ | ||
/** | ||
* Initialize a new `Charset` with the given `val` | ||
* | ||
* @param {String} val | ||
* @api public | ||
*/ | ||
module.exports = class Charset extends Node { | ||
/** | ||
* Initialize a new `Charset` with the given `val` | ||
* | ||
* @param {String} val | ||
* @api public | ||
*/ | ||
var Charset = module.exports = function Charset(val){ | ||
Node.call(this); | ||
this.val = val; | ||
}; | ||
constructor(val) { | ||
super(); | ||
this.val = val; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return @charset "val". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Charset.prototype.__proto__ = Node.prototype; | ||
toString() { | ||
return '@charset ' + this.val; | ||
}; | ||
/** | ||
* Return @charset "val". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Charset.prototype.toString = function(){ | ||
return '@charset ' + this.val; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Charset', | ||
val: this.val, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Charset.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Charset', | ||
val: this.val, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -14,52 +14,49 @@ | ||
/** | ||
* Initialize a new `Comment` with the given `str`. | ||
* | ||
* @param {String} str | ||
* @param {Boolean} suppress | ||
* @param {Boolean} inline | ||
* @api public | ||
*/ | ||
module.exports = class Comment extends Node { | ||
/** | ||
* Initialize a new `Comment` with the given `str`. | ||
* | ||
* @param {String} str | ||
* @param {Boolean} suppress | ||
* @param {Boolean} inline | ||
* @api public | ||
*/ | ||
var Comment = module.exports = function Comment(str, suppress, inline){ | ||
Node.call(this); | ||
this.str = str; | ||
this.suppress = suppress; | ||
this.inline = inline; | ||
}; | ||
constructor(str, suppress, inline) { | ||
super(); | ||
this.str = str; | ||
this.suppress = suppress; | ||
this.inline = inline; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Comment.prototype.__proto__ = Node.prototype; | ||
toJSON() { | ||
return { | ||
__type: 'Comment', | ||
str: this.str, | ||
suppress: this.suppress, | ||
inline: this.inline, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return comment. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Comment.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Comment', | ||
str: this.str, | ||
suppress: this.suppress, | ||
inline: this.inline, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return this.str; | ||
}; | ||
}; | ||
/** | ||
* Return comment. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Comment.prototype.toString = function(){ | ||
return this.str; | ||
}; |
@@ -15,62 +15,59 @@ | ||
/** | ||
* Initialize a new `Each` node with the given `val` name, | ||
* `key` name, `expr`, and `block`. | ||
* | ||
* @param {String} val | ||
* @param {String} key | ||
* @param {Expression} expr | ||
* @param {Block} block | ||
* @api public | ||
*/ | ||
module.exports = class Each extends Node { | ||
/** | ||
* Initialize a new `Each` node with the given `val` name, | ||
* `key` name, `expr`, and `block`. | ||
* | ||
* @param {String} val | ||
* @param {String} key | ||
* @param {Expression} expr | ||
* @param {Block} block | ||
* @api public | ||
*/ | ||
var Each = module.exports = function Each(val, key, expr, block){ | ||
Node.call(this); | ||
this.val = val; | ||
this.key = key; | ||
this.expr = expr; | ||
this.block = block; | ||
}; | ||
constructor(val, key, expr, block) { | ||
super(); | ||
this.val = val; | ||
this.key = key; | ||
this.expr = expr; | ||
this.block = block; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Each.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Each(this.val, this.key); | ||
clone.expr = this.expr.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Each.prototype.clone = function(parent){ | ||
var clone = new Each(this.val, this.key); | ||
clone.expr = this.expr.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Each', | ||
val: this.val, | ||
key: this.key, | ||
expr: this.expr, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Each.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Each', | ||
val: this.val, | ||
key: this.key, | ||
expr: this.expr, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
} |
@@ -16,206 +16,202 @@ | ||
/** | ||
* Initialize a new `Expression`. | ||
* | ||
* @param {Boolean} isList | ||
* @api public | ||
*/ | ||
module.exports = class Expression extends Node { | ||
/** | ||
* Initialize a new `Expression`. | ||
* | ||
* @param {Boolean} isList | ||
* @api public | ||
*/ | ||
var Expression = module.exports = function Expression(isList){ | ||
Node.call(this); | ||
this.nodes = []; | ||
this.isList = isList; | ||
}; | ||
constructor(isList) { | ||
super(); | ||
this.nodes = []; | ||
this.isList = isList; | ||
} | ||
/** | ||
* Check if the variable has a value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if the variable has a value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Expression.prototype.__defineGetter__('isEmpty', function(){ | ||
return !this.nodes.length; | ||
}); | ||
get isEmpty() { | ||
return !this.nodes.length; | ||
}; | ||
/** | ||
* Return the first node in this expression. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* 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; | ||
}); | ||
get first() { | ||
return this.nodes[0] | ||
? this.nodes[0].first | ||
: nodes.null; | ||
}; | ||
/** | ||
* Hash all the nodes in order. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* 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('::'); | ||
}); | ||
get hash() { | ||
return this.nodes.map(function (node) { | ||
return node.hash; | ||
}).join('::'); | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Expression.prototype.__proto__ = Node.prototype; | ||
clone(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; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Push the given `node`. | ||
* | ||
* @param {Node} 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(node) { | ||
this.nodes.push(node); | ||
}; | ||
/** | ||
* Push the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {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()); | ||
operate(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 super.operate(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 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); | ||
} | ||
}; | ||
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 | ||
*/ | ||
/** | ||
* 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(); | ||
}; | ||
toBoolean() { | ||
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 | ||
*/ | ||
/** | ||
* 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 ? ', ' : ' ') + ')'; | ||
}; | ||
toString() { | ||
return '(' + this.nodes.map(function (node) { | ||
return node.toString(); | ||
}).join(this.isList ? ', ' : ' ') + ')'; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* 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 | ||
toJSON() { | ||
return { | ||
__type: 'Expression', | ||
isList: this.isList, | ||
preserve: this.preserve, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename, | ||
nodes: this.nodes | ||
}; | ||
}; | ||
}; |
@@ -14,57 +14,54 @@ | ||
/** | ||
* Initialize a new `Extend` with the given `selectors` array. | ||
* | ||
* @param {Array} selectors array of the selectors | ||
* @api public | ||
*/ | ||
module.exports = class Extend extends Node { | ||
/** | ||
* Initialize a new `Extend` with the given `selectors` array. | ||
* | ||
* @param {Array} selectors array of the selectors | ||
* @api public | ||
*/ | ||
var Extend = module.exports = function Extend(selectors){ | ||
Node.call(this); | ||
this.selectors = selectors; | ||
}; | ||
constructor(selectors) { | ||
super(); | ||
this.selectors = selectors; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Extend.prototype.__proto__ = Node.prototype; | ||
clone() { | ||
return new Extend(this.selectors); | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return `@extend selectors`. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Extend.prototype.clone = function(){ | ||
return new Extend(this.selectors); | ||
}; | ||
toString() { | ||
return '@extend ' + this.selectors.join(', '); | ||
}; | ||
/** | ||
* Return `@extend selectors`. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Extend.prototype.toString = function(){ | ||
return '@extend ' + this.selectors.join(', '); | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Extend', | ||
selectors: this.selectors, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Extend.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Extend', | ||
selectors: this.selectors, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -14,72 +14,68 @@ | ||
/** | ||
* Initialize a new `Feature` with the given `segs`. | ||
* | ||
* @param {Array} segs | ||
* @api public | ||
*/ | ||
module.exports = class Feature extends Node { | ||
/** | ||
* Initialize a new `Feature` with the given `segs`. | ||
* | ||
* @param {Array} segs | ||
* @api public | ||
*/ | ||
var Feature = module.exports = function Feature(segs){ | ||
Node.call(this); | ||
this.segments = segs; | ||
this.expr = null; | ||
}; | ||
constructor(segs) { | ||
super(); | ||
this.segments = segs; | ||
this.expr = null; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Feature.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Feature; | ||
clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); | ||
if (this.expr) clone.expr = this.expr.clone(parent, clone); | ||
if (this.name) clone.name = this.name; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return "<ident>" or "(<ident>: <expr>)" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Feature.prototype.clone = function(parent){ | ||
var clone = new Feature; | ||
clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); | ||
if (this.expr) clone.expr = this.expr.clone(parent, clone); | ||
if (this.name) clone.name = this.name; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toString() { | ||
if (this.expr) { | ||
return '(' + this.segments.join('') + ': ' + this.expr.toString() + ')'; | ||
} else { | ||
return this.segments.join(''); | ||
} | ||
}; | ||
/** | ||
* Return "<ident>" or "(<ident>: <expr>)" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Feature.prototype.toString = function(){ | ||
if (this.expr) { | ||
return '(' + this.segments.join('') + ': ' + this.expr.toString() + ')'; | ||
} else { | ||
return this.segments.join(''); | ||
} | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Feature.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'Feature', | ||
segments: this.segments, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
var json = { | ||
__type: 'Feature', | ||
segments: this.segments, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.expr) json.expr = this.expr; | ||
if (this.name) json.name = this.name; | ||
return json; | ||
}; | ||
if (this.expr) json.expr = this.expr; | ||
if (this.name) json.name = this.name; | ||
return json; | ||
}; |
@@ -14,116 +14,113 @@ | ||
/** | ||
* Initialize a new `Function` with `name`, `params`, and `body`. | ||
* | ||
* @param {String} name | ||
* @param {Params|Function} params | ||
* @param {Block} body | ||
* @api public | ||
*/ | ||
module.exports = class Function extends Node { | ||
/** | ||
* Initialize a new `Function` with `name`, `params`, and `body`. | ||
* | ||
* @param {String} name | ||
* @param {Params|Function} params | ||
* @param {Block} body | ||
* @api public | ||
*/ | ||
var Function = module.exports = function Function(name, params, body){ | ||
Node.call(this); | ||
this.name = name; | ||
this.params = params; | ||
this.block = body; | ||
if ('function' == typeof params) this.fn = params; | ||
}; | ||
constructor(name, params, body) { | ||
super(); | ||
this.name = name; | ||
this.params = params; | ||
this.block = body; | ||
if ('function' == typeof params) this.fn = params; | ||
} | ||
/** | ||
* Check function arity. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check function arity. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Function.prototype.__defineGetter__('arity', function(){ | ||
return this.params.length; | ||
}); | ||
get arity() { | ||
return this.params.length; | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Function.prototype.__proto__ = Node.prototype; | ||
get hash() { | ||
return 'function ' + this.name; | ||
}; | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Function.prototype.__defineGetter__('hash', function(){ | ||
return 'function ' + this.name; | ||
}); | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Function.prototype.clone = function(parent){ | ||
if (this.fn) { | ||
var clone = new Function( | ||
clone(parent) { | ||
if (this.fn) { | ||
var clone = new Function( | ||
this.name | ||
, this.fn); | ||
} else { | ||
var clone = new Function(this.name); | ||
clone.params = this.params.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
} | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
, this.fn); | ||
} else { | ||
var clone = new Function(this.name); | ||
clone.params = this.params.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
} | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return <name>(param1, param2, ...). | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return <name>(param1, param2, ...). | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Function.prototype.toString = function(){ | ||
if (this.fn) { | ||
return this.name | ||
+ '(' | ||
+ this.fn.toString() | ||
.match(/^function *\w*\((.*?)\)/) | ||
.slice(1) | ||
.join(', ') | ||
+ ')'; | ||
} else { | ||
return this.name | ||
+ '(' | ||
+ this.params.nodes.join(', ') | ||
+ ')'; | ||
} | ||
}; | ||
toString() { | ||
if (this.fn) { | ||
return this.name | ||
+ '(' | ||
+ this.fn.toString() | ||
.match(/^function *\w*\((.*?)\)/) | ||
.slice(1) | ||
.join(', ') | ||
+ ')'; | ||
} else { | ||
return this.name | ||
+ '(' | ||
+ this.params.nodes.join(', ') | ||
+ ')'; | ||
} | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Function.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'Function', | ||
name: this.name, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
var json = { | ||
__type: 'Function', | ||
name: this.name, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.fn) { | ||
json.fn = this.fn; | ||
} else { | ||
json.params = this.params; | ||
json.block = this.block; | ||
} | ||
return json; | ||
}; | ||
if (this.fn) { | ||
json.fn = this.fn; | ||
} else { | ||
json.params = this.params; | ||
json.block = this.block; | ||
} | ||
return json; | ||
}; |
@@ -14,98 +14,94 @@ | ||
/** | ||
* Initialize a new `Group`. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Group extends Node { | ||
/** | ||
* Initialize a new `Group`. | ||
* | ||
* @api public | ||
*/ | ||
var Group = module.exports = function Group(){ | ||
Node.call(this); | ||
this.nodes = []; | ||
this.extends = []; | ||
}; | ||
constructor() { | ||
super(); | ||
this.nodes = []; | ||
this.extends = []; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Push the given `selector` node. | ||
* | ||
* @param {Selector} selector | ||
* @api public | ||
*/ | ||
Group.prototype.__proto__ = Node.prototype; | ||
push(selector) { | ||
this.nodes.push(selector); | ||
}; | ||
/** | ||
* Push the given `selector` node. | ||
* | ||
* @param {Selector} selector | ||
* @api public | ||
*/ | ||
/** | ||
* Return this set's `Block`. | ||
*/ | ||
Group.prototype.push = function(selector){ | ||
this.nodes.push(selector); | ||
}; | ||
get block() { | ||
return this.nodes[0].block; | ||
}; | ||
/** | ||
* Return this set's `Block`. | ||
*/ | ||
/** | ||
* Assign `block` to each selector in this set. | ||
* | ||
* @param {Block} block | ||
* @api public | ||
*/ | ||
Group.prototype.__defineGetter__('block', function(){ | ||
return this.nodes[0].block; | ||
}); | ||
set block(block) { | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
this.nodes[i].block = block; | ||
} | ||
}; | ||
/** | ||
* Assign `block` to each selector in this set. | ||
* | ||
* @param {Block} block | ||
* @api public | ||
*/ | ||
/** | ||
* Check if this set has only placeholders. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Group.prototype.__defineSetter__('block', function(block){ | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
this.nodes[i].block = block; | ||
} | ||
}); | ||
get hasOnlyPlaceholders() { | ||
return this.nodes.every(function (selector) { return selector.isPlaceholder; }); | ||
}; | ||
/** | ||
* Check if this set has only placeholders. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Group.prototype.__defineGetter__('hasOnlyPlaceholders', function(){ | ||
return this.nodes.every(function(selector) { return selector.isPlaceholder; }); | ||
}); | ||
clone(parent) { | ||
var clone = new Group; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
this.nodes.forEach(function (node) { | ||
clone.push(node.clone(parent, clone)); | ||
}); | ||
clone.filename = this.filename; | ||
clone.block = this.block.clone(parent, clone); | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Group.prototype.clone = function(parent){ | ||
var clone = new Group; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
this.nodes.forEach(function(node){ | ||
clone.push(node.clone(parent, clone)); | ||
}); | ||
clone.filename = this.filename; | ||
clone.block = this.block.clone(parent, clone); | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Group.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Group', | ||
nodes: this.nodes, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Group', | ||
nodes: this.nodes, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -25,217 +25,215 @@ | ||
var HSLA = exports = module.exports = function HSLA(h,s,l,a){ | ||
Node.call(this); | ||
this.h = clampDegrees(h); | ||
this.s = clampPercentage(s); | ||
this.l = clampPercentage(l); | ||
this.a = clampAlpha(a); | ||
this.hsla = this; | ||
}; | ||
exports = module.exports = class HSLA extends Node { | ||
constructor(h, s, l, a) { | ||
super(); | ||
this.h = clampDegrees(h); | ||
this.s = clampPercentage(s); | ||
this.l = clampPercentage(l); | ||
this.a = clampAlpha(a); | ||
this.hsla = this; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return hsla(n,n,n,n). | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
HSLA.prototype.__proto__ = Node.prototype; | ||
toString() { | ||
return 'hsla(' | ||
+ this.h + ',' | ||
+ this.s.toFixed(0) + '%,' | ||
+ this.l.toFixed(0) + '%,' | ||
+ this.a + ')'; | ||
}; | ||
/** | ||
* Return hsla(n,n,n,n). | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
HSLA.prototype.toString = function(){ | ||
return 'hsla(' | ||
+ this.h + ',' | ||
+ this.s.toFixed(0) + '%,' | ||
+ this.l.toFixed(0) + '%,' | ||
+ this.a + ')'; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
HSLA.prototype.clone = function(parent){ | ||
var clone = new HSLA( | ||
clone(parent) { | ||
var clone = new HSLA( | ||
this.h | ||
, this.s | ||
, this.l | ||
, this.a); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
, this.s | ||
, this.l | ||
, this.a); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
HSLA.prototype.toJSON = function(){ | ||
return { | ||
__type: 'HSLA', | ||
h: this.h, | ||
s: this.s, | ||
l: this.l, | ||
a: this.a, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'HSLA', | ||
h: this.h, | ||
s: this.s, | ||
l: this.l, | ||
a: this.a, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Return rgba `RGBA` representation. | ||
* | ||
* @return {RGBA} | ||
* @api public | ||
*/ | ||
/** | ||
* Return rgba `RGBA` representation. | ||
* | ||
* @return {RGBA} | ||
* @api public | ||
*/ | ||
HSLA.prototype.__defineGetter__('rgba', function(){ | ||
return nodes.RGBA.fromHSLA(this); | ||
}); | ||
get rgba() { | ||
return nodes.RGBA.fromHSLA(this); | ||
}; | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
HSLA.prototype.__defineGetter__('hash', function(){ | ||
return this.rgba.toString(); | ||
}); | ||
get hash() { | ||
return this.rgba.toString(); | ||
}; | ||
/** | ||
* Add h,s,l to the current component values. | ||
* | ||
* @param {Number} h | ||
* @param {Number} s | ||
* @param {Number} l | ||
* @return {HSLA} new node | ||
* @api public | ||
*/ | ||
/** | ||
* Add h,s,l to the current component values. | ||
* | ||
* @param {Number} h | ||
* @param {Number} s | ||
* @param {Number} l | ||
* @return {HSLA} new node | ||
* @api public | ||
*/ | ||
HSLA.prototype.add = function(h,s,l){ | ||
return new HSLA( | ||
add(h, s, l) { | ||
return new HSLA( | ||
this.h + h | ||
, this.s + s | ||
, this.l + l | ||
, this.a); | ||
}; | ||
, this.s + s | ||
, this.l + l | ||
, this.a); | ||
}; | ||
/** | ||
* Subtract h,s,l from the current component values. | ||
* | ||
* @param {Number} h | ||
* @param {Number} s | ||
* @param {Number} l | ||
* @return {HSLA} new node | ||
* @api public | ||
*/ | ||
/** | ||
* Subtract h,s,l from the current component values. | ||
* | ||
* @param {Number} h | ||
* @param {Number} s | ||
* @param {Number} l | ||
* @return {HSLA} new node | ||
* @api public | ||
*/ | ||
HSLA.prototype.sub = function(h,s,l){ | ||
return this.add(-h, -s, -l); | ||
}; | ||
sub(h, s, l) { | ||
return this.add(-h, -s, -l); | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
HSLA.prototype.operate = function(op, right){ | ||
switch (op) { | ||
case '==': | ||
case '!=': | ||
case '<=': | ||
case '>=': | ||
case '<': | ||
case '>': | ||
case 'is a': | ||
case '||': | ||
case '&&': | ||
return this.rgba.operate(op, right); | ||
default: | ||
return this.rgba.operate(op, right).hsla; | ||
} | ||
}; | ||
operate(op, right) { | ||
switch (op) { | ||
case '==': | ||
case '!=': | ||
case '<=': | ||
case '>=': | ||
case '<': | ||
case '>': | ||
case 'is a': | ||
case '||': | ||
case '&&': | ||
return this.rgba.operate(op, right); | ||
default: | ||
return this.rgba.operate(op, right).hsla; | ||
} | ||
}; | ||
/** | ||
* Return `HSLA` representation of the given `color`. | ||
* | ||
* @param {RGBA} color | ||
* @return {HSLA} | ||
* @api public | ||
*/ | ||
exports.fromRGBA = function(rgba){ | ||
var r = rgba.r / 255 | ||
, g = rgba.g / 255 | ||
, b = rgba.b / 255 | ||
, a = rgba.a; | ||
/** | ||
* Adjust lightness by `percent`. | ||
* | ||
* @param {Number} percent | ||
* @return {HSLA} for chaining | ||
* @api public | ||
*/ | ||
var min = Math.min(r,g,b) | ||
, max = Math.max(r,g,b) | ||
, l = (max + min) / 2 | ||
, d = max - min | ||
, h, s; | ||
adjustLightness(percent) { | ||
this.l = clampPercentage(this.l + this.l * (percent / 100)); | ||
return this; | ||
}; | ||
switch (max) { | ||
case min: h = 0; break; | ||
case r: h = 60 * (g-b) / d; break; | ||
case g: h = 60 * (b-r) / d + 120; break; | ||
case b: h = 60 * (r-g) / d + 240; break; | ||
} | ||
/** | ||
* Adjust hue by `deg`. | ||
* | ||
* @param {Number} deg | ||
* @return {HSLA} for chaining | ||
* @api public | ||
*/ | ||
if (max == min) { | ||
s = 0; | ||
} else if (l < .5) { | ||
s = d / (2 * l); | ||
} else { | ||
s = d / (2 - 2 * l); | ||
} | ||
adjustHue(deg) { | ||
this.h = clampDegrees(this.h + deg); | ||
return this; | ||
}; | ||
h %= 360; | ||
s *= 100; | ||
l *= 100; | ||
return new HSLA(h,s,l,a); | ||
}; | ||
/** | ||
* Return `HSLA` representation of the given `color`. | ||
* | ||
* @param {RGBA} color | ||
* @return {HSLA} | ||
* @api public | ||
*/ | ||
/** | ||
* Adjust lightness by `percent`. | ||
* | ||
* @param {Number} percent | ||
* @return {HSLA} for chaining | ||
* @api public | ||
*/ | ||
static fromRGBA(rgba) { | ||
var r = rgba.r / 255 | ||
, g = rgba.g / 255 | ||
, b = rgba.b / 255 | ||
, a = rgba.a; | ||
HSLA.prototype.adjustLightness = function(percent){ | ||
this.l = clampPercentage(this.l + this.l * (percent / 100)); | ||
return this; | ||
}; | ||
var min = Math.min(r, g, b) | ||
, max = Math.max(r, g, b) | ||
, l = (max + min) / 2 | ||
, d = max - min | ||
, h, s; | ||
/** | ||
* Adjust hue by `deg`. | ||
* | ||
* @param {Number} deg | ||
* @return {HSLA} for chaining | ||
* @api public | ||
*/ | ||
switch (max) { | ||
case min: h = 0; break; | ||
case r: h = 60 * (g - b) / d; break; | ||
case g: h = 60 * (b - r) / d + 120; break; | ||
case b: h = 60 * (r - g) / d + 240; break; | ||
} | ||
HSLA.prototype.adjustHue = function(deg){ | ||
this.h = clampDegrees(this.h + deg); | ||
return this; | ||
if (max == min) { | ||
s = 0; | ||
} else if (l < .5) { | ||
s = d / (2 * l); | ||
} else { | ||
s = d / (2 - 2 * l); | ||
} | ||
h %= 360; | ||
s *= 100; | ||
l *= 100; | ||
return new HSLA(h, s, l, a); | ||
}; | ||
}; | ||
@@ -242,0 +240,0 @@ |
@@ -15,143 +15,139 @@ | ||
/** | ||
* Initialize a new `Ident` by `name` with the given `val` node. | ||
* | ||
* @param {String} name | ||
* @param {Node} val | ||
* @api public | ||
*/ | ||
module.exports = class Ident extends Node { | ||
/** | ||
* Initialize a new `Ident` by `name` with the given `val` node. | ||
* | ||
* @param {String} name | ||
* @param {Node} val | ||
* @api public | ||
*/ | ||
var Ident = module.exports = function Ident(name, val, mixin){ | ||
Node.call(this); | ||
this.name = name; | ||
this.string = name; | ||
this.val = val || nodes.null; | ||
this.mixin = !!mixin; | ||
}; | ||
constructor(name, val, mixin) { | ||
super(); | ||
this.name = name; | ||
this.string = name; | ||
this.val = val || nodes.null; | ||
this.mixin = !!mixin; | ||
} | ||
/** | ||
* Check if the variable has a value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if the variable has a value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Ident.prototype.__defineGetter__('isEmpty', function(){ | ||
return undefined == this.val; | ||
}); | ||
get isEmpty() { | ||
return undefined == this.val; | ||
}; | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Ident.prototype.__defineGetter__('hash', function(){ | ||
return this.name; | ||
}); | ||
get hash() { | ||
return this.name; | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Ident.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Ident(this.name); | ||
clone.val = this.val.clone(parent, clone); | ||
clone.mixin = this.mixin; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.property = this.property; | ||
clone.rest = this.rest; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Ident.prototype.clone = function(parent){ | ||
var clone = new Ident(this.name); | ||
clone.val = this.val.clone(parent, clone); | ||
clone.mixin = this.mixin; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.property = this.property; | ||
clone.rest = this.rest; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Ident', | ||
name: this.name, | ||
val: this.val, | ||
mixin: this.mixin, | ||
property: this.property, | ||
rest: this.rest, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return <name>. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Ident.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Ident', | ||
name: this.name, | ||
val: this.val, | ||
mixin: this.mixin, | ||
property: this.property, | ||
rest: this.rest, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return this.name; | ||
}; | ||
}; | ||
/** | ||
* Return <name>. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Coerce `other` to an ident. | ||
* | ||
* @param {Node} other | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Ident.prototype.toString = function(){ | ||
return this.name; | ||
}; | ||
coerce(other) { | ||
switch (other.nodeName) { | ||
case 'ident': | ||
case 'string': | ||
case 'literal': | ||
return new Ident(other.string); | ||
case 'unit': | ||
return new Ident(other.toString()); | ||
default: | ||
return super.coerce(other); | ||
} | ||
}; | ||
/** | ||
* Coerce `other` to an ident. | ||
* | ||
* @param {Node} other | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Ident.prototype.coerce = function(other){ | ||
switch (other.nodeName) { | ||
case 'ident': | ||
case 'string': | ||
case 'literal': | ||
return new Ident(other.string); | ||
case 'unit': | ||
return new Ident(other.toString()); | ||
default: | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
operate(op, right) { | ||
var val = right.first; | ||
switch (op) { | ||
case '-': | ||
if ('unit' == val.nodeName) { | ||
var expr = new nodes.Expression; | ||
val = val.clone(); | ||
val.val = -val.val; | ||
expr.push(this); | ||
expr.push(val); | ||
return expr; | ||
} | ||
case '+': | ||
return new nodes.Ident(this.string + this.coerce(val).string); | ||
} | ||
return super.operate(op, right); | ||
}; | ||
}; | ||
/** | ||
* 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.clone(); | ||
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); | ||
}; |
@@ -14,66 +14,62 @@ | ||
/** | ||
* Initialize a new `If` with the given `cond`. | ||
* | ||
* @param {Expression} cond | ||
* @param {Boolean|Block} negate, block | ||
* @api public | ||
*/ | ||
module.exports = class If extends Node { | ||
/** | ||
* Initialize a new `If` with the given `cond`. | ||
* | ||
* @param {Expression} cond | ||
* @param {Boolean|Block} negate, block | ||
* @api public | ||
*/ | ||
var If = module.exports = function If(cond, negate){ | ||
Node.call(this); | ||
this.cond = cond; | ||
this.elses = []; | ||
if (negate && negate.nodeName) { | ||
this.block = negate; | ||
} else { | ||
this.negate = negate; | ||
constructor(cond, negate) { | ||
super(); | ||
this.cond = cond; | ||
this.elses = []; | ||
if (negate && negate.nodeName) { | ||
this.block = negate; | ||
} else { | ||
this.negate = negate; | ||
} | ||
} | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
If.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new If(); | ||
clone.cond = this.cond.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.elses = this.elses.map(function (node) { return node.clone(parent, clone); }); | ||
clone.negate = this.negate; | ||
clone.postfix = this.postfix; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
If.prototype.clone = function(parent){ | ||
var clone = new If(); | ||
clone.cond = this.cond.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.elses = this.elses.map(function(node){ return node.clone(parent, clone); }); | ||
clone.negate = this.negate; | ||
clone.postfix = this.postfix; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
If.prototype.toJSON = function(){ | ||
return { | ||
__type: 'If', | ||
cond: this.cond, | ||
block: this.block, | ||
elses: this.elses, | ||
negate: this.negate, | ||
postfix: this.postfix, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'If', | ||
cond: this.cond, | ||
block: this.block, | ||
elses: this.elses, | ||
negate: this.negate, | ||
postfix: this.postfix, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -14,56 +14,53 @@ | ||
/** | ||
* Initialize a new `Import` with the given `expr`. | ||
* | ||
* @param {Expression} expr | ||
* @api public | ||
*/ | ||
module.exports = class Import extends Node { | ||
/** | ||
* Initialize a new `Import` with the given `expr`. | ||
* | ||
* @param {Expression} expr | ||
* @api public | ||
*/ | ||
var Import = module.exports = function Import(expr, once){ | ||
Node.call(this); | ||
this.path = expr; | ||
this.once = once || false; | ||
}; | ||
constructor(expr, once) { | ||
super(); | ||
this.path = expr; | ||
this.once = once || false; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Import.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Import(); | ||
clone.path = this.path.nodeName ? this.path.clone(parent, clone) : this.path; | ||
clone.once = this.once; | ||
clone.mtime = this.mtime; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Import.prototype.clone = function(parent){ | ||
var clone = new Import(); | ||
clone.path = this.path.nodeName ? this.path.clone(parent, clone) : this.path; | ||
clone.once = this.once; | ||
clone.mtime = this.mtime; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Import', | ||
path: this.path, | ||
once: this.once, | ||
mtime: this.mtime, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Import.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Import', | ||
path: this.path, | ||
once: this.once, | ||
mtime: this.mtime, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -14,69 +14,66 @@ | ||
/** | ||
* Initialize a new `Keyframes` with the given `segs`, | ||
* and optional vendor `prefix`. | ||
* | ||
* @param {Array} segs | ||
* @param {String} prefix | ||
* @api public | ||
*/ | ||
module.exports = class Keyframes extends Atrule { | ||
/** | ||
* Initialize a new `Keyframes` with the given `segs`, | ||
* and optional vendor `prefix`. | ||
* | ||
* @param {Array} segs | ||
* @param {String} prefix | ||
* @api public | ||
*/ | ||
var Keyframes = module.exports = function Keyframes(segs, prefix){ | ||
Atrule.call(this, 'keyframes'); | ||
this.segments = segs; | ||
this.prefix = prefix || 'official'; | ||
}; | ||
constructor(segs, prefix) { | ||
super('keyframes') | ||
this.segments = segs; | ||
this.prefix = prefix || 'official'; | ||
} | ||
/** | ||
* Inherit from `Atrule.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Keyframes.prototype.__proto__ = Atrule.prototype; | ||
clone(parent) { | ||
var clone = new Keyframes; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); | ||
clone.prefix = this.prefix; | ||
clone.block = this.block.clone(parent, clone); | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Keyframes.prototype.clone = function(parent){ | ||
var clone = new Keyframes; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.segments = this.segments.map(function(node) { return node.clone(parent, clone); }); | ||
clone.prefix = this.prefix; | ||
clone.block = this.block.clone(parent, clone); | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Keyframes', | ||
segments: this.segments, | ||
prefix: this.prefix, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return `@keyframes name`. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Keyframes.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Keyframes', | ||
segments: this.segments, | ||
prefix: this.prefix, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return '@keyframes ' + this.segments.join(''); | ||
}; | ||
}; | ||
/** | ||
* Return `@keyframes name`. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Keyframes.prototype.toString = function(){ | ||
return '@keyframes ' + this.segments.join(''); | ||
}; | ||
}; |
@@ -15,99 +15,96 @@ | ||
/** | ||
* Initialize a new `Literal` with the given `str`. | ||
* | ||
* @param {String} str | ||
* @api public | ||
*/ | ||
module.exports = class Literal extends Node { | ||
/** | ||
* Initialize a new `Literal` with the given `str`. | ||
* | ||
* @param {String} str | ||
* @api public | ||
*/ | ||
var Literal = module.exports = function Literal(str){ | ||
Node.call(this); | ||
this.val = str; | ||
this.string = str; | ||
this.prefixed = false; | ||
}; | ||
constructor(str) { | ||
super(); | ||
this.val = str; | ||
this.string = str; | ||
this.prefixed = false; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Literal.prototype.__proto__ = Node.prototype; | ||
get hash() { | ||
return this.val; | ||
}; | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return literal value. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Literal.prototype.__defineGetter__('hash', function(){ | ||
return this.val; | ||
}); | ||
toString() { | ||
return this.val.toString(); | ||
}; | ||
/** | ||
* Return literal value. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Coerce `other` to a literal. | ||
* | ||
* @param {Node} other | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Literal.prototype.toString = function(){ | ||
return this.val.toString(); | ||
}; | ||
coerce(other) { | ||
switch (other.nodeName) { | ||
case 'ident': | ||
case 'string': | ||
case 'literal': | ||
return new Literal(other.string); | ||
default: | ||
return super.coerce(other); | ||
} | ||
}; | ||
/** | ||
* Coerce `other` to a literal. | ||
* | ||
* @param {Node} other | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Literal.prototype.coerce = function(other){ | ||
switch (other.nodeName) { | ||
case 'ident': | ||
case 'string': | ||
case 'literal': | ||
return new Literal(other.string); | ||
default: | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
}; | ||
operate(op, right) { | ||
var val = right.first; | ||
switch (op) { | ||
case '+': | ||
return new nodes.Literal(this.string + this.coerce(val).string); | ||
default: | ||
return super.operate(op, right); | ||
} | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @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); | ||
} | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Literal', | ||
val: this.val, | ||
string: this.string, | ||
prefixed: this.prefixed, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Literal.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Literal', | ||
val: this.val, | ||
string: this.string, | ||
prefixed: this.prefixed, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -14,64 +14,60 @@ | ||
/** | ||
* Initialize a new `Media` with the given `val` | ||
* | ||
* @param {String} val | ||
* @api public | ||
*/ | ||
module.exports = class Media extends Atrule { | ||
/** | ||
* Initialize a new `Media` with the given `val` | ||
* | ||
* @param {String} val | ||
* @api public | ||
*/ | ||
var Media = module.exports = function Media(val){ | ||
Atrule.call(this, 'media'); | ||
this.val = val; | ||
}; | ||
constructor(val) { | ||
super('media'); | ||
this.val = val; | ||
} | ||
/** | ||
* Inherit from `Atrule.prototype`. | ||
*/ | ||
/** | ||
* Clone this node. | ||
* | ||
* @return {Media} | ||
* @api public | ||
*/ | ||
Media.prototype.__proto__ = Atrule.prototype; | ||
clone(parent) { | ||
var clone = new Media; | ||
clone.val = this.val.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Clone this node. | ||
* | ||
* @return {Media} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Media.prototype.clone = function(parent){ | ||
var clone = new Media; | ||
clone.val = this.val.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Media', | ||
val: this.val, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return @media "val". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Media.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Media', | ||
val: this.val, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return '@media ' + this.val; | ||
}; | ||
}; | ||
/** | ||
* Return @media "val". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Media.prototype.toString = function(){ | ||
return '@media ' + this.val; | ||
}; |
@@ -14,70 +14,66 @@ | ||
/** | ||
* Initialize a new `Member` with `left` and `right`. | ||
* | ||
* @param {Node} left | ||
* @param {Node} right | ||
* @api public | ||
*/ | ||
module.exports = class Member extends Node { | ||
/** | ||
* Initialize a new `Member` with `left` and `right`. | ||
* | ||
* @param {Node} left | ||
* @param {Node} right | ||
* @api public | ||
*/ | ||
var Member = module.exports = function Member(left, right){ | ||
Node.call(this); | ||
this.left = left; | ||
this.right = right; | ||
}; | ||
constructor(left, right) { | ||
super(); | ||
this.left = left; | ||
this.right = right; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Member.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Member; | ||
clone.left = this.left.clone(parent, clone); | ||
clone.right = this.right.clone(parent, clone); | ||
if (this.val) clone.val = this.val.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Member.prototype.clone = function(parent){ | ||
var clone = new Member; | ||
clone.left = this.left.clone(parent, clone); | ||
clone.right = this.right.clone(parent, clone); | ||
if (this.val) clone.val = this.val.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toJSON() { | ||
var json = { | ||
__type: 'Member', | ||
left: this.left, | ||
right: this.right, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.val) json.val = this.val; | ||
return json; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a string representation of this node. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Member.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'Member', | ||
left: this.left, | ||
right: this.right, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return this.left.toString() | ||
+ '.' + this.right.toString(); | ||
}; | ||
if (this.val) json.val = this.val; | ||
return json; | ||
}; | ||
/** | ||
* Return a string representation of this node. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Member.prototype.toString = function(){ | ||
return this.left.toString() | ||
+ '.' + this.right.toString(); | ||
}; |
@@ -13,49 +13,45 @@ /*! | ||
/** | ||
* Initialize a new `Namespace` with the given `val` and `prefix` | ||
* | ||
* @param {String|Call} val | ||
* @param {String} [prefix] | ||
* @api public | ||
*/ | ||
module.exports = class Namespace extends Node { | ||
/** | ||
* Initialize a new `Namespace` with the given `val` and `prefix` | ||
* | ||
* @param {String|Call} val | ||
* @param {String} [prefix] | ||
* @api public | ||
*/ | ||
var Namespace = module.exports = function Namespace(val, prefix){ | ||
Node.call(this); | ||
this.val = val; | ||
this.prefix = prefix; | ||
}; | ||
constructor(val, prefix) { | ||
super(); | ||
this.val = val; | ||
this.prefix = prefix; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return @namespace "val". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Namespace.prototype.__proto__ = Node.prototype; | ||
toString() { | ||
return '@namespace ' + (this.prefix ? this.prefix + ' ' : '') + this.val; | ||
}; | ||
/** | ||
* Return @namespace "val". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Namespace.prototype.toString = function(){ | ||
return '@namespace ' + (this.prefix ? this.prefix + ' ' : '') + this.val; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Namespace.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Namespace', | ||
val: this.val, | ||
prefix: this.prefix, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Namespace', | ||
val: this.val, | ||
prefix: this.prefix, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -16,38 +16,35 @@ | ||
/** | ||
* Initialize a new `CoercionError` with the given `msg`. | ||
* | ||
* @param {String} msg | ||
* @api private | ||
*/ | ||
class CoercionError extends Error { | ||
/** | ||
* Initialize a new `CoercionError` with the given `msg`. | ||
* | ||
* @param {String} msg | ||
* @api private | ||
*/ | ||
function CoercionError(msg) { | ||
this.name = 'CoercionError' | ||
this.message = msg | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, CoercionError); | ||
constructor(msg) { | ||
super(); | ||
this.name = 'CoercionError' | ||
this.message = msg | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, CoercionError); | ||
} | ||
} | ||
} | ||
/** | ||
* Inherit from `Error.prototype`. | ||
*/ | ||
CoercionError.prototype.__proto__ = Error.prototype; | ||
/** | ||
* Node constructor. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Node { | ||
/** | ||
* Node constructor. | ||
* | ||
* @api public | ||
*/ | ||
var Node = module.exports = function Node(){ | ||
this.lineno = nodes.lineno || 1; | ||
this.column = nodes.column || 1; | ||
this.filename = nodes.filename; | ||
}; | ||
constructor() { | ||
this.lineno = nodes.lineno || 1; | ||
this.column = nodes.column || 1; | ||
this.filename = nodes.filename; | ||
} | ||
Node.prototype = { | ||
constructor: Node, | ||
/** | ||
@@ -62,3 +59,3 @@ * Return this node. | ||
return this; | ||
}, | ||
} | ||
@@ -74,3 +71,3 @@ /** | ||
return this.val; | ||
}, | ||
} | ||
@@ -86,3 +83,3 @@ /** | ||
return this.constructor.name.toLowerCase(); | ||
}, | ||
} | ||
@@ -96,5 +93,5 @@ /** | ||
clone: function(){ | ||
clone() { | ||
return this; | ||
}, | ||
} | ||
@@ -108,3 +105,3 @@ /** | ||
toJSON: function(){ | ||
toJSON() { | ||
return { | ||
@@ -115,3 +112,3 @@ lineno: this.lineno, | ||
}; | ||
}, | ||
} | ||
@@ -125,5 +122,5 @@ /** | ||
eval: function(){ | ||
eval() { | ||
return new Evaluator(this).evaluate(); | ||
}, | ||
} | ||
@@ -137,5 +134,5 @@ /** | ||
toBoolean: function(){ | ||
toBoolean() { | ||
return nodes.true; | ||
}, | ||
} | ||
@@ -149,3 +146,3 @@ /** | ||
toExpression: function(){ | ||
toExpression() { | ||
if ('expression' == this.nodeName) return this; | ||
@@ -155,3 +152,3 @@ var expr = new nodes.Expression; | ||
return expr; | ||
}, | ||
} | ||
@@ -166,3 +163,3 @@ /** | ||
shouldCoerce: function(op){ | ||
shouldCoerce(op) { | ||
switch (op) { | ||
@@ -177,3 +174,3 @@ case 'is a': | ||
} | ||
}, | ||
} | ||
@@ -189,7 +186,7 @@ /** | ||
operate: function(op, right){ | ||
operate(op, right) { | ||
switch (op) { | ||
case 'is a': | ||
if ('string' == right.first.nodeName) { | ||
return nodes.Boolean(this.nodeName == right.val); | ||
return new nodes.Boolean(this.nodeName == right.val); | ||
} else { | ||
@@ -199,13 +196,13 @@ throw new Error('"is a" expects a string, got ' + right.toString()); | ||
case '==': | ||
return nodes.Boolean(this.hash == right.hash); | ||
return new nodes.Boolean(this.hash == right.hash); | ||
case '!=': | ||
return nodes.Boolean(this.hash != right.hash); | ||
return new nodes.Boolean(this.hash != right.hash); | ||
case '>=': | ||
return nodes.Boolean(this.hash >= right.hash); | ||
return new nodes.Boolean(this.hash >= right.hash); | ||
case '<=': | ||
return nodes.Boolean(this.hash <= right.hash); | ||
return new nodes.Boolean(this.hash <= right.hash); | ||
case '>': | ||
return nodes.Boolean(this.hash > right.hash); | ||
return new nodes.Boolean(this.hash > right.hash); | ||
case '<': | ||
return nodes.Boolean(this.hash < right.hash); | ||
return new nodes.Boolean(this.hash < right.hash); | ||
case '||': | ||
@@ -223,3 +220,3 @@ return this.toBoolean().isTrue | ||
if (1 == len && 'object' == vals[0].nodeName) { | ||
return nodes.Boolean(vals[0].has(this.hash)); | ||
return new nodes.Boolean(vals[0].has(this.hash)); | ||
} | ||
@@ -254,3 +251,3 @@ | ||
} | ||
}, | ||
} | ||
@@ -265,3 +262,3 @@ /** | ||
coerce: function(other){ | ||
coerce(other) { | ||
if (other.nodeName == this.nodeName) return other; | ||
@@ -271,1 +268,2 @@ throw new CoercionError('cannot coerce ' + other + ' to ' + this.nodeName); | ||
}; | ||
@@ -21,11 +21,4 @@ | ||
var Null = module.exports = function Null(){}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
Null.prototype.__proto__ = Node.prototype; | ||
/** | ||
module.exports = class Null extends Node { | ||
/** | ||
* Return 'Null'. | ||
@@ -37,54 +30,58 @@ * | ||
Null.prototype.inspect = | ||
Null.prototype.toString = function(){ | ||
return 'null'; | ||
}; | ||
toString() { | ||
return 'null'; | ||
}; | ||
/** | ||
* Return false. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
inspect() { | ||
return 'null'; | ||
} | ||
Null.prototype.toBoolean = function(){ | ||
return nodes.false; | ||
}; | ||
/** | ||
* Return false. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if the node is a null node. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
toBoolean() { | ||
return nodes.false; | ||
}; | ||
Null.prototype.__defineGetter__('isNull', function(){ | ||
return true; | ||
}); | ||
/** | ||
* Check if the node is a null node. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
get isNull() { | ||
return true; | ||
}; | ||
Null.prototype.__defineGetter__('hash', function(){ | ||
return null; | ||
}); | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
get hash() { | ||
return null; | ||
}; | ||
Null.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Null', | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
toJSON() { | ||
return { | ||
__type: 'Null', | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -16,230 +16,230 @@ | ||
/** | ||
* Initialize a new `Object`. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Object extends Node { | ||
/** | ||
* Initialize a new `Object`. | ||
* | ||
* @api public | ||
*/ | ||
var Object = module.exports = function Object(){ | ||
Node.call(this); | ||
this.vals = {}; | ||
this.keys = {}; | ||
}; | ||
constructor() { | ||
super(); | ||
this.vals = {}; | ||
this.keys = {}; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Set `key` to `val`. | ||
* | ||
* @param {String} key | ||
* @param {Node} val | ||
* @return {Object} for chaining | ||
* @api public | ||
*/ | ||
Object.prototype.__proto__ = Node.prototype; | ||
setValue(key, val) { | ||
this.vals[key] = val; | ||
return this; | ||
}; | ||
/** | ||
* Set `key` to `val`. | ||
* | ||
* @param {String} key | ||
* @param {Node} val | ||
* @return {Object} for chaining | ||
* @api public | ||
*/ | ||
/** | ||
* Alias for `setValue` for compatible API | ||
*/ | ||
Object.prototype.setValue = function(key, val){ | ||
this.vals[key] = val; | ||
return this; | ||
}; | ||
get set() { | ||
return this.setValue; | ||
} | ||
/** | ||
* Alias for `setValue` for compatible API | ||
*/ | ||
Object.prototype.set = Object.prototype.setValue; | ||
/** | ||
* Set `key` to `val`. | ||
* | ||
* @param {String} key | ||
* @param {Node} val | ||
* @return {Object} for chaining | ||
* @api public | ||
*/ | ||
/** | ||
* Set `key` to `val`. | ||
* | ||
* @param {String} key | ||
* @param {Node} val | ||
* @return {Object} for chaining | ||
* @api public | ||
*/ | ||
setKey(key, val) { | ||
this.keys[key] = val; | ||
return this; | ||
}; | ||
Object.prototype.setKey = function(key, val){ | ||
this.keys[key] = val; | ||
return this; | ||
}; | ||
/** | ||
* Return length. | ||
* | ||
* @return {Number} | ||
* @api public | ||
*/ | ||
/** | ||
* Return length. | ||
* | ||
* @return {Number} | ||
* @api public | ||
*/ | ||
get length() { | ||
return nativeObj.keys(this.vals).length; | ||
}; | ||
Object.prototype.__defineGetter__('length', function() { | ||
return nativeObj.keys(this.vals).length; | ||
}); | ||
/** | ||
* Get `key`. | ||
* | ||
* @param {String} key | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Get `key`. | ||
* | ||
* @param {String} key | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
get(key) { | ||
return this.vals[key] || nodes.null; | ||
}; | ||
Object.prototype.get = function(key){ | ||
return this.vals[key] || nodes.null; | ||
}; | ||
/** | ||
* Has `key`? | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Has `key`? | ||
* | ||
* @param {String} key | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
has(key) { | ||
return key in this.vals; | ||
}; | ||
Object.prototype.has = function(key){ | ||
return key in this.vals; | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Object.prototype.operate = function(op, right){ | ||
switch (op) { | ||
case '.': | ||
case '[]': | ||
return this.get(right.hash); | ||
case '==': | ||
var vals = this.vals | ||
, a | ||
, b; | ||
if ('object' != right.nodeName || this.length != right.length) | ||
return nodes.false; | ||
for (var key in vals) { | ||
a = vals[key]; | ||
b = right.vals[key]; | ||
if (a.operate(op, b).isFalse) | ||
operate(op, right) { | ||
switch (op) { | ||
case '.': | ||
case '[]': | ||
return this.get(right.hash); | ||
case '==': | ||
var vals = this.vals | ||
, a | ||
, b; | ||
if ('object' != right.nodeName || this.length != right.length) | ||
return nodes.false; | ||
} | ||
return nodes.true; | ||
case '!=': | ||
return this.operate('==', right).negate(); | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
} | ||
}; | ||
for (var key in vals) { | ||
a = vals[key]; | ||
b = right.vals[key]; | ||
if (a.operate(op, b).isFalse) | ||
return nodes.false; | ||
} | ||
return nodes.true; | ||
case '!=': | ||
return this.operate('==', right).negate(); | ||
default: | ||
return super.operate.call(op, right); | ||
} | ||
}; | ||
/** | ||
* Return Boolean based on the length of this object. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return Boolean based on the length of this object. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Object.prototype.toBoolean = function(){ | ||
return nodes.Boolean(this.length); | ||
}; | ||
toBoolean() { | ||
return new nodes.Boolean(this.length); | ||
}; | ||
/** | ||
* Convert object to string with properties. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Convert object to string with properties. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Object.prototype.toBlock = function(){ | ||
var str = '{' | ||
, key | ||
, val; | ||
toBlock() { | ||
var str = '{' | ||
, key | ||
, val; | ||
for (key in this.vals) { | ||
val = this.get(key); | ||
if ('object' == val.first.nodeName) { | ||
str += key + ' ' + val.first.toBlock(); | ||
} else { | ||
switch (key) { | ||
case '@charset': | ||
str += key + ' ' + val.first.toString() + ';'; | ||
break; | ||
default: | ||
str += key + ':' + toString(val) + ';'; | ||
for (key in this.vals) { | ||
val = this.get(key); | ||
if ('object' == val.first.nodeName) { | ||
str += key + ' ' + val.first.toBlock(); | ||
} else { | ||
switch (key) { | ||
case '@charset': | ||
str += key + ' ' + val.first.toString() + ';'; | ||
break; | ||
default: | ||
str += key + ':' + toString(val) + ';'; | ||
} | ||
} | ||
} | ||
} | ||
str += '}'; | ||
str += '}'; | ||
return str; | ||
return str; | ||
function toString(node) { | ||
if (node.nodes) { | ||
return node.nodes.map(toString).join(node.isList ? ',' : ' '); | ||
} else if ('literal' == node.nodeName && ',' == node.val) { | ||
return '\\,'; | ||
function toString(node) { | ||
if (node.nodes) { | ||
return node.nodes.map(toString).join(node.isList ? ',' : ' '); | ||
} else if ('literal' == node.nodeName && ',' == node.val) { | ||
return '\\,'; | ||
} | ||
return node.toString(); | ||
} | ||
return node.toString(); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Object.prototype.clone = function(parent){ | ||
var clone = new Object; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone(parent) { | ||
var clone = new Object; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
var key; | ||
for (key in this.vals) { | ||
clone.vals[key] = this.vals[key].clone(parent, clone); | ||
} | ||
var key; | ||
for (key in this.vals) { | ||
clone.vals[key] = this.vals[key].clone(parent, clone); | ||
} | ||
for (key in this.keys) { | ||
clone.keys[key] = this.keys[key].clone(parent, clone); | ||
} | ||
for (key in this.keys) { | ||
clone.keys[key] = this.keys[key].clone(parent, clone); | ||
} | ||
return clone; | ||
}; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Object.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Object', | ||
vals: this.vals, | ||
keys: this.keys, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Object', | ||
vals: this.vals, | ||
keys: this.keys, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Return "{ <prop>: <val> }" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return "{ <prop>: <val> }" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Object.prototype.toString = function(){ | ||
var obj = {}; | ||
for (var prop in this.vals) { | ||
obj[prop] = this.vals[prop].toString(); | ||
} | ||
return JSON.stringify(obj); | ||
toString() { | ||
var obj = {}; | ||
for (var prop in this.vals) { | ||
obj[prop] = this.vals[prop].toString(); | ||
} | ||
return JSON.stringify(obj); | ||
}; | ||
}; |
@@ -14,78 +14,73 @@ | ||
/** | ||
* Initialize a new `Params` with `name`, `params`, and `body`. | ||
* | ||
* @param {String} name | ||
* @param {Params} params | ||
* @param {Expression} body | ||
* @api public | ||
*/ | ||
module.exports = class Params extends Node { | ||
/** | ||
* Initialize a new `Params` with `name`, `params`, and `body`. | ||
* | ||
* @param {String} name | ||
* @param {Params} params | ||
* @param {Expression} body | ||
* @api public | ||
*/ | ||
var Params = module.exports = function Params(){ | ||
Node.call(this); | ||
this.nodes = []; | ||
}; | ||
constructor() { | ||
super(); | ||
this.nodes = []; | ||
} | ||
/** | ||
* Check function arity. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Check function arity. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Params.prototype.__defineGetter__('length', function(){ | ||
return this.nodes.length; | ||
}); | ||
get length() { | ||
return this.nodes.length; | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Push the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
Params.prototype.__proto__ = Node.prototype; | ||
push(node) { | ||
this.nodes.push(node); | ||
}; | ||
/** | ||
* Push the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Params.prototype.push = function(node){ | ||
this.nodes.push(node); | ||
}; | ||
clone(parent) { | ||
var clone = new Params; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
this.nodes.forEach(function (node) { | ||
clone.push(node.clone(parent, clone)); | ||
}); | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Params.prototype.clone = function(parent){ | ||
var clone = new Params; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
this.nodes.forEach(function(node){ | ||
clone.push(node.clone(parent, clone)); | ||
}); | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Params.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Params', | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Params', | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; | ||
@@ -14,84 +14,80 @@ | ||
/** | ||
* Initialize a new `Property` with the given `segs` and optional `expr`. | ||
* | ||
* @param {Array} segs | ||
* @param {Expression} expr | ||
* @api public | ||
*/ | ||
module.exports = class Property extends Node { | ||
/** | ||
* Initialize a new `Property` with the given `segs` and optional `expr`. | ||
* | ||
* @param {Array} segs | ||
* @param {Expression} expr | ||
* @api public | ||
*/ | ||
var Property = module.exports = function Property(segs, expr){ | ||
Node.call(this); | ||
this.segments = segs; | ||
this.expr = expr; | ||
}; | ||
constructor(segs, expr) { | ||
super(); | ||
this.segments = segs; | ||
this.expr = expr; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Property.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Property(this.segments); | ||
clone.name = this.name; | ||
if (this.literal) clone.literal = this.literal; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); | ||
if (this.expr) clone.expr = this.expr.clone(parent, clone); | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Property.prototype.clone = function(parent){ | ||
var clone = new Property(this.segments); | ||
clone.name = this.name; | ||
if (this.literal) clone.literal = this.literal; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); | ||
if (this.expr) clone.expr = this.expr.clone(parent, clone); | ||
return clone; | ||
}; | ||
toJSON() { | ||
var json = { | ||
__type: 'Property', | ||
segments: this.segments, | ||
name: this.name, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
if (this.expr) json.expr = this.expr; | ||
if (this.literal) json.literal = this.literal; | ||
return json; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return string representation of this node. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Property.prototype.toJSON = function(){ | ||
var json = { | ||
__type: 'Property', | ||
segments: this.segments, | ||
name: this.name, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return 'property(' + this.segments.join('') + ', ' + this.expr + ')'; | ||
}; | ||
if (this.expr) json.expr = this.expr; | ||
if (this.literal) json.literal = this.literal; | ||
return json; | ||
}; | ||
/** | ||
* Return string representation of this node. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on the property expression. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Property.prototype.toString = function(){ | ||
return 'property(' + this.segments.join('') + ', ' + this.expr + ')'; | ||
operate(op, right, val) { | ||
return this.expr.operate(op, right, val); | ||
}; | ||
}; | ||
/** | ||
* 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); | ||
}; |
@@ -14,96 +14,93 @@ | ||
/** | ||
* Initialize a new `QueryList`. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class QueryList extends Node { | ||
/** | ||
* Initialize a new `QueryList`. | ||
* | ||
* @api public | ||
*/ | ||
var QueryList = module.exports = function QueryList(){ | ||
Node.call(this); | ||
this.nodes = []; | ||
}; | ||
constructor() { | ||
super(); | ||
this.nodes = []; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
QueryList.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new QueryList; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
for (var i = 0; i < this.nodes.length; ++i) { | ||
clone.push(this.nodes[i].clone(parent, clone)); | ||
} | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Push the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
QueryList.prototype.clone = function(parent){ | ||
var clone = new QueryList; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
for (var i = 0; i < this.nodes.length; ++i) { | ||
clone.push(this.nodes[i].clone(parent, clone)); | ||
} | ||
return clone; | ||
}; | ||
push(node) { | ||
this.nodes.push(node); | ||
}; | ||
/** | ||
* Push the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
/** | ||
* Merges this query list with the `other`. | ||
* | ||
* @param {QueryList} other | ||
* @return {QueryList} | ||
* @api private | ||
*/ | ||
QueryList.prototype.push = function(node){ | ||
this.nodes.push(node); | ||
}; | ||
merge(other) { | ||
var list = new QueryList | ||
, merged; | ||
this.nodes.forEach(function (query) { | ||
for (var i = 0, len = other.nodes.length; i < len; ++i) { | ||
merged = query.merge(other.nodes[i]); | ||
if (merged) list.push(merged); | ||
} | ||
}); | ||
return list; | ||
}; | ||
/** | ||
* Merges this query list with the `other`. | ||
* | ||
* @param {QueryList} other | ||
* @return {QueryList} | ||
* @api private | ||
*/ | ||
/** | ||
* Return "<a>, <b>, <c>" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
QueryList.prototype.merge = function(other){ | ||
var list = new QueryList | ||
, merged; | ||
this.nodes.forEach(function(query){ | ||
for (var i = 0, len = other.nodes.length; i < len; ++i){ | ||
merged = query.merge(other.nodes[i]); | ||
if (merged) list.push(merged); | ||
} | ||
}); | ||
return list; | ||
}; | ||
toString() { | ||
return '(' + this.nodes.map(function (node) { | ||
return node.toString(); | ||
}).join(', ') + ')'; | ||
}; | ||
/** | ||
* Return "<a>, <b>, <c>" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
QueryList.prototype.toString = function(){ | ||
return '(' + this.nodes.map(function(node){ | ||
return node.toString(); | ||
}).join(', ') + ')'; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'QueryList', | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
QueryList.prototype.toJSON = function(){ | ||
return { | ||
__type: 'QueryList', | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -14,158 +14,154 @@ | ||
/** | ||
* Initialize a new `Query`. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Query extends Node { | ||
/** | ||
* Initialize a new `Query`. | ||
* | ||
* @api public | ||
*/ | ||
var Query = module.exports = function Query(){ | ||
Node.call(this); | ||
this.nodes = []; | ||
this.type = ''; | ||
this.predicate = ''; | ||
}; | ||
constructor() { | ||
super(); | ||
this.nodes = []; | ||
this.type = ''; | ||
this.predicate = ''; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Query.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Query; | ||
clone.predicate = this.predicate; | ||
clone.type = this.type; | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
clone.push(this.nodes[i].clone(parent, clone)); | ||
} | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Push the given `feature`. | ||
* | ||
* @param {Feature} feature | ||
* @api public | ||
*/ | ||
Query.prototype.clone = function(parent){ | ||
var clone = new Query; | ||
clone.predicate = this.predicate; | ||
clone.type = this.type; | ||
for (var i = 0, len = this.nodes.length; i < len; ++i) { | ||
clone.push(this.nodes[i].clone(parent, clone)); | ||
} | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
push(feature) { | ||
this.nodes.push(feature); | ||
}; | ||
/** | ||
* Push the given `feature`. | ||
* | ||
* @param {Feature} feature | ||
* @api public | ||
*/ | ||
/** | ||
* Return resolved type of this query. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Query.prototype.push = function(feature){ | ||
this.nodes.push(feature); | ||
}; | ||
get resolvedType() { | ||
if (this.type) { | ||
return this.type.nodeName | ||
? this.type.string | ||
: this.type; | ||
} | ||
}; | ||
/** | ||
* Return resolved type of this query. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Return resolved predicate of this query. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Query.prototype.__defineGetter__('resolvedType', function(){ | ||
if (this.type) { | ||
return this.type.nodeName | ||
? this.type.string | ||
: this.type; | ||
} | ||
}); | ||
get resolvedPredicate() { | ||
if (this.predicate) { | ||
return this.predicate.nodeName | ||
? this.predicate.string | ||
: this.predicate; | ||
} | ||
}; | ||
/** | ||
* Return resolved predicate of this query. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Merges this query with the `other`. | ||
* | ||
* @param {Query} other | ||
* @return {Query} | ||
* @api private | ||
*/ | ||
Query.prototype.__defineGetter__('resolvedPredicate', function(){ | ||
if (this.predicate) { | ||
return this.predicate.nodeName | ||
? this.predicate.string | ||
: this.predicate; | ||
} | ||
}); | ||
merge(other) { | ||
var query = new Query | ||
, p1 = this.resolvedPredicate | ||
, p2 = other.resolvedPredicate | ||
, t1 = this.resolvedType | ||
, t2 = other.resolvedType | ||
, type, pred; | ||
/** | ||
* Merges this query with the `other`. | ||
* | ||
* @param {Query} other | ||
* @return {Query} | ||
* @api private | ||
*/ | ||
// Stolen from Sass :D | ||
t1 = t1 || t2; | ||
t2 = t2 || t1; | ||
if (('not' == p1) ^ ('not' == p2)) { | ||
if (t1 == t2) return; | ||
type = ('not' == p1) ? t2 : t1; | ||
pred = ('not' == p1) ? p2 : p1; | ||
} else if (('not' == p1) && ('not' == p2)) { | ||
if (t1 != t2) return; | ||
type = t1; | ||
pred = 'not'; | ||
} else if (t1 != t2) { | ||
return; | ||
} else { | ||
type = t1; | ||
pred = p1 || p2; | ||
} | ||
query.predicate = pred; | ||
query.type = type; | ||
query.nodes = this.nodes.concat(other.nodes); | ||
return query; | ||
}; | ||
Query.prototype.merge = function(other){ | ||
var query = new Query | ||
, p1 = this.resolvedPredicate | ||
, p2 = other.resolvedPredicate | ||
, t1 = this.resolvedType | ||
, t2 = other.resolvedType | ||
, type, pred; | ||
/** | ||
* Return "<a> and <b> and <c>" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
// Stolen from Sass :D | ||
t1 = t1 || t2; | ||
t2 = t2 || t1; | ||
if (('not' == p1) ^ ('not' == p2)) { | ||
if (t1 == t2) return; | ||
type = ('not' == p1) ? t2 : t1; | ||
pred = ('not' == p1) ? p2 : p1; | ||
} else if (('not' == p1) && ('not' == p2)) { | ||
if (t1 != t2) return; | ||
type = t1; | ||
pred = 'not'; | ||
} else if (t1 != t2) { | ||
return; | ||
} else { | ||
type = t1; | ||
pred = p1 || p2; | ||
} | ||
query.predicate = pred; | ||
query.type = type; | ||
query.nodes = this.nodes.concat(other.nodes); | ||
return query; | ||
}; | ||
toString() { | ||
var pred = this.predicate ? this.predicate + ' ' : '' | ||
, type = this.type || '' | ||
, len = this.nodes.length | ||
, str = pred + type; | ||
if (len) { | ||
str += (type && ' and ') + this.nodes.map(function (expr) { | ||
return expr.toString(); | ||
}).join(' and '); | ||
} | ||
return str; | ||
}; | ||
/** | ||
* Return "<a> and <b> and <c>" | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Query.prototype.toString = function(){ | ||
var pred = this.predicate ? this.predicate + ' ' : '' | ||
, type = this.type || '' | ||
, len = this.nodes.length | ||
, str = pred + type; | ||
if (len) { | ||
str += (type && ' and ') + this.nodes.map(function(expr){ | ||
return expr.toString(); | ||
}).join(' and '); | ||
} | ||
return str; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Query.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Query', | ||
predicate: this.predicate, | ||
type: this.type, | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Query', | ||
predicate: this.predicate, | ||
type: this.type, | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -22,43 +22,40 @@ | ||
var Return = module.exports = function Return(expr){ | ||
this.expr = expr || nodes.null; | ||
}; | ||
module.exports = class Return extends Node { | ||
constructor(expr) { | ||
super(); | ||
this.expr = expr || nodes.null; | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Return.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Return(); | ||
clone.expr = this.expr.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Return.prototype.clone = function(parent){ | ||
var clone = new Return(); | ||
clone.expr = this.expr.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Return.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Return', | ||
expr: this.expr, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Return', | ||
expr: this.expr, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -18,327 +18,324 @@ | ||
/** | ||
* Initialize a new `RGBA` with the given r,g,b,a component values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @api public | ||
*/ | ||
exports = module.exports = class RGBA extends Node { | ||
/** | ||
* Initialize a new `RGBA` with the given r,g,b,a component values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @api public | ||
*/ | ||
var RGBA = exports = module.exports = function RGBA(r,g,b,a){ | ||
Node.call(this); | ||
this.r = clamp(r); | ||
this.g = clamp(g); | ||
this.b = clamp(b); | ||
this.a = clampAlpha(a); | ||
this.name = ''; | ||
this.rgba = this; | ||
}; | ||
constructor(r, g, b, a) { | ||
super(); | ||
this.r = clamp(r); | ||
this.g = clamp(g); | ||
this.b = clamp(b); | ||
this.a = clampAlpha(a); | ||
this.name = ''; | ||
this.rgba = this; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return an `RGBA` without clamping values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @return {RGBA} | ||
* @api public | ||
*/ | ||
RGBA.prototype.__proto__ = Node.prototype; | ||
static withoutClamping(r, g, b, a) { | ||
var rgba = new RGBA(0, 0, 0, 0); | ||
rgba.r = r; | ||
rgba.g = g; | ||
rgba.b = b; | ||
rgba.a = a; | ||
return rgba; | ||
}; | ||
/** | ||
* Return an `RGBA` without clamping values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @return {RGBA} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
RGBA.withoutClamping = function(r,g,b,a){ | ||
var rgba = new RGBA(0,0,0,0); | ||
rgba.r = r; | ||
rgba.g = g; | ||
rgba.b = b; | ||
rgba.a = a; | ||
return rgba; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
RGBA.prototype.clone = function(){ | ||
var clone = new RGBA( | ||
clone() { | ||
var clone = new RGBA( | ||
this.r | ||
, this.g | ||
, this.b | ||
, this.a); | ||
clone.raw = this.raw; | ||
clone.name = this.name; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
, this.g | ||
, this.b | ||
, this.a); | ||
clone.raw = this.raw; | ||
clone.name = this.name; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
RGBA.prototype.toJSON = function(){ | ||
return { | ||
__type: 'RGBA', | ||
r: this.r, | ||
g: this.g, | ||
b: this.b, | ||
a: this.a, | ||
raw: this.raw, | ||
name: this.name, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'RGBA', | ||
r: this.r, | ||
g: this.g, | ||
b: this.b, | ||
a: this.a, | ||
raw: this.raw, | ||
name: this.name, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Return true. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return true. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
RGBA.prototype.toBoolean = function(){ | ||
return nodes.true; | ||
}; | ||
toBoolean() { | ||
return nodes.true; | ||
}; | ||
/** | ||
* Return `HSLA` representation. | ||
* | ||
* @return {HSLA} | ||
* @api public | ||
*/ | ||
/** | ||
* Return `HSLA` representation. | ||
* | ||
* @return {HSLA} | ||
* @api public | ||
*/ | ||
RGBA.prototype.__defineGetter__('hsla', function(){ | ||
return HSLA.fromRGBA(this); | ||
}); | ||
get hsla() { | ||
return HSLA.fromRGBA(this); | ||
}; | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return hash. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
RGBA.prototype.__defineGetter__('hash', function(){ | ||
return this.toString(); | ||
}); | ||
get hash() { | ||
return this.toString(); | ||
}; | ||
/** | ||
* Add r,g,b,a to the current component values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
/** | ||
* Add r,g,b,a to the current component values. | ||
* | ||
* @param {Number} r | ||
* @param {Number} g | ||
* @param {Number} b | ||
* @param {Number} a | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.add = function(r,g,b,a){ | ||
return new RGBA( | ||
add(r, g, b, a) { | ||
return new RGBA( | ||
this.r + r | ||
, this.g + g | ||
, this.b + b | ||
, this.a + a); | ||
}; | ||
, 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 | ||
*/ | ||
/** | ||
* 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( | ||
sub(r, g, b, a) { | ||
return new RGBA( | ||
this.r - r | ||
, this.g - g | ||
, this.b - b | ||
, a == 1 ? this.a : this.a - a); | ||
}; | ||
, 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 | ||
*/ | ||
/** | ||
* Multiply rgb components by `n`. | ||
* | ||
* @param {String} n | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.multiply = function(n){ | ||
return new RGBA( | ||
multiply(n) { | ||
return new RGBA( | ||
this.r * n | ||
, this.g * n | ||
, this.b * n | ||
, this.a); | ||
}; | ||
, this.g * n | ||
, this.b * n | ||
, this.a); | ||
}; | ||
/** | ||
* Divide rgb components by `n`. | ||
* | ||
* @param {String} n | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
/** | ||
* Divide rgb components by `n`. | ||
* | ||
* @param {String} n | ||
* @return {RGBA} new node | ||
* @api public | ||
*/ | ||
RGBA.prototype.divide = function(n){ | ||
return new RGBA( | ||
divide(n) { | ||
return new RGBA( | ||
this.r / n | ||
, this.g / n | ||
, this.b / n | ||
, this.a); | ||
}; | ||
, this.g / n | ||
, this.b / n | ||
, this.a); | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
RGBA.prototype.operate = function(op, right){ | ||
if ('in' != op) right = right.first | ||
operate(op, right) { | ||
if ('in' != op) right = right.first | ||
switch (op) { | ||
case 'is a': | ||
if ('string' == right.nodeName && 'color' == right.string) { | ||
return nodes.true; | ||
} | ||
break; | ||
case '+': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
var n = right.val; | ||
switch (right.type) { | ||
case '%': return adjust(this, new nodes.String('lightness'), right); | ||
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 '-': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
var n = right.val; | ||
switch (right.type) { | ||
case '%': return adjust(this, new nodes.String('lightness'), new nodes.Unit(-n, '%')); | ||
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 '*': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
return this.multiply(right.val); | ||
} | ||
break; | ||
case '/': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
return this.divide(right.val); | ||
} | ||
break; | ||
} | ||
return Node.prototype.operate.call(this, op, right); | ||
}; | ||
switch (op) { | ||
case 'is a': | ||
if ('string' == right.nodeName && 'color' == right.string) { | ||
return nodes.true; | ||
} | ||
break; | ||
case '+': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
var n = right.val; | ||
switch (right.type) { | ||
case '%': return adjust(this, new nodes.String('lightness'), right); | ||
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 '-': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
var n = right.val; | ||
switch (right.type) { | ||
case '%': return adjust(this, new nodes.String('lightness'), new nodes.Unit(-n, '%')); | ||
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 '*': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
return this.multiply(right.val); | ||
} | ||
break; | ||
case '/': | ||
switch (right.nodeName) { | ||
case 'unit': | ||
return this.divide(right.val); | ||
} | ||
break; | ||
} | ||
return super.operate(op, right); | ||
}; | ||
/** | ||
* Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return #nnnnnn, #nnn, or rgba(n,n,n,n) string representation of the color. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
RGBA.prototype.toString = function(){ | ||
function pad(n) { | ||
return n < 16 | ||
? '0' + n.toString(16) | ||
: n.toString(16); | ||
} | ||
toString() { | ||
function pad(n) { | ||
return n < 16 | ||
? '0' + n.toString(16) | ||
: n.toString(16); | ||
} | ||
// special case for transparent named color | ||
if ('transparent' == this.name) | ||
return this.name; | ||
// special case for transparent named color | ||
if ('transparent' == this.name) | ||
return this.name; | ||
if (1 == this.a) { | ||
var r = pad(this.r) | ||
, g = pad(this.g) | ||
, b = pad(this.b); | ||
if (1 == this.a) { | ||
var r = pad(this.r) | ||
, g = pad(this.g) | ||
, b = pad(this.b); | ||
// Compress | ||
if (r[0] == r[1] && g[0] == g[1] && b[0] == b[1]) { | ||
return '#' + r[0] + g[0] + b[0]; | ||
// Compress | ||
if (r[0] == r[1] && g[0] == g[1] && b[0] == b[1]) { | ||
return '#' + r[0] + g[0] + b[0]; | ||
} else { | ||
return '#' + r + g + b; | ||
} | ||
} else { | ||
return '#' + r + g + b; | ||
return 'rgba(' | ||
+ this.r + ',' | ||
+ this.g + ',' | ||
+ this.b + ',' | ||
+ (+this.a.toFixed(3)) + ')'; | ||
} | ||
} else { | ||
return 'rgba(' | ||
+ this.r + ',' | ||
+ this.g + ',' | ||
+ this.b + ',' | ||
+ (+this.a.toFixed(3)) + ')'; | ||
} | ||
}; | ||
}; | ||
/** | ||
* Return a `RGBA` from the given `hsla`. | ||
* | ||
* @param {HSLA} hsla | ||
* @return {RGBA} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a `RGBA` from the given `hsla`. | ||
* | ||
* @param {HSLA} hsla | ||
* @return {RGBA} | ||
* @api public | ||
*/ | ||
exports.fromHSLA = function(hsla){ | ||
var h = hsla.h / 360 | ||
, s = hsla.s / 100 | ||
, l = hsla.l / 100 | ||
, a = hsla.a; | ||
static fromHSLA(hsla) { | ||
var h = hsla.h / 360 | ||
, s = hsla.s / 100 | ||
, l = hsla.l / 100 | ||
, a = hsla.a; | ||
var m2 = l <= .5 ? l * (s + 1) : l + s - l * s | ||
, m1 = l * 2 - m2; | ||
var m2 = l <= .5 ? l * (s + 1) : l + s - l * s | ||
, m1 = l * 2 - m2; | ||
var r = hue(h + 1/3) * 0xff | ||
, g = hue(h) * 0xff | ||
, b = hue(h - 1/3) * 0xff; | ||
var r = hue(h + 1 / 3) * 0xff | ||
, g = hue(h) * 0xff | ||
, b = hue(h - 1 / 3) * 0xff; | ||
function hue(h) { | ||
if (h < 0) ++h; | ||
if (h > 1) --h; | ||
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; | ||
if (h * 2 < 1) return m2; | ||
if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6; | ||
return m1; | ||
} | ||
return new RGBA(r,g,b,a); | ||
function hue(h) { | ||
if (h < 0) ++h; | ||
if (h > 1) --h; | ||
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; | ||
if (h * 2 < 1) return m2; | ||
if (h * 3 < 2) return m1 + (m2 - m1) * (2 / 3 - h) * 6; | ||
return m1; | ||
} | ||
return new RGBA(r, g, b, a); | ||
}; | ||
}; | ||
@@ -345,0 +342,0 @@ |
@@ -14,84 +14,82 @@ | ||
/** | ||
* Initialize a new `Root` node. | ||
* | ||
* @api public | ||
*/ | ||
module.exports = class Root extends Node { | ||
/** | ||
* Initialize a new `Root` node. | ||
* | ||
* @api public | ||
*/ | ||
var Root = module.exports = function Root(){ | ||
this.nodes = []; | ||
}; | ||
constructor() { | ||
super(); | ||
this.nodes = []; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Push a `node` to this block. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
Root.prototype.__proto__ = Node.prototype; | ||
push(node) { | ||
this.nodes.push(node); | ||
}; | ||
/** | ||
* Push a `node` to this block. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
/** | ||
* Unshift a `node` to this block. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
Root.prototype.push = function(node){ | ||
this.nodes.push(node); | ||
}; | ||
unshift(node) { | ||
this.nodes.unshift(node); | ||
}; | ||
/** | ||
* Unshift a `node` to this block. | ||
* | ||
* @param {Node} node | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Root.prototype.unshift = function(node){ | ||
this.nodes.unshift(node); | ||
}; | ||
clone() { | ||
var clone = new Root(); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
this.nodes.forEach(function (node) { | ||
clone.push(node.clone(clone, clone)); | ||
}); | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return "root". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Root.prototype.clone = function(){ | ||
var clone = new Root(); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
this.nodes.forEach(function(node){ | ||
clone.push(node.clone(clone, clone)); | ||
}); | ||
return clone; | ||
}; | ||
toString() { | ||
return '[Root]'; | ||
}; | ||
/** | ||
* Return "root". | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Root.prototype.toString = function(){ | ||
return '[Root]'; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Root', | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Root.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Root', | ||
nodes: this.nodes, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -15,81 +15,78 @@ | ||
/** | ||
* Initialize a new `Selector` with the given `segs`. | ||
* | ||
* @param {Array} segs | ||
* @api public | ||
*/ | ||
module.exports = class Selector extends Node { | ||
/** | ||
* Initialize a new `Selector` with the given `segs`. | ||
* | ||
* @param {Array} segs | ||
* @api public | ||
*/ | ||
var Selector = module.exports = function Selector(segs){ | ||
Node.call(this); | ||
this.inherits = true; | ||
this.segments = segs; | ||
this.optional = false; | ||
}; | ||
constructor(segs) { | ||
super(); | ||
this.inherits = true; | ||
this.segments = segs; | ||
this.optional = false; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return the selector string. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Selector.prototype.__proto__ = Node.prototype; | ||
toString() { | ||
return this.segments.join('') + (this.optional ? ' !optional' : ''); | ||
}; | ||
/** | ||
* Return the selector string. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Check if this is placeholder selector. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
Selector.prototype.toString = function(){ | ||
return this.segments.join('') + (this.optional ? ' !optional' : ''); | ||
}; | ||
get isPlaceholder() { | ||
return this.val && ~this.val.substr(0, 2).indexOf('$'); | ||
}; | ||
/** | ||
* Check if this is placeholder selector. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Selector.prototype.__defineGetter__('isPlaceholder', function(){ | ||
return this.val && ~this.val.substr(0, 2).indexOf('$'); | ||
}); | ||
clone(parent) { | ||
var clone = new Selector; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.inherits = this.inherits; | ||
clone.val = this.val; | ||
clone.segments = this.segments.map(function (node) { return node.clone(parent, clone); }); | ||
clone.optional = this.optional; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Selector.prototype.clone = function(parent){ | ||
var clone = new Selector; | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
clone.inherits = this.inherits; | ||
clone.val = this.val; | ||
clone.segments = this.segments.map(function(node){ return node.clone(parent, clone); }); | ||
clone.optional = this.optional; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Selector', | ||
inherits: this.inherits, | ||
segments: this.segments, | ||
optional: this.optional, | ||
val: this.val, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Selector.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Selector', | ||
inherits: this.inherits, | ||
segments: this.segments, | ||
optional: this.optional, | ||
val: this.val, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -16,133 +16,130 @@ /*! | ||
/** | ||
* Initialize a new `String` with the given `val`. | ||
* | ||
* @param {String} val | ||
* @param {String} quote | ||
* @api public | ||
*/ | ||
module.exports = class String extends Node { | ||
/** | ||
* Initialize a new `String` with the given `val`. | ||
* | ||
* @param {String} val | ||
* @param {String} quote | ||
* @api public | ||
*/ | ||
var String = module.exports = function String(val, quote){ | ||
Node.call(this); | ||
this.val = val; | ||
this.string = val; | ||
this.prefixed = false; | ||
if (typeof quote !== 'string') { | ||
this.quote = "'"; | ||
} else { | ||
this.quote = quote; | ||
constructor(val, quote) { | ||
super(); | ||
this.val = val; | ||
this.string = val; | ||
this.prefixed = false; | ||
if (typeof quote !== 'string') { | ||
this.quote = "'"; | ||
} else { | ||
this.quote = quote; | ||
} | ||
} | ||
}; | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return quoted string. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
String.prototype.__proto__ = Node.prototype; | ||
toString() { | ||
return this.quote + this.val + this.quote; | ||
}; | ||
/** | ||
* Return quoted string. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
String.prototype.toString = function(){ | ||
return this.quote + this.val + this.quote; | ||
}; | ||
clone() { | ||
var clone = new String(this.val, this.quote); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
String.prototype.clone = function(){ | ||
var clone = new String(this.val, this.quote); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'String', | ||
val: this.val, | ||
quote: this.quote, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return Boolean based on the length of this string. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
String.prototype.toJSON = function(){ | ||
return { | ||
__type: 'String', | ||
val: this.val, | ||
quote: this.quote, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toBoolean() { | ||
return new nodes.Boolean(this.val.length); | ||
}; | ||
}; | ||
/** | ||
* Return Boolean based on the length of this string. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
/** | ||
* Coerce `other` to a string. | ||
* | ||
* @param {Node} other | ||
* @return {String} | ||
* @api public | ||
*/ | ||
String.prototype.toBoolean = function(){ | ||
return nodes.Boolean(this.val.length); | ||
}; | ||
coerce(other) { | ||
switch (other.nodeName) { | ||
case 'string': | ||
return other; | ||
case 'expression': | ||
return new String(other.nodes.map(function (node) { | ||
return this.coerce(node).val; | ||
}, this).join(' ')); | ||
default: | ||
return new String(other.toString()); | ||
} | ||
}; | ||
/** | ||
* Coerce `other` to a string. | ||
* | ||
* @param {Node} other | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
String.prototype.coerce = function(other){ | ||
switch (other.nodeName) { | ||
case 'string': | ||
return other; | ||
case 'expression': | ||
return new String(other.nodes.map(function(node){ | ||
return this.coerce(node).val; | ||
}, this).join(' ')); | ||
default: | ||
return new String(other.toString()); | ||
} | ||
}; | ||
operate(op, right) { | ||
switch (op) { | ||
case '%': | ||
var expr = new nodes.Expression; | ||
expr.push(this); | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
// constructargs | ||
var args = 'expression' == right.nodeName | ||
? utils.unwrap(right).nodes | ||
: [right]; | ||
String.prototype.operate = function(op, right){ | ||
switch (op) { | ||
case '%': | ||
var expr = new nodes.Expression; | ||
expr.push(this); | ||
// apply | ||
return sprintf.apply(null, [expr].concat(args)); | ||
case '+': | ||
var expr = new nodes.Expression; | ||
expr.push(new String(this.val + this.coerce(right).val)); | ||
return expr; | ||
default: | ||
return super.operate(op, right); | ||
} | ||
}; | ||
// constructargs | ||
var args = 'expression' == right.nodeName | ||
? utils.unwrap(right).nodes | ||
: [right]; | ||
// apply | ||
return sprintf.apply(null, [expr].concat(args)); | ||
case '+': | ||
var expr = new nodes.Expression; | ||
expr.push(new String(this.val + this.coerce(right).val)); | ||
return expr; | ||
default: | ||
return Node.prototype.operate.call(this, op, right); | ||
} | ||
}; |
@@ -13,64 +13,60 @@ /*! | ||
/** | ||
* Initialize a new supports node. | ||
* | ||
* @param {Expression} condition | ||
* @api public | ||
*/ | ||
module.exports = class Supports extends Atrule { | ||
/** | ||
* Initialize a new supports node. | ||
* | ||
* @param {Expression} condition | ||
* @api public | ||
*/ | ||
var Supports = module.exports = function Supports(condition){ | ||
Atrule.call(this, 'supports'); | ||
this.condition = condition; | ||
}; | ||
constructor(condition) { | ||
super('supports'); | ||
this.condition = condition; | ||
} | ||
/** | ||
* Inherit from `Atrule.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Supports.prototype.__proto__ = Atrule.prototype; | ||
clone(parent) { | ||
var clone = new Supports; | ||
clone.condition = this.condition.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Supports.prototype.clone = function(parent){ | ||
var clone = new Supports; | ||
clone.condition = this.condition.clone(parent, clone); | ||
clone.block = this.block.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
toJSON() { | ||
return { | ||
__type: 'Supports', | ||
condition: this.condition, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return @supports | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Supports.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Supports', | ||
condition: this.condition, | ||
block: this.block, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toString() { | ||
return '@supports ' + this.condition; | ||
}; | ||
}; | ||
/** | ||
* Return @supports | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Supports.prototype.toString = function(){ | ||
return '@supports ' + this.condition; | ||
}; |
@@ -14,59 +14,55 @@ | ||
/** | ||
* Initialize a new `Ternary` with `cond`, `trueExpr` and `falseExpr`. | ||
* | ||
* @param {Expression} cond | ||
* @param {Expression} trueExpr | ||
* @param {Expression} falseExpr | ||
* @api public | ||
*/ | ||
module.exports = class Ternary extends Node { | ||
/** | ||
* Initialize a new `Ternary` with `cond`, `trueExpr` and `falseExpr`. | ||
* | ||
* @param {Expression} cond | ||
* @param {Expression} trueExpr | ||
* @param {Expression} falseExpr | ||
* @api public | ||
*/ | ||
var Ternary = module.exports = function Ternary(cond, trueExpr, falseExpr){ | ||
Node.call(this); | ||
this.cond = cond; | ||
this.trueExpr = trueExpr; | ||
this.falseExpr = falseExpr; | ||
}; | ||
constructor(cond, trueExpr, falseExpr) { | ||
super(); | ||
this.cond = cond; | ||
this.trueExpr = trueExpr; | ||
this.falseExpr = falseExpr; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
Ternary.prototype.__proto__ = Node.prototype; | ||
clone(parent) { | ||
var clone = new Ternary(); | ||
clone.cond = this.cond.clone(parent, clone); | ||
clone.trueExpr = this.trueExpr.clone(parent, clone); | ||
clone.falseExpr = this.falseExpr.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Ternary.prototype.clone = function(parent){ | ||
var clone = new Ternary(); | ||
clone.cond = this.cond.clone(parent, clone); | ||
clone.trueExpr = this.trueExpr.clone(parent, clone); | ||
clone.falseExpr = this.falseExpr.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Ternary.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Ternary', | ||
cond: this.cond, | ||
trueExpr: this.trueExpr, | ||
falseExpr: this.falseExpr, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Ternary', | ||
cond: this.cond, | ||
trueExpr: this.trueExpr, | ||
falseExpr: this.falseExpr, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; |
@@ -14,54 +14,52 @@ | ||
/** | ||
* Initialize a new `UnaryOp` with `op`, and `expr`. | ||
* | ||
* @param {String} op | ||
* @param {Node} expr | ||
* @api public | ||
*/ | ||
module.exports = class UnaryOp extends Node { | ||
/** | ||
* Initialize a new `UnaryOp` with `op`, and `expr`. | ||
* | ||
* @param {String} op | ||
* @param {Node} expr | ||
* @api public | ||
*/ | ||
var UnaryOp = module.exports = function UnaryOp(op, expr){ | ||
Node.call(this); | ||
this.op = op; | ||
this.expr = expr; | ||
}; | ||
constructor(op, expr) { | ||
super(); | ||
this.op = op; | ||
this.expr = expr; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
UnaryOp.prototype.__proto__ = Node.prototype; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
clone(parent) { | ||
var clone = new UnaryOp(this.op); | ||
clone.expr = this.expr.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
UnaryOp.prototype.clone = function(parent){ | ||
var clone = new UnaryOp(this.op); | ||
clone.expr = this.expr.clone(parent, clone); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
toJSON() { | ||
return { | ||
__type: 'UnaryOp', | ||
op: this.op, | ||
expr: this.expr, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
UnaryOp.prototype.toJSON = function(){ | ||
return { | ||
__type: 'UnaryOp', | ||
op: this.op, | ||
expr: this.expr, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; |
@@ -20,196 +20,192 @@ | ||
var FACTOR_TABLE = { | ||
'mm': {val: 1, label: 'mm'}, | ||
'cm': {val: 10, label: 'mm'}, | ||
'in': {val: 25.4, label: 'mm'}, | ||
'pt': {val: 25.4/72, label: 'mm'}, | ||
'ms': {val: 1, label: 'ms'}, | ||
's': {val: 1000, label: 'ms'}, | ||
'Hz': {val: 1, label: 'Hz'}, | ||
'kHz': {val: 1000, label: 'Hz'} | ||
'mm': { val: 1, label: 'mm' }, | ||
'cm': { val: 10, label: 'mm' }, | ||
'in': { val: 25.4, label: 'mm' }, | ||
'pt': { val: 25.4 / 72, label: 'mm' }, | ||
'ms': { val: 1, label: 'ms' }, | ||
's': { val: 1000, label: 'ms' }, | ||
'Hz': { val: 1, label: 'Hz' }, | ||
'kHz': { val: 1000, label: 'Hz' } | ||
}; | ||
/** | ||
* 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 | ||
*/ | ||
module.exports = class Unit extends Node { | ||
/** | ||
* 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; | ||
}; | ||
constructor(val, type) { | ||
super(); | ||
this.val = val; | ||
this.type = type; | ||
} | ||
/** | ||
* Inherit from `Node.prototype`. | ||
*/ | ||
/** | ||
* Return Boolean based on the unit value. | ||
* | ||
* @return {Boolean} | ||
* @api public | ||
*/ | ||
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 | ||
toBoolean() { | ||
return new nodes.Boolean(this.type | ||
? true | ||
: this.val); | ||
}; | ||
}; | ||
/** | ||
* Return unit string. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return unit string. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Unit.prototype.toString = function(){ | ||
return this.val + (this.type || ''); | ||
}; | ||
toString() { | ||
return this.val + (this.type || ''); | ||
}; | ||
/** | ||
* Return a clone of this node. | ||
* | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* 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.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
clone() { | ||
var clone = new Unit(this.val, this.type); | ||
clone.lineno = this.lineno; | ||
clone.column = this.column; | ||
clone.filename = this.filename; | ||
return clone; | ||
}; | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
/** | ||
* Return a JSON representation of this node. | ||
* | ||
* @return {Object} | ||
* @api public | ||
*/ | ||
Unit.prototype.toJSON = function(){ | ||
return { | ||
__type: 'Unit', | ||
val: this.val, | ||
type: this.type, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
toJSON() { | ||
return { | ||
__type: 'Unit', | ||
val: this.val, | ||
type: this.type, | ||
lineno: this.lineno, | ||
column: this.column, | ||
filename: this.filename | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Operate on `right` with the given `op`. | ||
* | ||
* @param {String} op | ||
* @param {Node} right | ||
* @return {Node} | ||
* @api public | ||
*/ | ||
/** | ||
* 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; | ||
operate(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)) { | ||
right = right.first; | ||
// percentages | ||
if ('%' != this.type && ('-' == op || '+' == op) && '%' == right.type) { | ||
right = new Unit(this.val * (right.val / 100), '%'); | ||
} else { | ||
right = this.coerce(right); | ||
// swap color | ||
if ('rgba' == right.nodeName || 'hsla' == right.nodeName) { | ||
return right.operate(op, this); | ||
} | ||
switch (op) { | ||
case '-': | ||
return new Unit(this.val - right.val, type); | ||
case '+': | ||
// keyframes interpolation | ||
type = type || (right.type == '%' && right.type); | ||
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; | ||
if (start < end) { | ||
do { | ||
expr.push(new nodes.Unit(start)); | ||
} while (inclusive ? ++start <= end : ++start < end); | ||
} else { | ||
do { | ||
expr.push(new nodes.Unit(start)); | ||
} while (inclusive ? --start >= end : --start > end); | ||
} | ||
return expr; | ||
// operate | ||
if (this.shouldCoerce(op)) { | ||
right = right.first; | ||
// percentages | ||
if ('%' != this.type && ('-' == op || '+' == op) && '%' == right.type) { | ||
right = new Unit(this.val * (right.val / 100), '%'); | ||
} else { | ||
right = this.coerce(right); | ||
} | ||
switch (op) { | ||
case '-': | ||
return new Unit(this.val - right.val, type); | ||
case '+': | ||
// keyframes interpolation | ||
type = type || (right.type == '%' && right.type); | ||
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; | ||
if (start < end) { | ||
do { | ||
expr.push(new nodes.Unit(start)); | ||
} while (inclusive ? ++start <= end : ++start < end); | ||
} else { | ||
do { | ||
expr.push(new nodes.Unit(start)); | ||
} while (inclusive ? --start >= end : --start > end); | ||
} | ||
return expr; | ||
} | ||
} | ||
} | ||
return Node.prototype.operate.call(this, op, right); | ||
}; | ||
return super.operate(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 | ||
*/ | ||
/** | ||
* 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 | ||
, factorA = FACTOR_TABLE[a.type] | ||
, factorB = FACTOR_TABLE[b.type]; | ||
coerce(other) { | ||
if ('unit' == other.nodeName) { | ||
var a = this | ||
, b = other | ||
, factorA = FACTOR_TABLE[a.type] | ||
, factorB = FACTOR_TABLE[b.type]; | ||
if (factorA && factorB && (factorA.label == factorB.label)) { | ||
var bVal = b.val * (factorB.val / factorA.val); | ||
return new nodes.Unit(bVal, a.type); | ||
if (factorA && factorB && (factorA.label == factorB.label)) { | ||
var bVal = b.val * (factorB.val / factorA.val); | ||
return new nodes.Unit(bVal, a.type); | ||
} else { | ||
return new nodes.Unit(b.val, a.type); | ||
} | ||
} else if ('string' == other.nodeName) { | ||
// keyframes interpolation | ||
if ('%' == other.val) return new nodes.Unit(0, '%'); | ||
var val = parseFloat(other.val); | ||
if (isNaN(val)) super.coerce(other); | ||
return new nodes.Unit(val); | ||
} else { | ||
return new nodes.Unit(b.val, a.type); | ||
return super.coerce(other); | ||
} | ||
} else if ('string' == other.nodeName) { | ||
// keyframes interpolation | ||
if ('%' == other.val) return new nodes.Unit(0, '%'); | ||
var val = parseFloat(other.val); | ||
if (isNaN(val)) Node.prototype.coerce.call(this, other); | ||
return new nodes.Unit(val); | ||
} else { | ||
return Node.prototype.coerce.call(this, other); | ||
} | ||
}; | ||
}; |
@@ -21,3 +21,3 @@ /*! | ||
var debug = { | ||
lexer: require('debug')('stylus:lexer') | ||
lexer: require('debug')('stylus:lexer') | ||
, selector: require('debug')('stylus:parser:selector') | ||
@@ -31,3 +31,3 @@ }; | ||
var selectorTokens = [ | ||
'ident' | ||
'ident' | ||
, 'string' | ||
@@ -73,3 +73,3 @@ , 'selector' | ||
// Logical Combinations | ||
'is' | ||
'is' | ||
, 'has' | ||
@@ -149,56 +149,46 @@ , 'where' | ||
/** | ||
* Initialize a new `Parser` with the given `str` and `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @api private | ||
*/ | ||
module.exports = class Parser { | ||
/** | ||
* Initialize a new `Parser` with the given `str` and `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @api private | ||
*/ | ||
var Parser = module.exports = function Parser(str, options) { | ||
var self = this; | ||
options = options || {}; | ||
Parser.cache = Parser.cache || Parser.getCache(options); | ||
this.hash = Parser.cache.key(str, options); | ||
this.lexer = {}; | ||
if (!Parser.cache.has(this.hash)) { | ||
this.lexer = new Lexer(str, options); | ||
} | ||
this.prefix = options.prefix || ''; | ||
this.root = options.root || new nodes.Root; | ||
this.state = ['root']; | ||
this.stash = []; | ||
this.parens = 0; | ||
this.css = 0; | ||
this.state.pop = function(){ | ||
self.prevState = [].pop.call(this); | ||
constructor(str, options) { | ||
var self = this; | ||
options = options || {}; | ||
Parser.cache = Parser.cache || Parser.getCache(options); | ||
this.hash = Parser.cache.key(str, options); | ||
this.lexer = {}; | ||
if (!Parser.cache.has(this.hash)) { | ||
this.lexer = new Lexer(str, options); | ||
} | ||
this.prefix = options.prefix || ''; | ||
this.root = options.root || new nodes.Root; | ||
this.state = ['root']; | ||
this.stash = []; | ||
this.parens = 0; | ||
this.css = 0; | ||
this.state.pop = function () { | ||
self.prevState = [].pop.call(this); | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Get cache instance. | ||
* | ||
* @param {Object} options | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
Parser.getCache = function(options) { | ||
return false === options.cache | ||
? cache(false) | ||
: cache(options.cache || 'memory', options); | ||
}; | ||
/** | ||
* Parser prototype. | ||
*/ | ||
Parser.prototype = { | ||
/** | ||
* Constructor. | ||
* Get cache instance. | ||
* | ||
* @param {Object} options | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
constructor: Parser, | ||
static getCache(options) { | ||
return false === options.cache | ||
? cache(false) | ||
: cache(options.cache || 'memory', options); | ||
}; | ||
/** | ||
@@ -211,5 +201,5 @@ * Return current state. | ||
currentState: function() { | ||
currentState() { | ||
return this.state[this.state.length - 1]; | ||
}, | ||
} | ||
@@ -223,5 +213,5 @@ /** | ||
previousState: function() { | ||
previousState() { | ||
return this.state[this.state.length - 2]; | ||
}, | ||
} | ||
@@ -235,3 +225,3 @@ /** | ||
parse: function(){ | ||
parse() { | ||
var block = this.parent = this.root; | ||
@@ -254,3 +244,3 @@ if (Parser.cache.has(this.hash)) { | ||
return block; | ||
}, | ||
} | ||
@@ -264,3 +254,3 @@ /** | ||
error: function(msg){ | ||
error(msg) { | ||
var type = this.peek().type | ||
@@ -272,3 +262,3 @@ , val = undefined == this.peek().val | ||
throw new errors.ParseError(msg.replace('{peek}', '"' + type + val + '"')); | ||
}, | ||
} | ||
@@ -284,7 +274,7 @@ /** | ||
accept: function(type){ | ||
accept(type) { | ||
if (type == this.peek().type) { | ||
return this.next(); | ||
} | ||
}, | ||
} | ||
@@ -299,3 +289,3 @@ /** | ||
expect: function(type){ | ||
expect(type) { | ||
if (type != this.peek().type) { | ||
@@ -305,3 +295,3 @@ this.error('expected "' + type + '", got {peek}'); | ||
return this.next(); | ||
}, | ||
} | ||
@@ -315,3 +305,3 @@ /** | ||
next: function() { | ||
next() { | ||
var tok = this.stash.length | ||
@@ -331,3 +321,3 @@ ? this.stash.pop() | ||
return tok; | ||
}, | ||
} | ||
@@ -341,5 +331,5 @@ /** | ||
peek: function() { | ||
peek() { | ||
return this.lexer.peek(); | ||
}, | ||
} | ||
@@ -354,5 +344,5 @@ /** | ||
lookahead: function(n){ | ||
lookahead(n) { | ||
return this.lexer.lookahead(n); | ||
}, | ||
} | ||
@@ -367,3 +357,3 @@ /** | ||
isSelectorToken: function(n) { | ||
isSelectorToken(n) { | ||
var la = this.lookahead(n).type; | ||
@@ -382,3 +372,3 @@ switch (la) { | ||
} | ||
}, | ||
} | ||
@@ -393,6 +383,6 @@ /** | ||
isPseudoSelector: function(n){ | ||
isPseudoSelector(n) { | ||
var val = this.lookahead(n).val; | ||
return val && ~pseudoSelectors.indexOf(val.name); | ||
}, | ||
} | ||
@@ -407,3 +397,3 @@ /** | ||
lineContains: function(type){ | ||
lineContains(type) { | ||
var i = 1 | ||
@@ -416,3 +406,3 @@ , la; | ||
} | ||
}, | ||
} | ||
@@ -423,3 +413,3 @@ /** | ||
selectorToken: function() { | ||
selectorToken() { | ||
if (this.isSelectorToken(1)) { | ||
@@ -448,3 +438,3 @@ if ('{' == this.peek().type) { | ||
} | ||
}, | ||
} | ||
@@ -458,6 +448,6 @@ /** | ||
skip: function(tokens) { | ||
skip(tokens) { | ||
while (~tokens.indexOf(this.peek().type)) | ||
this.next(); | ||
}, | ||
} | ||
@@ -468,5 +458,5 @@ /** | ||
skipWhitespace: function() { | ||
skipWhitespace() { | ||
this.skip(['space', 'indent', 'outdent', 'newline']); | ||
}, | ||
} | ||
@@ -477,6 +467,6 @@ /** | ||
skipNewlines: function() { | ||
skipNewlines() { | ||
while ('newline' == this.peek().type) | ||
this.next(); | ||
}, | ||
} | ||
@@ -487,6 +477,6 @@ /** | ||
skipSpaces: function() { | ||
skipSpaces() { | ||
while ('space' == this.peek().type) | ||
this.next(); | ||
}, | ||
} | ||
@@ -497,7 +487,7 @@ /** | ||
skipSpacesAndComments: function() { | ||
skipSpacesAndComments() { | ||
while ('space' == this.peek().type | ||
|| 'comment' == this.peek().type) | ||
this.next(); | ||
}, | ||
} | ||
@@ -510,6 +500,6 @@ /** | ||
looksLikeFunctionDefinition: function(i) { | ||
looksLikeFunctionDefinition(i) { | ||
return 'indent' == this.lookahead(i).type | ||
|| '{' == this.lookahead(i).type; | ||
}, | ||
} | ||
@@ -525,3 +515,3 @@ /** | ||
looksLikeSelector: function(fromProperty) { | ||
looksLikeSelector(fromProperty) { | ||
var i = 1 | ||
@@ -540,3 +530,3 @@ , node | ||
&& ('newline' == this.lookahead(i + 1).type | ||
|| ',' == this.lookahead(i + 1).type)) i += 2; | ||
|| ',' == this.lookahead(i + 1).type)) i += 2; | ||
@@ -662,3 +652,3 @@ while (this.isSelectorToken(i) | ||
if (':' == this.lookahead(i++).type | ||
&& !this.lookahead(i-1).space | ||
&& !this.lookahead(i - 1).space | ||
&& this.isPseudoSelector(i)) | ||
@@ -691,3 +681,3 @@ return true; | ||
if (';' == this.lookahead(i).type || | ||
'}' == this.lookahead(i - 1).type) | ||
'}' == this.lookahead(i - 1).type) | ||
return false; | ||
@@ -698,3 +688,3 @@ } | ||
while (!~[ | ||
'indent' | ||
'indent' | ||
, 'outdent' | ||
@@ -711,3 +701,3 @@ , 'newline' | ||
return true; | ||
}, | ||
} | ||
@@ -719,3 +709,3 @@ /** | ||
looksLikeAttributeSelector: function(n) { | ||
looksLikeAttributeSelector(n) { | ||
var type = this.lookahead(n).type; | ||
@@ -728,3 +718,3 @@ if ('=' == type && this.bracketed) return true; | ||
&& !this.lineContains('='); | ||
}, | ||
} | ||
@@ -736,3 +726,3 @@ /** | ||
looksLikeKeyframe: function() { | ||
looksLikeKeyframe() { | ||
var i = 2 | ||
@@ -747,7 +737,7 @@ , type; | ||
while ('unit' == this.lookahead(++i).type | ||
|| 'newline' == this.lookahead(i).type) ; | ||
|| 'newline' == this.lookahead(i).type); | ||
type = this.lookahead(i).type; | ||
return 'indent' == type || '{' == type; | ||
} | ||
}, | ||
} | ||
@@ -758,3 +748,3 @@ /** | ||
stateAllowsSelector: function() { | ||
stateAllowsSelector() { | ||
switch (this.currentState()) { | ||
@@ -770,3 +760,3 @@ case 'root': | ||
} | ||
}, | ||
} | ||
@@ -780,9 +770,9 @@ /** | ||
assignAtblock: function(expr) { | ||
assignAtblock(expr) { | ||
try { | ||
expr.push(this.atblock(expr)); | ||
} catch(err) { | ||
} catch (err) { | ||
this.error('invalid right-hand side operand in assignment, got {peek}'); | ||
} | ||
}, | ||
} | ||
@@ -795,3 +785,3 @@ /** | ||
statement: function() { | ||
statement() { | ||
var stmt = this.stmt() | ||
@@ -817,3 +807,3 @@ , state = this.prevState | ||
while (op = | ||
this.accept('if') | ||
this.accept('if') | ||
|| this.accept('unless') | ||
@@ -844,3 +834,3 @@ || this.accept('for')) { | ||
return stmt; | ||
}, | ||
} | ||
@@ -868,3 +858,3 @@ /** | ||
stmt: function() { | ||
stmt() { | ||
var tok = this.peek(), selector; | ||
@@ -945,3 +935,3 @@ switch (tok.type) { | ||
} | ||
}, | ||
} | ||
@@ -952,3 +942,3 @@ /** | ||
block: function(node, scope) { | ||
block(node, scope) { | ||
var delim | ||
@@ -1009,3 +999,3 @@ , stmt | ||
return block; | ||
}, | ||
} | ||
@@ -1016,7 +1006,7 @@ /** | ||
comment: function(){ | ||
comment() { | ||
var node = this.next().val; | ||
this.skipSpaces(); | ||
return node; | ||
}, | ||
} | ||
@@ -1027,3 +1017,3 @@ /** | ||
for: function() { | ||
for() { | ||
this.expect('for'); | ||
@@ -1041,3 +1031,3 @@ var key | ||
return each; | ||
}, | ||
} | ||
@@ -1048,3 +1038,3 @@ /** | ||
return: function() { | ||
return() { | ||
this.expect('return'); | ||
@@ -1055,3 +1045,3 @@ var expr = this.expression(); | ||
: new nodes.Return(expr); | ||
}, | ||
} | ||
@@ -1062,3 +1052,3 @@ /** | ||
unless: function() { | ||
unless() { | ||
this.expect('unless'); | ||
@@ -1072,3 +1062,3 @@ this.state.push('conditional'); | ||
return node; | ||
}, | ||
} | ||
@@ -1079,3 +1069,3 @@ /** | ||
if: function() { | ||
if() { | ||
var token = this.expect('if'); | ||
@@ -1115,3 +1105,3 @@ | ||
return node; | ||
}, | ||
} | ||
@@ -1124,3 +1114,3 @@ /** | ||
atblock: function(node){ | ||
atblock(node) { | ||
if (!node) this.expect('atblock'); | ||
@@ -1132,3 +1122,3 @@ node = new nodes.Atblock; | ||
return node; | ||
}, | ||
} | ||
@@ -1139,3 +1129,3 @@ /** | ||
atrule: function(){ | ||
atrule() { | ||
var type = this.expect('atrule').val | ||
@@ -1155,3 +1145,3 @@ , node = new nodes.Atrule(type) | ||
return node; | ||
}, | ||
} | ||
@@ -1162,10 +1152,10 @@ /** | ||
scope: function(){ | ||
scope() { | ||
this.expect('scope'); | ||
var selector = this.selectorParts() | ||
.map(function(selector) { return selector.val; }) | ||
.map(function (selector) { return selector.val; }) | ||
.join(''); | ||
this.selectorScope = selector.trim(); | ||
return nodes.null; | ||
}, | ||
} | ||
@@ -1176,3 +1166,3 @@ /** | ||
supports: function(){ | ||
supports() { | ||
this.expect('supports'); | ||
@@ -1184,3 +1174,3 @@ var node = new nodes.Supports(this.supportsCondition()); | ||
return node; | ||
}, | ||
} | ||
@@ -1193,3 +1183,3 @@ /** | ||
supportsCondition: function(){ | ||
supportsCondition() { | ||
var node = this.supportsNegation() | ||
@@ -1203,3 +1193,3 @@ || this.supportsOp(); | ||
return node; | ||
}, | ||
} | ||
@@ -1210,3 +1200,3 @@ /** | ||
supportsNegation: function(){ | ||
supportsNegation() { | ||
if (this.accept('not')) { | ||
@@ -1218,3 +1208,3 @@ var node = new nodes.Expression; | ||
} | ||
}, | ||
} | ||
@@ -1225,3 +1215,3 @@ /** | ||
supportsOp: function(){ | ||
supportsOp() { | ||
var feature = this.supportsFeature() | ||
@@ -1239,3 +1229,3 @@ , op | ||
} | ||
}, | ||
} | ||
@@ -1247,3 +1237,3 @@ /** | ||
supportsFeature: function(){ | ||
supportsFeature() { | ||
this.skipSpacesAndComments(); | ||
@@ -1266,3 +1256,3 @@ if ('(' == this.peek().type) { | ||
} | ||
}, | ||
} | ||
@@ -1273,3 +1263,3 @@ /** | ||
extend: function(){ | ||
extend() { | ||
var tok = this.expect('extend') | ||
@@ -1296,3 +1286,3 @@ , selectors = [] | ||
sel.optional = true; | ||
} while(this.accept(',')); | ||
} while (this.accept(',')); | ||
@@ -1303,3 +1293,3 @@ node = new nodes.Extend(selectors); | ||
return node; | ||
}, | ||
} | ||
@@ -1310,3 +1300,3 @@ /** | ||
media: function() { | ||
media() { | ||
this.expect('media'); | ||
@@ -1318,3 +1308,3 @@ this.state.push('atrule'); | ||
return media; | ||
}, | ||
} | ||
@@ -1325,3 +1315,3 @@ /** | ||
queries: function() { | ||
queries() { | ||
var queries = new nodes.QueryList | ||
@@ -1336,3 +1326,3 @@ , skip = ['comment', 'newline', 'space']; | ||
return queries; | ||
}, | ||
} | ||
@@ -1345,3 +1335,3 @@ /** | ||
query: function() { | ||
query() { | ||
var query = new nodes.Query | ||
@@ -1355,3 +1345,3 @@ , expr | ||
&& ('.' == this.lookahead(2).type | ||
|| '[' == this.lookahead(2).type)) { | ||
|| '[' == this.lookahead(2).type)) { | ||
this.cond = true; | ||
@@ -1384,3 +1374,3 @@ expr = this.expression(); | ||
return query; | ||
}, | ||
} | ||
@@ -1391,3 +1381,3 @@ /** | ||
feature: function() { | ||
feature() { | ||
this.skipSpacesAndComments(); | ||
@@ -1407,3 +1397,3 @@ this.expect('('); | ||
return node; | ||
}, | ||
} | ||
@@ -1414,3 +1404,3 @@ /** | ||
mozdocument: function(){ | ||
mozdocument() { | ||
this.expect('-moz-document'); | ||
@@ -1429,3 +1419,3 @@ var mozdocument = new nodes.Atrule('-moz-document') | ||
return mozdocument; | ||
}, | ||
} | ||
@@ -1436,7 +1426,7 @@ /** | ||
import: function() { | ||
import() { | ||
this.expect('import'); | ||
this.allowPostfix = true; | ||
return new nodes.Import(this.expression(), false); | ||
}, | ||
} | ||
@@ -1447,7 +1437,7 @@ /** | ||
require: function() { | ||
require() { | ||
this.expect('require'); | ||
this.allowPostfix = true; | ||
return new nodes.Import(this.expression(), true); | ||
}, | ||
} | ||
@@ -1458,3 +1448,3 @@ /** | ||
charset: function() { | ||
charset() { | ||
this.expect('charset'); | ||
@@ -1464,3 +1454,3 @@ var str = this.expect('string').val; | ||
return new nodes.Charset(str); | ||
}, | ||
} | ||
@@ -1471,3 +1461,3 @@ /** | ||
namespace: function() { | ||
namespace() { | ||
var str | ||
@@ -1486,3 +1476,3 @@ , prefix; | ||
return new nodes.Namespace(str, prefix); | ||
}, | ||
} | ||
@@ -1493,3 +1483,3 @@ /** | ||
keyframes: function() { | ||
keyframes() { | ||
var tok = this.expect('keyframes') | ||
@@ -1510,3 +1500,3 @@ , keyframes; | ||
return keyframes; | ||
}, | ||
} | ||
@@ -1517,5 +1507,5 @@ /** | ||
literal: function() { | ||
literal() { | ||
return this.expect('literal').val; | ||
}, | ||
} | ||
@@ -1526,7 +1516,7 @@ /** | ||
id: function() { | ||
id() { | ||
var tok = this.expect('ident'); | ||
this.accept('space'); | ||
return tok.val; | ||
}, | ||
} | ||
@@ -1540,3 +1530,3 @@ /** | ||
ident: function() { | ||
ident() { | ||
var i = 2 | ||
@@ -1562,3 +1552,3 @@ , la = this.lookahead(i).type; | ||
while ('=' != this.lookahead(++i).type | ||
&& !~['[', ',', 'newline', 'indent', 'eos'].indexOf(this.lookahead(i).type)) ; | ||
&& !~['[', ',', 'newline', 'indent', 'eos'].indexOf(this.lookahead(i).type)); | ||
if ('=' == this.lookahead(i).type) { | ||
@@ -1575,3 +1565,3 @@ this._ident = this.peek(); | ||
&& 'selector' != this.lookahead(i).type | ||
&& 'eos' != this.lookahead(i).type) ; | ||
&& 'eos' != this.lookahead(i).type); | ||
if ('=' == this.lookahead(i).type) { | ||
@@ -1649,3 +1639,3 @@ this._ident = this.peek(); | ||
} | ||
}, | ||
} | ||
@@ -1656,3 +1646,3 @@ /** | ||
interpolate: function() { | ||
interpolate() { | ||
var node | ||
@@ -1671,5 +1661,5 @@ , segs = [] | ||
this.state.pop(); | ||
} else if (node = this.accept('-')){ | ||
} else if (node = this.accept('-')) { | ||
segs.push(new nodes.Literal('-')); | ||
} else if (node = this.accept('ident')){ | ||
} else if (node = this.accept('ident')) { | ||
segs.push(node.val); | ||
@@ -1682,3 +1672,3 @@ } else { | ||
return segs; | ||
}, | ||
} | ||
@@ -1690,3 +1680,3 @@ /** | ||
property: function() { | ||
property() { | ||
if (this.looksLikeSelector(true)) return this.selector(); | ||
@@ -1715,3 +1705,3 @@ | ||
return ret; | ||
}, | ||
} | ||
@@ -1724,3 +1714,3 @@ /** | ||
selector: function() { | ||
selector() { | ||
var arr | ||
@@ -1755,5 +1745,5 @@ , group = new nodes.Group | ||
return group; | ||
}, | ||
} | ||
selectorParts: function(){ | ||
selectorParts() { | ||
var tok | ||
@@ -1804,3 +1794,3 @@ , arr = []; | ||
return arr; | ||
}, | ||
} | ||
@@ -1811,3 +1801,3 @@ /** | ||
assignment: function() { | ||
assignment() { | ||
var | ||
@@ -1820,3 +1810,3 @@ op, | ||
if (op = | ||
this.accept('=') | ||
this.accept('=') | ||
|| this.accept('?=') | ||
@@ -1857,3 +1847,3 @@ || this.accept('+=') | ||
return node; | ||
}, | ||
} | ||
@@ -1865,3 +1855,3 @@ /** | ||
function: function() { | ||
function() { | ||
var parens = 1 | ||
@@ -1898,3 +1888,3 @@ , i = 2 | ||
} | ||
}, | ||
} | ||
@@ -1905,3 +1895,3 @@ /** | ||
url: function() { | ||
url() { | ||
this.expect('function'); | ||
@@ -1913,3 +1903,3 @@ this.state.push('function arguments'); | ||
return new nodes.Call('url', args); | ||
}, | ||
} | ||
@@ -1920,3 +1910,3 @@ /** | ||
functionCall: function() { | ||
functionCall() { | ||
var withBlock = this.accept('+'); | ||
@@ -1945,3 +1935,3 @@ if ('url' == this.peek().val.name) return this.url(); | ||
return call; | ||
}, | ||
} | ||
@@ -1952,3 +1942,3 @@ /** | ||
functionDefinition: function() { | ||
functionDefinition() { | ||
var | ||
@@ -1976,3 +1966,3 @@ tok = this.expect('function'), | ||
return new nodes.Ident(name, fn); | ||
}, | ||
} | ||
@@ -1986,3 +1976,3 @@ /** | ||
params: function() { | ||
params() { | ||
var tok | ||
@@ -2004,3 +1994,3 @@ , node | ||
return params; | ||
}, | ||
} | ||
@@ -2011,3 +2001,3 @@ /** | ||
args: function() { | ||
args() { | ||
var args = new nodes.Arguments | ||
@@ -2022,3 +2012,3 @@ , keyword; | ||
args.map[keyword] = this.expression(); | ||
// arg | ||
// arg | ||
} else { | ||
@@ -2030,3 +2020,3 @@ args.push(this.expression()); | ||
return args; | ||
}, | ||
} | ||
@@ -2037,3 +2027,3 @@ /** | ||
list: function() { | ||
list() { | ||
var node = this.expression(); | ||
@@ -2052,3 +2042,3 @@ | ||
return node; | ||
}, | ||
} | ||
@@ -2059,3 +2049,3 @@ /** | ||
expression: function() { | ||
expression() { | ||
var node | ||
@@ -2074,3 +2064,3 @@ , expr = new nodes.Expression; | ||
return expr; | ||
}, | ||
} | ||
@@ -2082,3 +2072,3 @@ /** | ||
negation: function() { | ||
negation() { | ||
if (this.accept('not')) { | ||
@@ -2088,3 +2078,3 @@ return new nodes.UnaryOp('!', this.negation()); | ||
return this.ternary(); | ||
}, | ||
} | ||
@@ -2095,3 +2085,3 @@ /** | ||
ternary: function() { | ||
ternary() { | ||
var node = this.logical(); | ||
@@ -2105,3 +2095,3 @@ if (this.accept('?')) { | ||
return node; | ||
}, | ||
} | ||
@@ -2112,3 +2102,3 @@ /** | ||
logical: function() { | ||
logical() { | ||
var op | ||
@@ -2120,3 +2110,3 @@ , node = this.typecheck(); | ||
return node; | ||
}, | ||
} | ||
@@ -2127,3 +2117,3 @@ /** | ||
typecheck: function() { | ||
typecheck() { | ||
var op | ||
@@ -2138,3 +2128,3 @@ , node = this.equality(); | ||
return node; | ||
}, | ||
} | ||
@@ -2145,3 +2135,3 @@ /** | ||
equality: function() { | ||
equality() { | ||
var op | ||
@@ -2156,3 +2146,3 @@ , node = this.in(); | ||
return node; | ||
}, | ||
} | ||
@@ -2163,3 +2153,3 @@ /** | ||
in: function() { | ||
in() { | ||
var node = this.relational(); | ||
@@ -2173,3 +2163,3 @@ while (this.accept('in')) { | ||
return node; | ||
}, | ||
} | ||
@@ -2180,11 +2170,11 @@ /** | ||
relational: function() { | ||
relational() { | ||
var op | ||
, node = this.range(); | ||
while (op = | ||
this.accept('>=') | ||
this.accept('>=') | ||
|| this.accept('<=') | ||
|| this.accept('<') | ||
|| this.accept('>') | ||
) { | ||
) { | ||
this.operand = true; | ||
@@ -2196,3 +2186,3 @@ if (!node) this.error('illegal unary "' + op + '", missing left-hand operand'); | ||
return node; | ||
}, | ||
} | ||
@@ -2203,3 +2193,3 @@ /** | ||
range: function() { | ||
range() { | ||
var op | ||
@@ -2214,3 +2204,3 @@ , node = this.additive(); | ||
return node; | ||
}, | ||
} | ||
@@ -2221,3 +2211,3 @@ /** | ||
additive: function() { | ||
additive() { | ||
var op | ||
@@ -2231,3 +2221,3 @@ , node = this.multiplicative(); | ||
return node; | ||
}, | ||
} | ||
@@ -2238,7 +2228,7 @@ /** | ||
multiplicative: function() { | ||
multiplicative() { | ||
var op | ||
, node = this.defined(); | ||
while (op = | ||
this.accept('**') | ||
this.accept('**') | ||
|| this.accept('*') | ||
@@ -2259,3 +2249,3 @@ || this.accept('/') | ||
return node; | ||
}, | ||
} | ||
@@ -2267,3 +2257,3 @@ /** | ||
defined: function() { | ||
defined() { | ||
var node = this.unary(); | ||
@@ -2275,3 +2265,3 @@ if (this.accept('is defined')) { | ||
return node; | ||
}, | ||
} | ||
@@ -2283,7 +2273,7 @@ /** | ||
unary: function() { | ||
unary() { | ||
var op | ||
, node; | ||
if (op = | ||
this.accept('!') | ||
this.accept('!') | ||
|| this.accept('~') | ||
@@ -2300,3 +2290,3 @@ || this.accept('+') | ||
return this.subscript(); | ||
}, | ||
} | ||
@@ -2308,3 +2298,3 @@ /** | ||
subscript: function() { | ||
subscript() { | ||
var node = this.member() | ||
@@ -2324,3 +2314,3 @@ , id; | ||
return node; | ||
}, | ||
} | ||
@@ -2332,3 +2322,3 @@ /** | ||
member: function() { | ||
member() { | ||
var node = this.primary(); | ||
@@ -2348,3 +2338,3 @@ if (node) { | ||
return node; | ||
}, | ||
} | ||
@@ -2356,3 +2346,3 @@ /** | ||
object: function(){ | ||
object() { | ||
var obj = new nodes.Object | ||
@@ -2389,3 +2379,3 @@ , id, val, comma, hash; | ||
return obj; | ||
}, | ||
} | ||
@@ -2406,3 +2396,3 @@ /** | ||
primary: function() { | ||
primary() { | ||
var tok; | ||
@@ -2409,0 +2399,0 @@ this.skipSpaces(); |
@@ -21,227 +21,222 @@ | ||
/** | ||
* Expose `Renderer`. | ||
*/ | ||
class Renderer extends EventEmitter { | ||
/** | ||
* Initialize a new `Renderer` with the given `str` and `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @api public | ||
*/ | ||
module.exports = Renderer; | ||
constructor(str, options) { | ||
super(); | ||
options = options || {}; | ||
options.globals = options.globals || {}; | ||
options.functions = options.functions || {}; | ||
options.use = options.use || []; | ||
options.use = Array.isArray(options.use) ? options.use : [options.use]; | ||
options.imports = [join(__dirname, 'functions/index.styl')].concat(options.imports || []); | ||
options.paths = options.paths || []; | ||
options.filename = options.filename || 'stylus'; | ||
options.Evaluator = options.Evaluator || Evaluator; | ||
this.options = options; | ||
this.str = str; | ||
this.events = events; | ||
} | ||
/** | ||
* Initialize a new `Renderer` with the given `str` and `options`. | ||
* | ||
* @param {String} str | ||
* @param {Object} options | ||
* @api public | ||
*/ | ||
/** | ||
* Parse and evaluate AST, then callback `fn(err, css, js)`. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
function Renderer(str, options) { | ||
options = options || {}; | ||
options.globals = options.globals || {}; | ||
options.functions = options.functions || {}; | ||
options.use = options.use || []; | ||
options.use = Array.isArray(options.use) ? options.use : [options.use]; | ||
options.imports = [join(__dirname, 'functions/index.styl')].concat(options.imports || []); | ||
options.paths = options.paths || []; | ||
options.filename = options.filename || 'stylus'; | ||
options.Evaluator = options.Evaluator || Evaluator; | ||
this.options = options; | ||
this.str = str; | ||
this.events = events; | ||
}; | ||
render(fn) { | ||
var parser = this.parser = new Parser(this.str, this.options); | ||
/** | ||
* Inherit from `EventEmitter.prototype`. | ||
*/ | ||
// use plugin(s) | ||
for (var i = 0, len = this.options.use.length; i < len; i++) { | ||
this.use(this.options.use[i]); | ||
} | ||
Renderer.prototype.__proto__ = EventEmitter.prototype; | ||
try { | ||
nodes.filename = this.options.filename; | ||
// parse | ||
var ast = parser.parse(); | ||
/** | ||
* Expose events explicitly. | ||
*/ | ||
// evaluate | ||
this.evaluator = new this.options.Evaluator(ast, this.options); | ||
this.nodes = nodes; | ||
this.evaluator.renderer = this; | ||
ast = this.evaluator.evaluate(); | ||
module.exports.events = events; | ||
// normalize | ||
var normalizer = new Normalizer(ast, this.options); | ||
ast = normalizer.normalize(); | ||
/** | ||
* Parse and evaluate AST, then callback `fn(err, css, js)`. | ||
* | ||
* @param {Function} fn | ||
* @api public | ||
*/ | ||
// compile | ||
var compiler = this.options.sourcemap | ||
? new (require('./visitor/sourcemapper'))(ast, this.options) | ||
: new (require('./visitor/compiler'))(ast, this.options) | ||
, css = compiler.compile(); | ||
Renderer.prototype.render = function(fn){ | ||
var parser = this.parser = new Parser(this.str, this.options); | ||
// expose sourcemap | ||
if (this.options.sourcemap) this.sourcemap = compiler.map.toJSON(); | ||
} catch (err) { | ||
var options = {}; | ||
options.input = err.input || this.str; | ||
options.filename = err.filename || this.options.filename; | ||
options.lineno = err.lineno || parser.lexer.lineno; | ||
options.column = err.column || parser.lexer.column; | ||
if (!fn) throw utils.formatException(err, options); | ||
return fn(utils.formatException(err, options)); | ||
} | ||
// use plugin(s) | ||
for (var i = 0, len = this.options.use.length; i < len; i++) { | ||
this.use(this.options.use[i]); | ||
// fire `end` event | ||
var listeners = this.listeners('end'); | ||
if (fn) listeners.push(fn); | ||
for (var i = 0, len = listeners.length; i < len; i++) { | ||
var ret = listeners[i](null, css); | ||
if (ret) css = ret; | ||
} | ||
if (!fn) return css; | ||
} | ||
try { | ||
nodes.filename = this.options.filename; | ||
// parse | ||
var ast = parser.parse(); | ||
/** | ||
* Get dependencies of the compiled file. | ||
* | ||
* @param {String} [filename] | ||
* @return {Array} | ||
* @api public | ||
*/ | ||
// evaluate | ||
this.evaluator = new this.options.Evaluator(ast, this.options); | ||
this.nodes = nodes; | ||
this.evaluator.renderer = this; | ||
ast = this.evaluator.evaluate(); | ||
deps(filename) { | ||
var opts = utils.merge({ cache: false }, this.options); | ||
if (filename) opts.filename = filename; | ||
// normalize | ||
var normalizer = new Normalizer(ast, this.options); | ||
ast = normalizer.normalize(); | ||
var DepsResolver = require('./visitor/deps-resolver') | ||
, parser = new Parser(this.str, opts); | ||
// compile | ||
var compiler = this.options.sourcemap | ||
? new (require('./visitor/sourcemapper'))(ast, this.options) | ||
: new (require('./visitor/compiler'))(ast, this.options) | ||
, css = compiler.compile(); | ||
try { | ||
nodes.filename = opts.filename; | ||
// parse | ||
var ast = parser.parse() | ||
, resolver = new DepsResolver(ast, opts); | ||
// expose sourcemap | ||
if (this.options.sourcemap) this.sourcemap = compiler.map.toJSON(); | ||
} catch (err) { | ||
var options = {}; | ||
options.input = err.input || this.str; | ||
options.filename = err.filename || this.options.filename; | ||
options.lineno = err.lineno || parser.lexer.lineno; | ||
options.column = err.column || parser.lexer.column; | ||
if (!fn) throw utils.formatException(err, options); | ||
return fn(utils.formatException(err, options)); | ||
} | ||
// resolve dependencies | ||
return resolver.resolve(); | ||
} catch (err) { | ||
var options = {}; | ||
options.input = err.input || this.str; | ||
options.filename = err.filename || opts.filename; | ||
options.lineno = err.lineno || parser.lexer.lineno; | ||
options.column = err.column || parser.lexer.column; | ||
throw utils.formatException(err, options); | ||
} | ||
}; | ||
// fire `end` event | ||
var listeners = this.listeners('end'); | ||
if (fn) listeners.push(fn); | ||
for (var i = 0, len = listeners.length; i < len; i++) { | ||
var ret = listeners[i](null, css); | ||
if (ret) css = ret; | ||
} | ||
if (!fn) return css; | ||
}; | ||
/** | ||
* Set option `key` to `val`. | ||
* | ||
* @param {String} key | ||
* @param {Mixed} val | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
/** | ||
* Get dependencies of the compiled file. | ||
* | ||
* @param {String} [filename] | ||
* @return {Array} | ||
* @api public | ||
*/ | ||
set(key, val) { | ||
this.options[key] = val; | ||
return this; | ||
}; | ||
Renderer.prototype.deps = function(filename){ | ||
var opts = utils.merge({ cache: false }, this.options); | ||
if (filename) opts.filename = filename; | ||
/** | ||
* Get option `key`. | ||
* | ||
* @param {String} key | ||
* @return {Mixed} val | ||
* @api public | ||
*/ | ||
var DepsResolver = require('./visitor/deps-resolver') | ||
, parser = new Parser(this.str, opts); | ||
get(key) { | ||
return this.options[key]; | ||
}; | ||
try { | ||
nodes.filename = opts.filename; | ||
// parse | ||
var ast = parser.parse() | ||
, resolver = new DepsResolver(ast, opts); | ||
/** | ||
* Include the given `path` to the lookup paths array. | ||
* | ||
* @param {String} path | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
// resolve dependencies | ||
return resolver.resolve(); | ||
} catch (err) { | ||
var options = {}; | ||
options.input = err.input || this.str; | ||
options.filename = err.filename || opts.filename; | ||
options.lineno = err.lineno || parser.lexer.lineno; | ||
options.column = err.column || parser.lexer.column; | ||
throw utils.formatException(err, options); | ||
} | ||
}; | ||
include(path) { | ||
this.options.paths.push(path); | ||
return this; | ||
}; | ||
/** | ||
* Set option `key` to `val`. | ||
* | ||
* @param {String} key | ||
* @param {Mixed} val | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
/** | ||
* Use the given `fn`. | ||
* | ||
* This allows for plugins to alter the renderer in | ||
* any way they wish, exposing paths etc. | ||
* | ||
* @param {Function} | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
Renderer.prototype.set = function(key, val){ | ||
this.options[key] = val; | ||
return this; | ||
}; | ||
use(fn) { | ||
fn.call(this, this); | ||
return this; | ||
}; | ||
/** | ||
* Get option `key`. | ||
* | ||
* @param {String} key | ||
* @return {Mixed} val | ||
* @api public | ||
*/ | ||
/** | ||
* Define function or global var with the given `name`. Optionally | ||
* the function may accept full expressions, by setting `raw` | ||
* to `true`. | ||
* | ||
* @param {String} name | ||
* @param {Function|Node} fn | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
Renderer.prototype.get = function(key){ | ||
return this.options[key]; | ||
}; | ||
define(name, fn, raw) { | ||
fn = utils.coerce(fn, raw); | ||
/** | ||
* Include the given `path` to the lookup paths array. | ||
* | ||
* @param {String} path | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
if (fn.nodeName) { | ||
this.options.globals[name] = fn; | ||
return this; | ||
} | ||
Renderer.prototype.include = function(path){ | ||
this.options.paths.push(path); | ||
return this; | ||
}; | ||
// function | ||
this.options.functions[name] = fn; | ||
if (undefined != raw) fn.raw = raw; | ||
return this; | ||
}; | ||
/** | ||
* Use the given `fn`. | ||
* | ||
* This allows for plugins to alter the renderer in | ||
* any way they wish, exposing paths etc. | ||
* | ||
* @param {Function} | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
/** | ||
* Import the given `file`. | ||
* | ||
* @param {String} file | ||
* @return {Renderer} for chaining | ||
* @api public | ||
*/ | ||
Renderer.prototype.use = function(fn){ | ||
fn.call(this, this); | ||
return this; | ||
import(file) { | ||
this.options.imports.push(file); | ||
return this; | ||
}; | ||
}; | ||
/** | ||
* Define function or global var with the given `name`. Optionally | ||
* the function may accept full expressions, by setting `raw` | ||
* to `true`. | ||
* | ||
* @param {String} name | ||
* @param {Function|Node} fn | ||
* @return {Renderer} for chaining | ||
* @api public | ||
* Expose `Renderer`. | ||
*/ | ||
Renderer.prototype.define = function(name, fn, raw){ | ||
fn = utils.coerce(fn, raw); | ||
module.exports = Renderer; | ||
if (fn.nodeName) { | ||
this.options.globals[name] = fn; | ||
return this; | ||
} | ||
// function | ||
this.options.functions[name] = fn; | ||
if (undefined != raw) fn.raw = raw; | ||
return this; | ||
}; | ||
/** | ||
* Import the given `file`. | ||
* | ||
* @param {String} file | ||
* @return {Renderer} for chaining | ||
* @api public | ||
* Expose events explicitly. | ||
*/ | ||
Renderer.prototype.import = function(file){ | ||
this.options.imports.push(file); | ||
return this; | ||
}; | ||
module.exports.events = events; |
@@ -9,251 +9,253 @@ /*! | ||
/** | ||
* Initialize a new `SelectorParser` | ||
* with the given `str` and selectors `stack`. | ||
* | ||
* @param {String} str | ||
* @param {Array} stack | ||
* @param {Array} parts | ||
* @api private | ||
*/ | ||
module.exports = class SelectorParser { | ||
/** | ||
* Initialize a new `SelectorParser` | ||
* with the given `str` and selectors `stack`. | ||
* | ||
* @param {String} str | ||
* @param {Array} stack | ||
* @param {Array} parts | ||
* @api private | ||
*/ | ||
var SelectorParser = module.exports = function SelectorParser(str, stack, parts) { | ||
this.str = str; | ||
this.stack = stack || []; | ||
this.parts = parts || []; | ||
this.pos = 0; | ||
this.level = 2; | ||
this.nested = true; | ||
this.ignore = false; | ||
}; | ||
constructor(str, stack, parts) { | ||
this.str = str; | ||
this.stack = stack || []; | ||
this.parts = parts || []; | ||
this.pos = 0; | ||
this.level = 2; | ||
this.nested = true; | ||
this.ignore = false; | ||
} | ||
/** | ||
* Consume the given `len` and move current position. | ||
* | ||
* @param {Number} len | ||
* @api private | ||
*/ | ||
/** | ||
* Consume the given `len` and move current position. | ||
* | ||
* @param {Number} len | ||
* @api private | ||
*/ | ||
SelectorParser.prototype.skip = function(len) { | ||
this.str = this.str.substr(len); | ||
this.pos += len; | ||
}; | ||
skip(len) { | ||
this.str = this.str.substr(len); | ||
this.pos += len; | ||
}; | ||
/** | ||
* Consume spaces. | ||
*/ | ||
/** | ||
* Consume spaces. | ||
*/ | ||
SelectorParser.prototype.skipSpaces = function() { | ||
while (' ' == this.str[0]) this.skip(1); | ||
}; | ||
skipSpaces() { | ||
while (' ' == this.str[0]) this.skip(1); | ||
}; | ||
/** | ||
* Fetch next token. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Fetch next token. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
SelectorParser.prototype.advance = function() { | ||
return this.root() | ||
|| this.relative() | ||
|| this.initial() | ||
|| this.escaped() | ||
|| this.parent() | ||
|| this.partial() | ||
|| this.char(); | ||
}; | ||
advance() { | ||
return this.root() | ||
|| this.relative() | ||
|| this.initial() | ||
|| this.escaped() | ||
|| this.parent() | ||
|| this.partial() | ||
|| this.char(); | ||
}; | ||
/** | ||
* '/' | ||
*/ | ||
/** | ||
* '/' | ||
*/ | ||
SelectorParser.prototype.root = function() { | ||
if (!this.pos && '/' == this.str[0] | ||
&& 'deep' != this.str.slice(1, 5)) { | ||
this.nested = false; | ||
this.skip(1); | ||
} | ||
}; | ||
root() { | ||
if (!this.pos && '/' == this.str[0] | ||
&& 'deep' != this.str.slice(1, 5)) { | ||
this.nested = false; | ||
this.skip(1); | ||
} | ||
}; | ||
/** | ||
* '../' | ||
*/ | ||
/** | ||
* '../' | ||
*/ | ||
SelectorParser.prototype.relative = function(multi) { | ||
if ((!this.pos || multi) && '../' == this.str.slice(0, 3)) { | ||
this.nested = false; | ||
this.skip(3); | ||
while (this.relative(true)) this.level++; | ||
if (!this.raw) { | ||
var ret = this.stack[this.stack.length - this.level]; | ||
if (ret) { | ||
return ret; | ||
} else { | ||
this.ignore = true; | ||
relative(multi) { | ||
if ((!this.pos || multi) && '../' == this.str.slice(0, 3)) { | ||
this.nested = false; | ||
this.skip(3); | ||
while (this.relative(true)) this.level++; | ||
if (!this.raw) { | ||
var ret = this.stack[this.stack.length - this.level]; | ||
if (ret) { | ||
return ret; | ||
} else { | ||
this.ignore = true; | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/** | ||
* '~/' | ||
*/ | ||
/** | ||
* '~/' | ||
*/ | ||
SelectorParser.prototype.initial = function() { | ||
if (!this.pos && '~' == this.str[0] && '/' == this.str[1]) { | ||
this.nested = false; | ||
this.skip(2); | ||
return this.stack[0]; | ||
} | ||
}; | ||
initial() { | ||
if (!this.pos && '~' == this.str[0] && '/' == this.str[1]) { | ||
this.nested = false; | ||
this.skip(2); | ||
return this.stack[0]; | ||
} | ||
}; | ||
/** | ||
* '\' ('&' | '^') | ||
*/ | ||
/** | ||
* '\' ('&' | '^') | ||
*/ | ||
SelectorParser.prototype.escaped = function() { | ||
if ('\\' == this.str[0]) { | ||
var char = this.str[1]; | ||
if ('&' == char || '^' == char) { | ||
this.skip(2); | ||
return char; | ||
escaped() { | ||
if ('\\' == this.str[0]) { | ||
var char = this.str[1]; | ||
if ('&' == char || '^' == char) { | ||
this.skip(2); | ||
return char; | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/** | ||
* '&' | ||
*/ | ||
/** | ||
* '&' | ||
*/ | ||
SelectorParser.prototype.parent = function() { | ||
if ('&' == this.str[0]) { | ||
this.nested = false; | ||
parent() { | ||
if ('&' == this.str[0]) { | ||
this.nested = false; | ||
if (!this.pos && (!this.stack.length || this.raw)) { | ||
var i = 0; | ||
while (' ' == this.str[++i]) ; | ||
if (~COMBINATORS.indexOf(this.str[i])) { | ||
this.skip(i + 1); | ||
return; | ||
if (!this.pos && (!this.stack.length || this.raw)) { | ||
var i = 0; | ||
while (' ' == this.str[++i]); | ||
if (~COMBINATORS.indexOf(this.str[i])) { | ||
this.skip(i + 1); | ||
return; | ||
} | ||
} | ||
this.skip(1); | ||
if (!this.raw) | ||
return this.stack[this.stack.length - 1]; | ||
} | ||
}; | ||
this.skip(1); | ||
if (!this.raw) | ||
return this.stack[this.stack.length - 1]; | ||
} | ||
}; | ||
/** | ||
* '^[' range ']' | ||
*/ | ||
/** | ||
* '^[' range ']' | ||
*/ | ||
SelectorParser.prototype.partial = function() { | ||
if ('^' == this.str[0] && '[' == this.str[1]) { | ||
this.skip(2); | ||
this.skipSpaces(); | ||
var ret = this.range(); | ||
this.skipSpaces(); | ||
if (']' != this.str[0]) return '^['; | ||
this.nested = false; | ||
this.skip(1); | ||
if (ret) { | ||
return ret; | ||
} else { | ||
this.ignore = true; | ||
partial() { | ||
if ('^' == this.str[0] && '[' == this.str[1]) { | ||
this.skip(2); | ||
this.skipSpaces(); | ||
var ret = this.range(); | ||
this.skipSpaces(); | ||
if (']' != this.str[0]) return '^['; | ||
this.nested = false; | ||
this.skip(1); | ||
if (ret) { | ||
return ret; | ||
} else { | ||
this.ignore = true; | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/** | ||
* '-'? 0-9+ | ||
*/ | ||
/** | ||
* '-'? 0-9+ | ||
*/ | ||
SelectorParser.prototype.number = function() { | ||
var i = 0, ret = ''; | ||
if ('-' == this.str[i]) | ||
ret += this.str[i++]; | ||
number() { | ||
var i = 0, ret = ''; | ||
if ('-' == this.str[i]) | ||
ret += this.str[i++]; | ||
while (this.str.charCodeAt(i) >= 48 | ||
&& this.str.charCodeAt(i) <= 57) | ||
ret += this.str[i++]; | ||
while (this.str.charCodeAt(i) >= 48 | ||
&& this.str.charCodeAt(i) <= 57) | ||
ret += this.str[i++]; | ||
if (ret) { | ||
this.skip(i); | ||
return Number(ret); | ||
} | ||
}; | ||
if (ret) { | ||
this.skip(i); | ||
return Number(ret); | ||
} | ||
}; | ||
/** | ||
* number ('..' number)? | ||
*/ | ||
/** | ||
* number ('..' number)? | ||
*/ | ||
SelectorParser.prototype.range = function() { | ||
var start = this.number() | ||
, ret; | ||
range() { | ||
var start = this.number() | ||
, ret; | ||
if ('..' == this.str.slice(0, 2)) { | ||
this.skip(2); | ||
var end = this.number() | ||
, len = this.parts.length; | ||
if ('..' == this.str.slice(0, 2)) { | ||
this.skip(2); | ||
var end = this.number() | ||
, len = this.parts.length; | ||
if (start < 0) start = len + start - 1; | ||
if (end < 0) end = len + end - 1; | ||
if (start < 0) start = len + start - 1; | ||
if (end < 0) end = len + end - 1; | ||
if (start > end) { | ||
var tmp = start; | ||
start = end; | ||
end = tmp; | ||
if (start > end) { | ||
var tmp = start; | ||
start = end; | ||
end = tmp; | ||
} | ||
if (end < len - 1) { | ||
ret = this.parts.slice(start, end + 1).map(function (part) { | ||
var selector = new SelectorParser(part, this.stack, this.parts); | ||
selector.raw = true; | ||
return selector.parse(); | ||
}, this).map(function (selector) { | ||
return (selector.nested ? ' ' : '') + selector.val; | ||
}).join('').trim(); | ||
} | ||
} else { | ||
ret = this.stack[ | ||
start < 0 ? this.stack.length + start - 1 : start | ||
]; | ||
} | ||
if (end < len - 1) { | ||
ret = this.parts.slice(start, end + 1).map(function(part) { | ||
var selector = new SelectorParser(part, this.stack, this.parts); | ||
selector.raw = true; | ||
return selector.parse(); | ||
}, this).map(function(selector) { | ||
return (selector.nested ? ' ' : '') + selector.val; | ||
}).join('').trim(); | ||
if (ret) { | ||
return ret; | ||
} else { | ||
this.ignore = true; | ||
} | ||
} else { | ||
ret = this.stack[ | ||
start < 0 ? this.stack.length + start - 1 : start | ||
]; | ||
} | ||
}; | ||
if (ret) { | ||
return ret; | ||
} else { | ||
this.ignore = true; | ||
} | ||
}; | ||
/** | ||
* .+ | ||
*/ | ||
/** | ||
* .+ | ||
*/ | ||
char() { | ||
var char = this.str[0]; | ||
this.skip(1); | ||
return char; | ||
}; | ||
SelectorParser.prototype.char = function() { | ||
var char = this.str[0]; | ||
this.skip(1); | ||
return char; | ||
}; | ||
/** | ||
* Parses the selector. | ||
* | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
/** | ||
* Parses the selector. | ||
* | ||
* @return {Object} | ||
* @api private | ||
*/ | ||
SelectorParser.prototype.parse = function() { | ||
var val = ''; | ||
while (this.str.length) { | ||
val += this.advance() || ''; | ||
if (this.ignore) { | ||
val = ''; | ||
break; | ||
parse() { | ||
var val = ''; | ||
while (this.str.length) { | ||
val += this.advance() || ''; | ||
if (this.ignore) { | ||
val = ''; | ||
break; | ||
} | ||
} | ||
} | ||
return { val: val.trimRight(), nested: this.nested }; | ||
return { val: val.trimRight(), nested: this.nested }; | ||
}; | ||
}; |
@@ -14,53 +14,55 @@ | ||
/** | ||
* Initialize a new `Frame` with the given `block`. | ||
* | ||
* @param {Block} block | ||
* @api private | ||
*/ | ||
module.exports = class Frame { | ||
/** | ||
* Initialize a new `Frame` with the given `block`. | ||
* | ||
* @param {Block} block | ||
* @api private | ||
*/ | ||
var Frame = module.exports = function Frame(block) { | ||
this._scope = false === block.scope | ||
? null | ||
: new Scope; | ||
this.block = block; | ||
}; | ||
constructor(block) { | ||
this._scope = false === block.scope | ||
? null | ||
: new Scope; | ||
this.block = block; | ||
} | ||
/** | ||
* Return this frame's scope or the parent scope | ||
* for scope-less blocks. | ||
* | ||
* @return {Scope} | ||
* @api public | ||
*/ | ||
/** | ||
* Return this frame's scope or the parent scope | ||
* for scope-less blocks. | ||
* | ||
* @return {Scope} | ||
* @api public | ||
*/ | ||
Frame.prototype.__defineGetter__('scope', function(){ | ||
return this._scope || this.parent.scope; | ||
}); | ||
get scope() { | ||
return this._scope || this.parent.scope; | ||
}; | ||
/** | ||
* Lookup the given local variable `name`. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup the given local variable `name`. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Frame.prototype.lookup = function(name){ | ||
return this.scope.lookup(name) | ||
}; | ||
lookup(name) { | ||
return this.scope.lookup(name) | ||
}; | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Frame.prototype.inspect = function(){ | ||
return '[Frame ' | ||
+ (false === this.block.scope | ||
inspect() { | ||
return '[Frame ' | ||
+ (false === this.block.scope | ||
? 'scope-less' | ||
: this.scope.inspect()) | ||
+ ']'; | ||
+ ']'; | ||
}; | ||
}; |
@@ -8,129 +8,126 @@ | ||
/** | ||
* Initialize a new `Stack`. | ||
* | ||
* @api private | ||
*/ | ||
module.exports = class Stack extends Array { | ||
/** | ||
* Initialize a new `Stack`. | ||
* | ||
* @api private | ||
*/ | ||
var Stack = module.exports = function Stack() { | ||
Array.apply(this, arguments); | ||
}; | ||
constructor() { | ||
super() | ||
Array.apply(this, arguments); | ||
} | ||
/** | ||
* Inherit from `Array.prototype`. | ||
*/ | ||
/** | ||
* Push the given `frame`. | ||
* | ||
* @param {Frame} frame | ||
* @api public | ||
*/ | ||
Stack.prototype.__proto__ = Array.prototype; | ||
push(frame) { | ||
frame.stack = this; | ||
frame.parent = this.currentFrame; | ||
return [].push.apply(this, arguments); | ||
}; | ||
/** | ||
* Push the given `frame`. | ||
* | ||
* @param {Frame} frame | ||
* @api public | ||
*/ | ||
/** | ||
* Return the current stack `Frame`. | ||
* | ||
* @return {Frame} | ||
* @api private | ||
*/ | ||
Stack.prototype.push = function(frame){ | ||
frame.stack = this; | ||
frame.parent = this.currentFrame; | ||
return [].push.apply(this, arguments); | ||
}; | ||
get currentFrame() { | ||
return this[this.length - 1]; | ||
}; | ||
/** | ||
* Return the current stack `Frame`. | ||
* | ||
* @return {Frame} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup stack frame for the given `block`. | ||
* | ||
* @param {Block} block | ||
* @return {Frame} | ||
* @api private | ||
*/ | ||
Stack.prototype.__defineGetter__('currentFrame', function(){ | ||
return this[this.length - 1]; | ||
}); | ||
/** | ||
* Lookup stack frame for the given `block`. | ||
* | ||
* @param {Block} block | ||
* @return {Frame} | ||
* @api private | ||
*/ | ||
Stack.prototype.getBlockFrame = function(block){ | ||
for (var i = 0; i < this.length; ++i) { | ||
if (block == this[i].block) { | ||
return this[i]; | ||
getBlockFrame(block) { | ||
for (var i = 0; i < this.length; ++i) { | ||
if (block == this[i].block) { | ||
return this[i]; | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/** | ||
* Lookup the given local variable `name`, relative | ||
* to the lexical scope of the current frame's `Block`. | ||
* | ||
* When the result of a lookup is an identifier | ||
* a recursive lookup is performed, defaulting to | ||
* returning the identifier itself. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup the given local variable `name`, relative | ||
* to the lexical scope of the current frame's `Block`. | ||
* | ||
* When the result of a lookup is an identifier | ||
* a recursive lookup is performed, defaulting to | ||
* returning the identifier itself. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Stack.prototype.lookup = function(name){ | ||
var block = this.currentFrame.block | ||
, val | ||
, ret; | ||
lookup(name) { | ||
var block = this.currentFrame.block | ||
, val | ||
, ret; | ||
do { | ||
var frame = this.getBlockFrame(block); | ||
if (frame && (val = frame.lookup(name))) { | ||
return val; | ||
} | ||
} while (block = block.parent); | ||
}; | ||
do { | ||
var frame = this.getBlockFrame(block); | ||
if (frame && (val = frame.lookup(name))) { | ||
return val; | ||
} | ||
} while (block = block.parent); | ||
}; | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Stack.prototype.inspect = function(){ | ||
return this.reverse().map(function(frame){ | ||
return frame.inspect(); | ||
}).join('\n'); | ||
}; | ||
inspect() { | ||
return this.reverse().map(function (frame) { | ||
return frame.inspect(); | ||
}).join('\n'); | ||
}; | ||
/** | ||
* Return stack string formatted as: | ||
* | ||
* at <context> (<filename>:<lineno>:<column>) | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Return stack string formatted as: | ||
* | ||
* at <context> (<filename>:<lineno>:<column>) | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Stack.prototype.toString = function(){ | ||
var block | ||
, node | ||
, buf = [] | ||
, location | ||
, len = this.length; | ||
toString() { | ||
var block | ||
, node | ||
, buf = [] | ||
, location | ||
, len = this.length; | ||
while (len--) { | ||
block = this[len].block; | ||
if (node = block.node) { | ||
location = '(' + node.filename + ':' + (node.lineno + 1) + ':' + node.column + ')'; | ||
switch (node.nodeName) { | ||
case 'function': | ||
buf.push(' at ' + node.name + '() ' + location); | ||
break; | ||
case 'group': | ||
buf.push(' at "' + node.nodes[0].val + '" ' + location); | ||
break; | ||
while (len--) { | ||
block = this[len].block; | ||
if (node = block.node) { | ||
location = '(' + node.filename + ':' + (node.lineno + 1) + ':' + node.column + ')'; | ||
switch (node.nodeName) { | ||
case 'function': | ||
buf.push(' at ' + node.name + '() ' + location); | ||
break; | ||
case 'group': | ||
buf.push(' at "' + node.nodes[0].val + '" ' + location); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
return buf.join('\n'); | ||
return buf.join('\n'); | ||
}; | ||
}; |
@@ -8,56 +8,49 @@ | ||
/** | ||
* Initialize a new `Scope`. | ||
* | ||
* @api private | ||
*/ | ||
module.exports = class Scope { | ||
/** | ||
* Initialize a new `Scope`. | ||
* | ||
* @api private | ||
*/ | ||
var Scope = module.exports = function Scope() { | ||
this.locals = {}; | ||
}; | ||
constructor() { | ||
this.locals = {}; | ||
} | ||
/** | ||
* Add `ident` node to the current scope. | ||
* | ||
* @param {Ident} ident | ||
* @api private | ||
*/ | ||
/** | ||
* Add `ident` node to the current scope. | ||
* | ||
* @param {Ident} ident | ||
* @api private | ||
*/ | ||
Scope.prototype.add = function(ident){ | ||
this.locals[ident.name] = ident.val; | ||
}; | ||
add(ident) { | ||
this.locals[ident.name] = ident.val; | ||
}; | ||
/** | ||
* Lookup the given local variable `name`. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup the given local variable `name`. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Scope.prototype.lookup = function(name){ | ||
return hasOwnProperty(this.locals, name) ? this.locals[name] : undefined; | ||
}; | ||
lookup(name) { | ||
return this.locals.hasOwnProperty(name) ? this.locals[name] : undefined; | ||
}; | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Scope.prototype.inspect = function(){ | ||
var keys = Object.keys(this.locals).map(function(key){ return '@' + key; }); | ||
return '[Scope' | ||
+ (keys.length ? ' ' + keys.join(', ') : '') | ||
+ ']'; | ||
inspect() { | ||
var keys = Object.keys(this.locals).map(function (key) { return '@' + key; }); | ||
return '[Scope' | ||
+ (keys.length ? ' ' + keys.join(', ') : '') | ||
+ ']'; | ||
}; | ||
}; | ||
/** | ||
* @param {Object} obj | ||
* @param {String} propName | ||
* @returns {Boolean} | ||
*/ | ||
function hasOwnProperty(obj, propName) { | ||
return Object.prototype.hasOwnProperty.call(obj, propName); | ||
} |
@@ -14,41 +14,43 @@ | ||
/** | ||
* Initialize a new `Token` with the given `type` and `val`. | ||
* | ||
* @param {String} type | ||
* @param {Mixed} val | ||
* @api private | ||
*/ | ||
exports = module.exports = class Token { | ||
/** | ||
* Initialize a new `Token` with the given `type` and `val`. | ||
* | ||
* @param {String} type | ||
* @param {Mixed} val | ||
* @api private | ||
*/ | ||
var Token = exports = module.exports = function Token(type, val) { | ||
this.type = type; | ||
this.val = val; | ||
}; | ||
constructor(type, val) { | ||
this.type = type; | ||
this.val = val; | ||
} | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Custom inspect. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Token.prototype.inspect = function(){ | ||
var val = ' ' + inspect(this.val); | ||
return '[Token:' + this.lineno + ':' + this.column + ' ' | ||
+ '\x1b[32m' + this.type + '\x1b[0m' | ||
+ '\x1b[33m' + (this.val ? val : '') + '\x1b[0m' | ||
+ ']'; | ||
}; | ||
inspect() { | ||
var val = ' ' + inspect(this.val); | ||
return '[Token:' + this.lineno + ':' + this.column + ' ' | ||
+ '\x1b[32m' + this.type + '\x1b[0m' | ||
+ '\x1b[33m' + (this.val ? val : '') + '\x1b[0m' | ||
+ ']'; | ||
}; | ||
/** | ||
* Return type or val. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return type or val. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Token.prototype.toString = function(){ | ||
return (undefined === this.val | ||
? this.type | ||
: this.val).toString(); | ||
toString() { | ||
return (undefined === this.val | ||
? this.type | ||
: this.val).toString(); | ||
}; | ||
}; |
@@ -9,6 +9,14 @@ | ||
// units found in http://www.w3.org/TR/css3-values | ||
// and in https://www.w3.org/TR/css-values-4 | ||
module.exports = [ | ||
'em', 'ex', 'ch', 'rem' // relative lengths | ||
, 'vw', 'vh', 'vmin', 'vmax' // relative viewport-percentage lengths | ||
, 'vw', 'svw', 'lvw', 'dvw' // relative viewport-percentage lengths (including de-facto standard) | ||
, 'vh', 'svh', 'lvh', 'dvh' | ||
, 'vi', 'svi', 'lvi', 'dvi' | ||
, 'vb', 'svb', 'lvb', 'dvb' | ||
, 'vmin', 'svmin', 'lvmin', 'dvmin' | ||
, 'vmax', 'svmax', 'lvmax', 'dvmax' | ||
, 'cm', 'mm', 'in', 'pt', 'pc', 'px' // absolute lengths | ||
@@ -15,0 +23,0 @@ , 'deg', 'grad', 'rad', 'turn' // angles |
@@ -15,538 +15,543 @@ /*! | ||
/** | ||
* Initialize a new `Compiler` with the given `root` Node | ||
* and the following `options`. | ||
* | ||
* Options: | ||
* | ||
* - `compress` Compress the CSS output (default: false) | ||
* | ||
* @param {Node} root | ||
* @api public | ||
*/ | ||
module.exports = class Compiler extends Visitor { | ||
/** | ||
* Initialize a new `Compiler` with the given `root` Node | ||
* and the following `options`. | ||
* | ||
* Options: | ||
* | ||
* - `compress` Compress the CSS output (default: false) | ||
* | ||
* @param {Node} root | ||
* @api public | ||
*/ | ||
var Compiler = module.exports = function Compiler(root, options) { | ||
options = options || {}; | ||
this.compress = options.compress; | ||
this.firebug = options.firebug; | ||
this.linenos = options.linenos; | ||
this.spaces = options['indent spaces'] || 2; | ||
this.indents = 1; | ||
Visitor.call(this, root); | ||
this.stack = []; | ||
}; | ||
constructor(root, options) { | ||
super(root); | ||
options = options || {}; | ||
this.compress = options.compress; | ||
this.firebug = options.firebug; | ||
this.linenos = options.linenos; | ||
this.spaces = options['indent spaces'] || 2; | ||
this.indents = 1; | ||
this.stack = []; | ||
} | ||
/** | ||
* Inherit from `Visitor.prototype`. | ||
*/ | ||
/** | ||
* Compile to css, and return a string of CSS. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Compiler.prototype.__proto__ = Visitor.prototype; | ||
compile() { | ||
return this.visit(this.root); | ||
}; | ||
/** | ||
* Compile to css, and return a string of CSS. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Output `str` | ||
* | ||
* @param {String} str | ||
* @param {Node} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Compiler.prototype.compile = function(){ | ||
return this.visit(this.root); | ||
}; | ||
out(str, node) { | ||
return str; | ||
}; | ||
/** | ||
* Output `str` | ||
* | ||
* @param {String} str | ||
* @param {Node} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Return indentation string. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
Compiler.prototype.out = function(str, node){ | ||
return str; | ||
}; | ||
get indent() { | ||
if (this.compress) return ''; | ||
return new Array(this.indents).join(Array(this.spaces + 1).join(' ')); | ||
}; | ||
/** | ||
* Return indentation string. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Check if given `node` needs brackets. | ||
* | ||
* @param {Node} node | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
Compiler.prototype.__defineGetter__('indent', function(){ | ||
if (this.compress) return ''; | ||
return new Array(this.indents).join(Array(this.spaces + 1).join(' ')); | ||
}); | ||
needBrackets(node) { | ||
return 1 == this.indents | ||
|| 'atrule' != node.nodeName | ||
|| node.hasOnlyProperties; | ||
}; | ||
/** | ||
* Check if given `node` needs brackets. | ||
* | ||
* @param {Node} node | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Root. | ||
*/ | ||
Compiler.prototype.needBrackets = function(node){ | ||
return 1 == this.indents | ||
|| 'atrule' != node.nodeName | ||
|| node.hasOnlyProperties; | ||
}; | ||
visitRoot(block) { | ||
this.buf = ''; | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
var node = block.nodes[i]; | ||
if (this.linenos || this.firebug) this.debugInfo(node); | ||
var ret = this.visit(node); | ||
if (ret) this.buf += this.out(ret + '\n', node); | ||
} | ||
return this.buf; | ||
}; | ||
/** | ||
* Visit Root. | ||
*/ | ||
/** | ||
* Visit Block. | ||
*/ | ||
Compiler.prototype.visitRoot = function(block){ | ||
this.buf = ''; | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
var node = block.nodes[i]; | ||
if (this.linenos || this.firebug) this.debugInfo(node); | ||
var ret = this.visit(node); | ||
if (ret) this.buf += this.out(ret + '\n', node); | ||
} | ||
return this.buf; | ||
}; | ||
visitBlock(block) { | ||
var node | ||
, separator = this.compress ? '' : '\n' | ||
, needBrackets | ||
, lastPropertyIndex; | ||
/** | ||
* Visit Block. | ||
*/ | ||
if (block.hasProperties && !block.lacksRenderedSelectors) { | ||
needBrackets = this.needBrackets(block.node); | ||
Compiler.prototype.visitBlock = function(block){ | ||
var node | ||
, separator = this.compress ? '' : '\n' | ||
, needBrackets | ||
, lastPropertyIndex; | ||
if (block.hasProperties && !block.lacksRenderedSelectors) { | ||
needBrackets = this.needBrackets(block.node); | ||
if (this.compress) { | ||
if (this.compress) { | ||
for (var i = block.nodes.length - 1; i >= 0; --i) { | ||
if (block.nodes[i].nodeName === 'property') { | ||
lastPropertyIndex = i; | ||
break; | ||
} | ||
if (block.nodes[i].nodeName === 'property') { | ||
lastPropertyIndex = i; | ||
break; | ||
} | ||
} | ||
} | ||
if (needBrackets) { | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
} | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
this.last = lastPropertyIndex === i; | ||
node = block.nodes[i]; | ||
switch (node.nodeName) { | ||
case 'null': | ||
case 'expression': | ||
case 'function': | ||
case 'group': | ||
case 'block': | ||
case 'unit': | ||
case 'media': | ||
case 'keyframes': | ||
case 'atrule': | ||
case 'supports': | ||
continue; | ||
// inline comments | ||
case !this.compress && node.inline && 'comment': | ||
this.buf = this.buf.slice(0, -1); | ||
this.buf += this.out(' ' + this.visit(node) + '\n', node); | ||
break; | ||
case 'property': | ||
var ret = this.visit(node) + separator; | ||
this.buf += this.compress ? ret : this.out(ret, node); | ||
break; | ||
default: | ||
this.buf += this.out(this.visit(node) + separator, node); | ||
} | ||
} | ||
if (needBrackets) { | ||
--this.indents; | ||
this.buf += this.out(this.indent + '}' + separator); | ||
} | ||
} | ||
if (needBrackets) { | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
} | ||
// Nesting | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
this.last = lastPropertyIndex === i; | ||
node = block.nodes[i]; | ||
switch (node.nodeName) { | ||
case 'null': | ||
case 'expression': | ||
case 'function': | ||
case 'group': | ||
case 'block': | ||
case 'unit': | ||
case 'keyframes': | ||
if (this.linenos || this.firebug) this.debugInfo(node); | ||
this.visit(node); | ||
break; | ||
case 'media': | ||
case 'keyframes': | ||
case 'import': | ||
case 'atrule': | ||
case 'supports': | ||
continue; | ||
// inline comments | ||
case !this.compress && node.inline && 'comment': | ||
this.buf = this.buf.slice(0, -1); | ||
this.buf += this.out(' ' + this.visit(node) + '\n', node); | ||
this.visit(node); | ||
break; | ||
case 'property': | ||
var ret = this.visit(node) + separator; | ||
this.buf += this.compress ? ret : this.out(ret, node); | ||
case 'comment': | ||
// only show unsuppressed comments | ||
if (!node.suppress) { | ||
this.buf += this.out(this.indent + this.visit(node) + '\n', node); | ||
} | ||
break; | ||
default: | ||
this.buf += this.out(this.visit(node) + separator, node); | ||
case 'charset': | ||
case 'literal': | ||
case 'namespace': | ||
this.buf += this.out(this.visit(node) + '\n', node); | ||
break; | ||
} | ||
} | ||
if (needBrackets) { | ||
--this.indents; | ||
this.buf += this.out(this.indent + '}' + separator); | ||
} | ||
} | ||
}; | ||
// Nesting | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
node = block.nodes[i]; | ||
switch (node.nodeName) { | ||
case 'group': | ||
case 'block': | ||
case 'keyframes': | ||
if (this.linenos || this.firebug) this.debugInfo(node); | ||
this.visit(node); | ||
break; | ||
case 'media': | ||
case 'import': | ||
case 'atrule': | ||
case 'supports': | ||
this.visit(node); | ||
break; | ||
case 'comment': | ||
// only show unsuppressed comments | ||
if (!node.suppress) { | ||
this.buf += this.out(this.indent + this.visit(node) + '\n', node); | ||
} | ||
break; | ||
case 'charset': | ||
case 'literal': | ||
case 'namespace': | ||
this.buf += this.out(this.visit(node) + '\n', node); | ||
break; | ||
} | ||
} | ||
}; | ||
/** | ||
* Visit Keyframes. | ||
*/ | ||
/** | ||
* Visit Keyframes. | ||
*/ | ||
visitKeyframes(node) { | ||
if (!node.frames) return; | ||
Compiler.prototype.visitKeyframes = function(node){ | ||
if (!node.frames) return; | ||
var prefix = 'official' == node.prefix | ||
? '' | ||
: '-' + node.prefix + '-'; | ||
var prefix = 'official' == node.prefix | ||
? '' | ||
: '-' + node.prefix + '-'; | ||
this.buf += this.out('@' + prefix + 'keyframes ' | ||
+ this.visit(node.val) | ||
+ (this.compress ? '{' : ' {\n'), node); | ||
this.buf += this.out('@' + prefix + 'keyframes ' | ||
+ this.visit(node.val) | ||
+ (this.compress ? '{' : ' {\n'), node); | ||
this.keyframe = true; | ||
++this.indents; | ||
this.visit(node.block); | ||
--this.indents; | ||
this.keyframe = false; | ||
this.keyframe = true; | ||
++this.indents; | ||
this.visit(node.block); | ||
--this.indents; | ||
this.keyframe = false; | ||
this.buf += this.out('}' + (this.compress ? '' : '\n')); | ||
}; | ||
this.buf += this.out('}' + (this.compress ? '' : '\n')); | ||
}; | ||
/** | ||
* Visit Media. | ||
*/ | ||
/** | ||
* Visit Media. | ||
*/ | ||
visitMedia(media) { | ||
var val = media.val; | ||
if (!media.hasOutput || !val.nodes.length) return; | ||
Compiler.prototype.visitMedia = function(media){ | ||
var val = media.val; | ||
if (!media.hasOutput || !val.nodes.length) return; | ||
this.buf += this.out('@media ', media); | ||
this.visit(val); | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
this.visit(media.block); | ||
--this.indents; | ||
this.buf += this.out('}' + (this.compress ? '' : '\n')); | ||
}; | ||
this.buf += this.out('@media ', media); | ||
this.visit(val); | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
this.visit(media.block); | ||
--this.indents; | ||
this.buf += this.out('}' + (this.compress ? '' : '\n')); | ||
}; | ||
/** | ||
* Visit QueryList. | ||
*/ | ||
/** | ||
* Visit QueryList. | ||
*/ | ||
visitQueryList(queries) { | ||
for (var i = 0, len = queries.nodes.length; i < len; ++i) { | ||
this.visit(queries.nodes[i]); | ||
if (len - 1 != i) this.buf += this.out(',' + (this.compress ? '' : ' ')); | ||
} | ||
}; | ||
Compiler.prototype.visitQueryList = function(queries){ | ||
for (var i = 0, len = queries.nodes.length; i < len; ++i) { | ||
this.visit(queries.nodes[i]); | ||
if (len - 1 != i) this.buf += this.out(',' + (this.compress ? '' : ' ')); | ||
} | ||
}; | ||
/** | ||
* Visit Query. | ||
*/ | ||
/** | ||
* Visit Query. | ||
*/ | ||
visitQuery(node) { | ||
var len = node.nodes.length; | ||
if (node.predicate) this.buf += this.out(node.predicate + ' '); | ||
if (node.type) this.buf += this.out(node.type + (len ? ' and ' : '')); | ||
for (var i = 0; i < len; ++i) { | ||
this.buf += this.out(this.visit(node.nodes[i])); | ||
if (len - 1 != i) this.buf += this.out(' and '); | ||
} | ||
}; | ||
Compiler.prototype.visitQuery = function(node){ | ||
var len = node.nodes.length; | ||
if (node.predicate) this.buf += this.out(node.predicate + ' '); | ||
if (node.type) this.buf += this.out(node.type + (len ? ' and ' : '')); | ||
for (var i = 0; i < len; ++i) { | ||
this.buf += this.out(this.visit(node.nodes[i])); | ||
if (len - 1 != i) this.buf += this.out(' and '); | ||
} | ||
}; | ||
/** | ||
* Visit Feature. | ||
*/ | ||
/** | ||
* Visit Feature. | ||
*/ | ||
visitFeature(node) { | ||
if (!node.expr) { | ||
return node.name; | ||
} else if (node.expr.isEmpty) { | ||
return '(' + node.name + ')'; | ||
} else { | ||
return '(' + node.name + ':' + (this.compress ? '' : ' ') + this.visit(node.expr) + ')'; | ||
} | ||
}; | ||
Compiler.prototype.visitFeature = function(node){ | ||
if (!node.expr) { | ||
return node.name; | ||
} else if (node.expr.isEmpty) { | ||
return '(' + node.name + ')'; | ||
} else { | ||
return '(' + node.name + ':' + (this.compress ? '' : ' ') + this.visit(node.expr) + ')'; | ||
} | ||
}; | ||
/** | ||
* Visit Import. | ||
*/ | ||
/** | ||
* Visit Import. | ||
*/ | ||
visitImport(imported) { | ||
this.buf += this.out('@import ' + this.visit(imported.path) + ';\n', imported); | ||
}; | ||
Compiler.prototype.visitImport = function(imported){ | ||
this.buf += this.out('@import ' + this.visit(imported.path) + ';\n', imported); | ||
}; | ||
/** | ||
* Visit Atrule. | ||
*/ | ||
/** | ||
* Visit Atrule. | ||
*/ | ||
visitAtrule(atrule) { | ||
var newline = this.compress ? '' : '\n'; | ||
Compiler.prototype.visitAtrule = function(atrule){ | ||
var newline = this.compress ? '' : '\n'; | ||
this.buf += this.out(this.indent + '@' + atrule.type, atrule); | ||
this.buf += this.out(this.indent + '@' + atrule.type, atrule); | ||
if (atrule.val) this.buf += this.out(' ' + atrule.val.trim()); | ||
if (atrule.val) this.buf += this.out(' ' + atrule.val.trim()); | ||
if (atrule.block) { | ||
if (atrule.block.isEmpty) { | ||
this.buf += this.out((this.compress ? '' : ' ') + '{}' + newline); | ||
} else if (atrule.hasOnlyProperties) { | ||
this.visit(atrule.block); | ||
if (atrule.block) { | ||
if (atrule.block.isEmpty) { | ||
this.buf += this.out((this.compress ? '' : ' ') + '{}' + newline); | ||
} else if (atrule.hasOnlyProperties) { | ||
this.visit(atrule.block); | ||
} else { | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
this.visit(atrule.block); | ||
--this.indents; | ||
this.buf += this.out(this.indent + '}' + newline); | ||
} | ||
} else { | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
this.visit(atrule.block); | ||
--this.indents; | ||
this.buf += this.out(this.indent + '}' + newline); | ||
this.buf += this.out(';' + newline); | ||
} | ||
} else { | ||
this.buf += this.out(';' + newline); | ||
} | ||
}; | ||
}; | ||
/** | ||
* Visit Supports. | ||
*/ | ||
/** | ||
* Visit Supports. | ||
*/ | ||
Compiler.prototype.visitSupports = function(node){ | ||
if (!node.hasOutput) return; | ||
visitSupports(node) { | ||
if (!node.hasOutput) return; | ||
this.buf += this.out(this.indent + '@supports ', node); | ||
this.isCondition = true; | ||
this.buf += this.out(this.visit(node.condition)); | ||
this.isCondition = false; | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
this.visit(node.block); | ||
--this.indents; | ||
this.buf += this.out(this.indent + '}' + (this.compress ? '' : '\n')); | ||
}, | ||
this.buf += this.out(this.indent + '@supports ', node); | ||
this.isCondition = true; | ||
this.buf += this.out(this.visit(node.condition)); | ||
this.isCondition = false; | ||
this.buf += this.out(this.compress ? '{' : ' {\n'); | ||
++this.indents; | ||
this.visit(node.block); | ||
--this.indents; | ||
this.buf += this.out(this.indent + '}' + (this.compress ? '' : '\n')); | ||
} | ||
/** | ||
* Visit Comment. | ||
*/ | ||
/** | ||
* Visit Comment. | ||
*/ | ||
Compiler.prototype.visitComment = function(comment){ | ||
return this.compress | ||
? comment.suppress | ||
? '' | ||
: comment.str | ||
: comment.str; | ||
}; | ||
visitComment(comment) { | ||
return this.compress | ||
? comment.suppress | ||
? '' | ||
: comment.str | ||
: comment.str; | ||
}; | ||
/** | ||
* Visit Function. | ||
*/ | ||
/** | ||
* Visit Function. | ||
*/ | ||
Compiler.prototype.visitFunction = function(fn){ | ||
return fn.name; | ||
}; | ||
visitFunction(fn) { | ||
return fn.name; | ||
}; | ||
/** | ||
* Visit Charset. | ||
*/ | ||
/** | ||
* Visit Charset. | ||
*/ | ||
Compiler.prototype.visitCharset = function(charset){ | ||
return '@charset ' + this.visit(charset.val) + ';'; | ||
}; | ||
visitCharset(charset) { | ||
return '@charset ' + this.visit(charset.val) + ';'; | ||
}; | ||
/** | ||
* Visit Namespace. | ||
*/ | ||
/** | ||
* Visit Namespace. | ||
*/ | ||
Compiler.prototype.visitNamespace = function(namespace){ | ||
return '@namespace ' | ||
+ (namespace.prefix ? this.visit(namespace.prefix) + ' ' : '') | ||
+ this.visit(namespace.val) + ';'; | ||
}; | ||
visitNamespace(namespace) { | ||
return '@namespace ' | ||
+ (namespace.prefix ? this.visit(namespace.prefix) + ' ' : '') | ||
+ this.visit(namespace.val) + ';'; | ||
}; | ||
/** | ||
* Visit Literal. | ||
*/ | ||
/** | ||
* Visit Literal. | ||
*/ | ||
Compiler.prototype.visitLiteral = function(lit){ | ||
var val = lit.val; | ||
if (lit.css) val = val.replace(/^ /gm, ''); | ||
return val; | ||
}; | ||
visitLiteral(lit) { | ||
var val = lit.val; | ||
if (lit.css) val = val.replace(/^ /gm, ''); | ||
return val; | ||
}; | ||
/** | ||
* Visit Boolean. | ||
*/ | ||
/** | ||
* Visit Boolean. | ||
*/ | ||
Compiler.prototype.visitBoolean = function(bool){ | ||
return bool.toString(); | ||
}; | ||
visitBoolean(bool) { | ||
return bool.toString(); | ||
}; | ||
/** | ||
* Visit RGBA. | ||
*/ | ||
/** | ||
* Visit RGBA. | ||
*/ | ||
Compiler.prototype.visitRGBA = function(rgba){ | ||
return rgba.toString(); | ||
}; | ||
visitRGBA(rgba) { | ||
return rgba.toString(); | ||
}; | ||
/** | ||
* Visit HSLA. | ||
*/ | ||
/** | ||
* Visit HSLA. | ||
*/ | ||
Compiler.prototype.visitHSLA = function(hsla){ | ||
return hsla.rgba.toString(); | ||
}; | ||
visitHSLA(hsla) { | ||
return hsla.rgba.toString(); | ||
}; | ||
/** | ||
* Visit Unit. | ||
*/ | ||
/** | ||
* Visit Unit. | ||
*/ | ||
Compiler.prototype.visitUnit = function(unit){ | ||
var type = unit.type || '' | ||
, n = unit.val | ||
, float = n != (n | 0); | ||
visitUnit(unit) { | ||
var type = unit.type || '' | ||
, n = unit.val | ||
, float = n != (n | 0); | ||
// Compress | ||
if (this.compress) { | ||
// Always return '0' unless the unit is a percentage, time, degree or fraction | ||
if (!(['%', 's', 'ms', 'deg', 'fr'].includes(type)) && 0 == n) return '0'; | ||
// Omit leading '0' on floats | ||
if (float && n < 1 && n > -1) { | ||
return n.toString().replace('0.', '.') + type; | ||
// Compress | ||
if (this.compress) { | ||
// Always return '0' unless the unit is a percentage, time, degree or fraction | ||
if (!(['%', 's', 'ms', 'deg', 'fr'].includes(type)) && 0 == n) return '0'; | ||
// Omit leading '0' on floats | ||
if (float && n < 1 && n > -1) { | ||
return n.toString().replace('0.', '.') + type; | ||
} | ||
} | ||
} | ||
return (float ? parseFloat(n.toFixed(15)) : n).toString() + type; | ||
}; | ||
return (float ? parseFloat(n.toFixed(15)) : n).toString() + type; | ||
}; | ||
/** | ||
* Visit Group. | ||
*/ | ||
/** | ||
* Visit Group. | ||
*/ | ||
Compiler.prototype.visitGroup = function(group){ | ||
var stack = this.keyframe ? [] : this.stack | ||
, comma = this.compress ? ',' : ',\n'; | ||
visitGroup(group) { | ||
var stack = this.keyframe ? [] : this.stack | ||
, comma = this.compress ? ',' : ',\n'; | ||
stack.push(group.nodes); | ||
stack.push(group.nodes); | ||
// selectors | ||
if (group.block.hasProperties) { | ||
var selectors = utils.compileSelectors.call(this, stack) | ||
, len = selectors.length; | ||
// selectors | ||
if (group.block.hasProperties) { | ||
var selectors = utils.compileSelectors.call(this, stack) | ||
, len = selectors.length; | ||
if (len) { | ||
if (this.keyframe) comma = this.compress ? ',' : ', '; | ||
if (len) { | ||
if (this.keyframe) comma = this.compress ? ',' : ', '; | ||
for (var i = 0; i < len; ++i) { | ||
var selector = selectors[i] | ||
, last = (i == len - 1); | ||
for (var i = 0; i < len; ++i) { | ||
var selector = selectors[i] | ||
, last = (i == len - 1); | ||
// keyframe blocks (10%, 20% { ... }) | ||
if (this.keyframe) selector = i ? selector.trim() : selector; | ||
// keyframe blocks (10%, 20% { ... }) | ||
if (this.keyframe) selector = i ? selector.trim() : selector; | ||
this.buf += this.out(selector + (last ? '' : comma), group.nodes[i]); | ||
this.buf += this.out(selector + (last ? '' : comma), group.nodes[i]); | ||
} | ||
} else { | ||
group.block.lacksRenderedSelectors = true; | ||
} | ||
} else { | ||
group.block.lacksRenderedSelectors = true; | ||
} | ||
} | ||
// output block | ||
this.visit(group.block); | ||
stack.pop(); | ||
}; | ||
// output block | ||
this.visit(group.block); | ||
stack.pop(); | ||
}; | ||
/** | ||
* Visit Ident. | ||
*/ | ||
/** | ||
* Visit Ident. | ||
*/ | ||
Compiler.prototype.visitIdent = function(ident){ | ||
return ident.name; | ||
}; | ||
visitIdent(ident) { | ||
return ident.name; | ||
}; | ||
/** | ||
* Visit String. | ||
*/ | ||
/** | ||
* Visit String. | ||
*/ | ||
Compiler.prototype.visitString = function(string){ | ||
return this.isURL | ||
? string.val | ||
: string.toString(); | ||
}; | ||
visitString(string) { | ||
return this.isURL | ||
? string.val | ||
: string.toString(); | ||
}; | ||
/** | ||
* Visit Null. | ||
*/ | ||
/** | ||
* Visit Null. | ||
*/ | ||
Compiler.prototype.visitNull = function(node){ | ||
return ''; | ||
}; | ||
visitNull(node) { | ||
return ''; | ||
}; | ||
/** | ||
* Visit Call. | ||
*/ | ||
/** | ||
* Visit Call. | ||
*/ | ||
Compiler.prototype.visitCall = function(call){ | ||
this.isURL = 'url' == call.name; | ||
var args = call.args.nodes.map(function(arg){ | ||
return this.visit(arg); | ||
}, this).join(this.compress ? ',' : ', '); | ||
if (this.isURL) args = '"' + args + '"'; | ||
this.isURL = false; | ||
return call.name + '(' + args + ')'; | ||
}; | ||
visitCall(call) { | ||
this.isURL = 'url' == call.name; | ||
var args = call.args.nodes.map(function (arg) { | ||
return this.visit(arg); | ||
}, this).join(this.compress ? ',' : ', '); | ||
if (this.isURL) args = '"' + args + '"'; | ||
this.isURL = false; | ||
return call.name + '(' + args + ')'; | ||
}; | ||
/** | ||
* Visit Expression. | ||
*/ | ||
/** | ||
* Visit Expression. | ||
*/ | ||
Compiler.prototype.visitExpression = function(expr){ | ||
var buf = [] | ||
, self = this | ||
, len = expr.nodes.length | ||
, nodes = expr.nodes.map(function(node){ return self.visit(node); }); | ||
visitExpression(expr) { | ||
var buf = [] | ||
, self = this | ||
, len = expr.nodes.length | ||
, nodes = expr.nodes.map(function (node) { return self.visit(node); }); | ||
nodes.forEach(function(node, i){ | ||
var last = i == len - 1; | ||
buf.push(node); | ||
if ('/' == nodes[i + 1] || '/' == node) return; | ||
if (last) return; | ||
nodes.forEach(function (node, i) { | ||
var last = i == len - 1; | ||
buf.push(node); | ||
if ('/' == nodes[i + 1] || '/' == node) return; | ||
if (last) return; | ||
var space = self.isURL || (self.isCondition | ||
var space = self.isURL || (self.isCondition | ||
&& (')' == nodes[i + 1] || '(' == node)) | ||
? '' : ' '; | ||
buf.push(expr.isList | ||
? (self.compress ? ',' : ', ') | ||
: space); | ||
}); | ||
buf.push(expr.isList | ||
? (self.compress ? ',' : ', ') | ||
: space); | ||
}); | ||
return buf.join(''); | ||
}; | ||
return buf.join(''); | ||
}; | ||
/** | ||
* Visit Arguments. | ||
*/ | ||
/** | ||
* Visit Arguments. | ||
*/ | ||
Compiler.prototype.visitArguments = Compiler.prototype.visitExpression; | ||
get visitArguments() { | ||
return this.visitExpression; | ||
} | ||
/** | ||
* Visit Property. | ||
*/ | ||
/** | ||
* Visit Property. | ||
*/ | ||
Compiler.prototype.visitProperty = function(prop){ | ||
var val = this.visit(prop.expr).trim() | ||
, name = (prop.name || prop.segments.join('')) | ||
, arr = []; | ||
visitProperty(prop) { | ||
var val = this.visit(prop.expr).trim() | ||
, name = (prop.name || prop.segments.join('')) | ||
, arr = []; | ||
if (name === '@apply') { | ||
if (name === '@apply') { | ||
arr.push( | ||
this.out(this.indent), | ||
this.out(name + ' ', prop), | ||
this.out(val, prop.expr), | ||
this.out(this.compress ? (this.last ? '' : ';') : ';') | ||
); | ||
return arr.join(''); | ||
} | ||
arr.push( | ||
this.out(this.indent), | ||
this.out(name + ' ', prop), | ||
this.out(name + (this.compress ? ':' : ': '), prop), | ||
this.out(val, prop.expr), | ||
@@ -556,35 +561,29 @@ this.out(this.compress ? (this.last ? '' : ';') : ';') | ||
return arr.join(''); | ||
} | ||
arr.push( | ||
this.out(this.indent), | ||
this.out(name + (this.compress ? ':' : ': '), prop), | ||
this.out(val, prop.expr), | ||
this.out(this.compress ? (this.last ? '' : ';') : ';') | ||
); | ||
return arr.join(''); | ||
}; | ||
}; | ||
/** | ||
* Debug info. | ||
*/ | ||
/** | ||
* Debug info. | ||
*/ | ||
Compiler.prototype.debugInfo = function(node){ | ||
debugInfo(node) { | ||
var path = node.filename == 'stdin' ? 'stdin' : fs.realpathSync(node.filename) | ||
, line = (node.nodes && node.nodes.length ? node.nodes[0].lineno : node.lineno) || 1; | ||
var path = node.filename == 'stdin' ? 'stdin' : fs.realpathSync(node.filename) | ||
, line = (node.nodes && node.nodes.length ? node.nodes[0].lineno : node.lineno) || 1; | ||
if (this.linenos){ | ||
this.buf += '\n/* ' + 'line ' + line + ' : ' + path + ' */\n'; | ||
if (this.linenos) { | ||
this.buf += '\n/* ' + 'line ' + line + ' : ' + path + ' */\n'; | ||
} | ||
if (this.firebug) { | ||
// debug info for firebug, the crazy formatting is needed | ||
path = 'file\\\:\\\/\\\/' + path.replace(/([.:/\\])/g, function (m) { | ||
return '\\' + (m === '\\' ? '\/' : m) | ||
}); | ||
line = '\\00003' + line; | ||
this.buf += '\n@media -stylus-debug-info' | ||
+ '{filename{font-family:' + path | ||
+ '}line{font-family:' + line + '}}\n'; | ||
} | ||
} | ||
if (this.firebug){ | ||
// debug info for firebug, the crazy formatting is needed | ||
path = 'file\\\:\\\/\\\/' + path.replace(/([.:/\\])/g, function(m) { | ||
return '\\' + (m === '\\' ? '\/' : m) | ||
}); | ||
line = '\\00003' + line; | ||
this.buf += '\n@media -stylus-debug-info' | ||
+ '{filename{font-family:' + path | ||
+ '}line{font-family:' + line + '}}\n'; | ||
} | ||
} | ||
}; |
@@ -13,161 +13,156 @@ | ||
/** | ||
* Initialize a new `DepsResolver` with the given `root` Node | ||
* and the `options`. | ||
* | ||
* @param {Node} root | ||
* @param {Object} options | ||
* @api private | ||
*/ | ||
module.exports = class DepsResolver extends Visitor { | ||
/** | ||
* Initialize a new `DepsResolver` with the given `root` Node | ||
* and the `options`. | ||
* | ||
* @param {Node} root | ||
* @param {Object} options | ||
* @api private | ||
*/ | ||
var DepsResolver = module.exports = function DepsResolver(root, options) { | ||
this.root = root; | ||
this.filename = options.filename; | ||
this.paths = options.paths || []; | ||
this.paths.push(dirname(options.filename || '.')); | ||
this.options = options; | ||
this.functions = {}; | ||
this.deps = []; | ||
}; | ||
constructor(root, options) { | ||
super(root) | ||
this.filename = options.filename; | ||
this.paths = options.paths || []; | ||
this.paths.push(dirname(options.filename || '.')); | ||
this.options = options; | ||
this.functions = {}; | ||
this.deps = []; | ||
} | ||
/** | ||
* Inherit from `Visitor.prototype`. | ||
*/ | ||
DepsResolver.prototype.__proto__ = Visitor.prototype; | ||
visit(node) { | ||
switch (node.nodeName) { | ||
case 'root': | ||
case 'block': | ||
case 'expression': | ||
this.visitRoot(node); | ||
break; | ||
case 'group': | ||
case 'media': | ||
case 'atblock': | ||
case 'atrule': | ||
case 'keyframes': | ||
case 'each': | ||
case 'supports': | ||
this.visit(node.block); | ||
break; | ||
default: | ||
super.visit(node); | ||
} | ||
}; | ||
var visit = DepsResolver.prototype.visit; | ||
/** | ||
* Visit Root. | ||
*/ | ||
DepsResolver.prototype.visit = function(node) { | ||
switch (node.nodeName) { | ||
case 'root': | ||
case 'block': | ||
case 'expression': | ||
this.visitRoot(node); | ||
break; | ||
case 'group': | ||
case 'media': | ||
case 'atblock': | ||
case 'atrule': | ||
case 'keyframes': | ||
case 'each': | ||
case 'supports': | ||
this.visit(node.block); | ||
break; | ||
default: | ||
visit.call(this, node); | ||
} | ||
}; | ||
visitRoot(block) { | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
this.visit(block.nodes[i]); | ||
} | ||
}; | ||
/** | ||
* Visit Root. | ||
*/ | ||
/** | ||
* Visit Ident. | ||
*/ | ||
DepsResolver.prototype.visitRoot = function(block) { | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
this.visit(block.nodes[i]); | ||
} | ||
}; | ||
visitIdent(ident) { | ||
this.visit(ident.val); | ||
}; | ||
/** | ||
* Visit Ident. | ||
*/ | ||
/** | ||
* Visit If. | ||
*/ | ||
DepsResolver.prototype.visitIdent = function(ident) { | ||
this.visit(ident.val); | ||
}; | ||
visitIf(node) { | ||
this.visit(node.block); | ||
this.visit(node.cond); | ||
for (var i = 0, len = node.elses.length; i < len; ++i) { | ||
this.visit(node.elses[i]); | ||
} | ||
}; | ||
/** | ||
* Visit If. | ||
*/ | ||
/** | ||
* Visit Function. | ||
*/ | ||
DepsResolver.prototype.visitIf = function(node) { | ||
this.visit(node.block); | ||
this.visit(node.cond); | ||
for (var i = 0, len = node.elses.length; i < len; ++i) { | ||
this.visit(node.elses[i]); | ||
} | ||
}; | ||
visitFunction(fn) { | ||
this.functions[fn.name] = fn.block; | ||
}; | ||
/** | ||
* Visit Function. | ||
*/ | ||
/** | ||
* Visit Call. | ||
*/ | ||
DepsResolver.prototype.visitFunction = function(fn) { | ||
this.functions[fn.name] = fn.block; | ||
}; | ||
visitCall(call) { | ||
if (call.name in this.functions) this.visit(this.functions[call.name]); | ||
if (call.block) this.visit(call.block); | ||
}; | ||
/** | ||
* Visit Call. | ||
*/ | ||
/** | ||
* Visit Import. | ||
*/ | ||
DepsResolver.prototype.visitCall = function(call) { | ||
if (call.name in this.functions) this.visit(this.functions[call.name]); | ||
if (call.block) this.visit(call.block); | ||
}; | ||
visitImport(node) { | ||
// If it's a url() call, skip | ||
if (node.path.first.name === 'url') return; | ||
/** | ||
* Visit Import. | ||
*/ | ||
var path = !node.path.first.val.isNull && node.path.first.val || node.path.first.name | ||
, literal, found, oldPath; | ||
DepsResolver.prototype.visitImport = function(node) { | ||
// If it's a url() call, skip | ||
if (node.path.first.name === 'url') return; | ||
if (!path) return; | ||
var path = !node.path.first.val.isNull && node.path.first.val || node.path.first.name | ||
, literal, found, oldPath; | ||
literal = /\.css(?:"|$)/.test(path); | ||
if (!path) return; | ||
// support optional .styl | ||
if (!literal && !/\.styl$/i.test(path)) { | ||
oldPath = path; | ||
path += '.styl'; | ||
} | ||
literal = /\.css(?:"|$)/.test(path); | ||
// Lookup | ||
found = utils.find(path, this.paths, this.filename); | ||
// support optional .styl | ||
if (!literal && !/\.styl$/i.test(path)) { | ||
oldPath = path; | ||
path += '.styl'; | ||
} | ||
// support optional index | ||
if (!found && oldPath) found = utils.lookupIndex(oldPath, this.paths, this.filename); | ||
// Lookup | ||
found = utils.find(path, this.paths, this.filename); | ||
if (!found) return; | ||
// support optional index | ||
if (!found && oldPath) found = utils.lookupIndex(oldPath, this.paths, this.filename); | ||
this.deps = this.deps.concat(found); | ||
if (!found) return; | ||
if (literal) return; | ||
this.deps = this.deps.concat(found); | ||
// nested imports | ||
for (var i = 0, len = found.length; i < len; ++i) { | ||
var file = found[i] | ||
, dir = dirname(file) | ||
, str = fs.readFileSync(file, 'utf-8') | ||
, block = new nodes.Block | ||
, parser = new Parser(str, utils.merge({ root: block }, this.options)); | ||
if (literal) return; | ||
if (!~this.paths.indexOf(dir)) this.paths.push(dir); | ||
// nested imports | ||
for (var i = 0, len = found.length; i < len; ++i) { | ||
var file = found[i] | ||
, dir = dirname(file) | ||
, str = fs.readFileSync(file, 'utf-8') | ||
, block = new nodes.Block | ||
, parser = new Parser(str, utils.merge({ root: block }, this.options)); | ||
try { | ||
block = parser.parse(); | ||
} catch (err) { | ||
err.filename = file; | ||
err.lineno = parser.lexer.lineno; | ||
err.column = parser.lexer.column; | ||
err.input = str; | ||
throw err; | ||
} | ||
if (!~this.paths.indexOf(dir)) this.paths.push(dir); | ||
try { | ||
block = parser.parse(); | ||
} catch (err) { | ||
err.filename = file; | ||
err.lineno = parser.lexer.lineno; | ||
err.column = parser.lexer.column; | ||
err.input = str; | ||
throw err; | ||
this.visit(block); | ||
} | ||
}; | ||
this.visit(block); | ||
} | ||
}; | ||
/** | ||
* Get dependencies. | ||
*/ | ||
/** | ||
* Get dependencies. | ||
*/ | ||
DepsResolver.prototype.resolve = function() { | ||
this.visit(this.root); | ||
return utils.uniq(this.deps); | ||
resolve() { | ||
this.visit(this.root); | ||
return utils.uniq(this.deps); | ||
}; | ||
}; |
@@ -106,1509 +106,1506 @@ | ||
/** | ||
* Initialize a new `Evaluator` with the given `root` Node | ||
* and the following `options`. | ||
* | ||
* Options: | ||
* | ||
* - `compress` Compress the css output, defaults to false | ||
* - `warn` Warn the user of duplicate function definitions etc | ||
* | ||
* @param {Node} root | ||
* @api private | ||
*/ | ||
module.exports = class Evaluator extends Visitor { | ||
/** | ||
* Initialize a new `Evaluator` with the given `root` Node | ||
* and the following `options`. | ||
* | ||
* Options: | ||
* | ||
* - `compress` Compress the css output, defaults to false | ||
* - `warn` Warn the user of duplicate function definitions etc | ||
* | ||
* @param {Node} root | ||
* @api private | ||
*/ | ||
var Evaluator = module.exports = function Evaluator(root, options) { | ||
options = options || {}; | ||
Visitor.call(this, root); | ||
var functions = this.functions = options.functions || {}; | ||
this.stack = new Stack; | ||
this.imports = options.imports || []; | ||
this.globals = options.globals || {}; | ||
this.paths = options.paths || []; | ||
this.prefix = options.prefix || ''; | ||
this.filename = options.filename; | ||
this.includeCSS = options['include css']; | ||
this.resolveURL = functions.url | ||
&& 'resolver' == functions.url.name | ||
&& functions.url.options; | ||
this.paths.push(dirname(options.filename || '.')); | ||
this.stack.push(this.global = new Frame(root)); | ||
this.warnings = options.warn; | ||
this.options = options; | ||
this.calling = []; // TODO: remove, use stack | ||
this.importStack = []; | ||
this.requireHistory = {}; | ||
this.return = 0; | ||
}; | ||
constructor(root, options) { | ||
super(root); | ||
options = options || {}; | ||
var functions = this.functions = options.functions || {}; | ||
this.stack = new Stack; | ||
this.imports = options.imports || []; | ||
this.globals = options.globals || {}; | ||
this.paths = options.paths || []; | ||
this.prefix = options.prefix || ''; | ||
this.filename = options.filename; | ||
this.includeCSS = options['include css']; | ||
this.resolveURL = functions.url | ||
&& 'resolver' == functions.url.name | ||
&& functions.url.options; | ||
this.paths.push(dirname(options.filename || '.')); | ||
this.stack.push(this.global = new Frame(root)); | ||
this.warnings = options.warn; | ||
this.options = options; | ||
this.calling = []; // TODO: remove, use stack | ||
this.importStack = []; | ||
this.requireHistory = {}; | ||
this.return = 0; | ||
} | ||
/** | ||
* Inherit from `Visitor.prototype`. | ||
*/ | ||
/** | ||
* Proxy visit to expose node line numbers. | ||
* | ||
* @param {Node} node | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.__proto__ = Visitor.prototype; | ||
/** | ||
* Proxy visit to expose node line numbers. | ||
* | ||
* @param {Node} node | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
var visit = Visitor.prototype.visit; | ||
Evaluator.prototype.visit = function(node){ | ||
try { | ||
return visit.call(this, node); | ||
} catch (err) { | ||
if (err.filename) throw err; | ||
err.lineno = node.lineno; | ||
err.column = node.column; | ||
err.filename = node.filename; | ||
err.stylusStack = this.stack.toString(); | ||
visit(node) { | ||
try { | ||
err.input = fs.readFileSync(err.filename, 'utf8'); | ||
return super.visit(node); | ||
} catch (err) { | ||
// ignore | ||
if (err.filename) throw err; | ||
err.lineno = node.lineno; | ||
err.column = node.column; | ||
err.filename = node.filename; | ||
err.stylusStack = this.stack.toString(); | ||
try { | ||
err.input = fs.readFileSync(err.filename, 'utf8'); | ||
} catch (err) { | ||
// ignore | ||
} | ||
throw err; | ||
} | ||
throw err; | ||
} | ||
}; | ||
}; | ||
/** | ||
* Perform evaluation setup: | ||
* | ||
* - populate global scope | ||
* - iterate imports | ||
* | ||
* @api private | ||
*/ | ||
/** | ||
* Perform evaluation setup: | ||
* | ||
* - populate global scope | ||
* - iterate imports | ||
* | ||
* @api private | ||
*/ | ||
Evaluator.prototype.setup = function(){ | ||
var root = this.root; | ||
var imports = []; | ||
setup() { | ||
var root = this.root; | ||
var imports = []; | ||
this.populateGlobalScope(); | ||
this.imports.forEach(function(file){ | ||
var expr = new nodes.Expression; | ||
expr.push(new nodes.String(file)); | ||
imports.push(new nodes.Import(expr)); | ||
}, this); | ||
this.populateGlobalScope(); | ||
this.imports.forEach(function (file) { | ||
var expr = new nodes.Expression; | ||
expr.push(new nodes.String(file)); | ||
imports.push(new nodes.Import(expr)); | ||
}, this); | ||
root.nodes = imports.concat(root.nodes); | ||
}; | ||
root.nodes = imports.concat(root.nodes); | ||
}; | ||
/** | ||
* Populate the global scope with: | ||
* | ||
* - css colors | ||
* - user-defined globals | ||
* | ||
* @api private | ||
*/ | ||
/** | ||
* Populate the global scope with: | ||
* | ||
* - css colors | ||
* - user-defined globals | ||
* | ||
* @api private | ||
*/ | ||
Evaluator.prototype.populateGlobalScope = function(){ | ||
var scope = this.global.scope; | ||
populateGlobalScope() { | ||
var scope = this.global.scope; | ||
// colors | ||
Object.keys(colors).forEach(function(name){ | ||
var color = colors[name] | ||
, rgba = new nodes.RGBA(color[0], color[1], color[2], color[3]) | ||
, node = new nodes.Ident(name, rgba); | ||
rgba.name = name; | ||
scope.add(node); | ||
}); | ||
// colors | ||
Object.keys(colors).forEach(function (name) { | ||
var color = colors[name] | ||
, rgba = new nodes.RGBA(color[0], color[1], color[2], color[3]) | ||
, node = new nodes.Ident(name, rgba); | ||
rgba.name = name; | ||
scope.add(node); | ||
}); | ||
// expose url function | ||
scope.add(new nodes.Ident( | ||
'embedurl', | ||
new nodes.Function('embedurl', require('../functions/url')({ | ||
limit: false | ||
})) | ||
)); | ||
// expose url function | ||
scope.add(new nodes.Ident( | ||
'embedurl', | ||
new nodes.Function('embedurl', require('../functions/url')({ | ||
limit: false | ||
})) | ||
)); | ||
// user-defined globals | ||
var globals = this.globals; | ||
Object.keys(globals).forEach(function(name){ | ||
var val = globals[name]; | ||
if (!val.nodeName) val = new nodes.Literal(val); | ||
scope.add(new nodes.Ident(name, val)); | ||
}); | ||
}; | ||
// user-defined globals | ||
var globals = this.globals; | ||
Object.keys(globals).forEach(function (name) { | ||
var val = globals[name]; | ||
if (!val.nodeName) val = new nodes.Literal(val); | ||
scope.add(new nodes.Ident(name, val)); | ||
}); | ||
}; | ||
/** | ||
* Evaluate the tree. | ||
* | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Evaluate the tree. | ||
* | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.evaluate = function(){ | ||
debug('eval %s', this.filename); | ||
this.setup(); | ||
return this.visit(this.root); | ||
}; | ||
evaluate() { | ||
debug('eval %s', this.filename); | ||
this.setup(); | ||
return this.visit(this.root); | ||
}; | ||
/** | ||
* Visit Group. | ||
*/ | ||
/** | ||
* Visit Group. | ||
*/ | ||
Evaluator.prototype.visitGroup = function(group){ | ||
group.nodes = group.nodes.map(function(selector){ | ||
selector.val = this.interpolate(selector); | ||
debug('ruleset %s', selector.val); | ||
return selector; | ||
}, this); | ||
visitGroup(group) { | ||
group.nodes = group.nodes.map(function (selector) { | ||
selector.val = this.interpolate(selector); | ||
debug('ruleset %s', selector.val); | ||
return selector; | ||
}, this); | ||
group.block = this.visit(group.block); | ||
return group; | ||
}; | ||
group.block = this.visit(group.block); | ||
return group; | ||
}; | ||
/** | ||
* Visit Return. | ||
*/ | ||
/** | ||
* Visit Return. | ||
*/ | ||
Evaluator.prototype.visitReturn = function(ret){ | ||
ret.expr = this.visit(ret.expr); | ||
throw ret; | ||
}; | ||
visitReturn(ret) { | ||
ret.expr = this.visit(ret.expr); | ||
throw ret; | ||
}; | ||
/** | ||
* Visit Media. | ||
*/ | ||
/** | ||
* Visit Media. | ||
*/ | ||
Evaluator.prototype.visitMedia = function(media){ | ||
media.block = this.visit(media.block); | ||
media.val = this.visit(media.val); | ||
return media; | ||
}; | ||
visitMedia(media) { | ||
media.block = this.visit(media.block); | ||
media.val = this.visit(media.val); | ||
return media; | ||
}; | ||
/** | ||
* Visit QueryList. | ||
*/ | ||
/** | ||
* Visit QueryList. | ||
*/ | ||
Evaluator.prototype.visitQueryList = function(queries){ | ||
var val, query; | ||
queries.nodes.forEach(this.visit, this); | ||
visitQueryList(queries) { | ||
var val, query; | ||
queries.nodes.forEach(this.visit, this); | ||
if (1 == queries.nodes.length) { | ||
query = queries.nodes[0]; | ||
if (val = this.lookup(query.type)) { | ||
val = val.first.string; | ||
if (!val) return queries; | ||
var Parser = require('../parser') | ||
, parser = new Parser(val, this.options); | ||
queries = this.visit(parser.queries()); | ||
if (1 == queries.nodes.length) { | ||
query = queries.nodes[0]; | ||
if (val = this.lookup(query.type)) { | ||
val = val.first.string; | ||
if (!val) return queries; | ||
var Parser = require('../parser') | ||
, parser = new Parser(val, this.options); | ||
queries = this.visit(parser.queries()); | ||
} | ||
} | ||
} | ||
return queries; | ||
}; | ||
return queries; | ||
}; | ||
/** | ||
* Visit Query. | ||
*/ | ||
/** | ||
* Visit Query. | ||
*/ | ||
Evaluator.prototype.visitQuery = function(node){ | ||
node.predicate = this.visit(node.predicate); | ||
node.type = this.visit(node.type); | ||
node.nodes.forEach(this.visit, this); | ||
return node; | ||
}; | ||
visitQuery(node) { | ||
node.predicate = this.visit(node.predicate); | ||
node.type = this.visit(node.type); | ||
node.nodes.forEach(this.visit, this); | ||
return node; | ||
}; | ||
/** | ||
* Visit Feature. | ||
*/ | ||
/** | ||
* Visit Feature. | ||
*/ | ||
Evaluator.prototype.visitFeature = function(node){ | ||
node.name = this.interpolate(node); | ||
if (node.expr) { | ||
this.return++; | ||
node.expr = this.visit(node.expr); | ||
this.return--; | ||
} | ||
return node; | ||
}; | ||
visitFeature(node) { | ||
node.name = this.interpolate(node); | ||
if (node.expr) { | ||
this.return++; | ||
node.expr = this.visit(node.expr); | ||
this.return--; | ||
} | ||
return node; | ||
}; | ||
/** | ||
* Visit Object. | ||
*/ | ||
/** | ||
* Visit Object. | ||
*/ | ||
Evaluator.prototype.visitObject = function(obj){ | ||
for (var key in obj.vals) { | ||
obj.vals[key] = this.visit(obj.vals[key]); | ||
} | ||
return obj; | ||
}; | ||
visitObject(obj) { | ||
for (var key in obj.vals) { | ||
obj.vals[key] = this.visit(obj.vals[key]); | ||
} | ||
return obj; | ||
}; | ||
/** | ||
* Visit Member. | ||
*/ | ||
/** | ||
* Visit Member. | ||
*/ | ||
Evaluator.prototype.visitMember = function(node){ | ||
var left = node.left | ||
, right = node.right | ||
, obj = this.visit(left).first; | ||
visitMember(node) { | ||
var left = node.left | ||
, right = node.right | ||
, obj = this.visit(left).first; | ||
if ('object' != obj.nodeName) { | ||
throw new Error(left.toString() + ' has no property .' + right); | ||
} | ||
if (node.val) { | ||
this.return++; | ||
obj.set(right.name, this.visit(node.val)); | ||
this.return--; | ||
} | ||
return obj.get(right.name); | ||
}; | ||
if ('object' != obj.nodeName) { | ||
throw new Error(left.toString() + ' has no property .' + right); | ||
} | ||
if (node.val) { | ||
this.return++; | ||
obj.set(right.name, this.visit(node.val)); | ||
this.return--; | ||
} | ||
return obj.get(right.name); | ||
}; | ||
/** | ||
* Visit Keyframes. | ||
*/ | ||
/** | ||
* Visit Keyframes. | ||
*/ | ||
Evaluator.prototype.visitKeyframes = function(keyframes){ | ||
var val; | ||
if (keyframes.fabricated) return keyframes; | ||
keyframes.val = this.interpolate(keyframes).trim(); | ||
if (val = this.lookup(keyframes.val)) { | ||
keyframes.val = val.first.string || val.first.name; | ||
} | ||
keyframes.block = this.visit(keyframes.block); | ||
visitKeyframes(keyframes) { | ||
var val; | ||
if (keyframes.fabricated) return keyframes; | ||
keyframes.val = this.interpolate(keyframes).trim(); | ||
if (val = this.lookup(keyframes.val)) { | ||
keyframes.val = val.first.string || val.first.name; | ||
} | ||
keyframes.block = this.visit(keyframes.block); | ||
if ('official' != keyframes.prefix) return keyframes; | ||
if ('official' != keyframes.prefix) return keyframes; | ||
this.vendors.forEach(function(prefix){ | ||
// IE never had prefixes for keyframes | ||
if ('ms' == prefix) return; | ||
var node = keyframes.clone(); | ||
node.val = keyframes.val; | ||
node.prefix = prefix; | ||
node.block = keyframes.block; | ||
node.fabricated = true; | ||
this.currentBlock.push(node); | ||
}, this); | ||
this.vendors.forEach(function (prefix) { | ||
// IE never had prefixes for keyframes | ||
if ('ms' == prefix) return; | ||
var node = keyframes.clone(); | ||
node.val = keyframes.val; | ||
node.prefix = prefix; | ||
node.block = keyframes.block; | ||
node.fabricated = true; | ||
this.currentBlock.push(node); | ||
}, this); | ||
return nodes.null; | ||
}; | ||
return nodes.null; | ||
}; | ||
/** | ||
* Visit Function. | ||
*/ | ||
/** | ||
* Visit Function. | ||
*/ | ||
Evaluator.prototype.visitFunction = function(fn){ | ||
// check local | ||
var local = this.stack.currentFrame.scope.lookup(fn.name); | ||
if (local) this.warn('local ' + local.nodeName + ' "' + fn.name + '" previously defined in this scope'); | ||
visitFunction(fn) { | ||
// check local | ||
var local = this.stack.currentFrame.scope.lookup(fn.name); | ||
if (local) this.warn('local ' + local.nodeName + ' "' + fn.name + '" previously defined in this scope'); | ||
// user-defined | ||
var user = this.functions[fn.name]; | ||
if (user) this.warn('user-defined function "' + fn.name + '" is already defined'); | ||
// user-defined | ||
var user = this.functions[fn.name]; | ||
if (user) this.warn('user-defined function "' + fn.name + '" is already defined'); | ||
// BIF | ||
var bif = bifs[fn.name]; | ||
if (bif) this.warn('built-in function "' + fn.name + '" is already defined'); | ||
// BIF | ||
var bif = bifs[fn.name]; | ||
if (bif) this.warn('built-in function "' + fn.name + '" is already defined'); | ||
return fn; | ||
}; | ||
return fn; | ||
}; | ||
/** | ||
* Visit Each. | ||
*/ | ||
/** | ||
* Visit Each. | ||
*/ | ||
Evaluator.prototype.visitEach = function(each){ | ||
this.return++; | ||
var expr = utils.unwrap(this.visit(each.expr)) | ||
, len = expr.nodes.length | ||
, val = new nodes.Ident(each.val) | ||
, key = new nodes.Ident(each.key || '__index__') | ||
, scope = this.currentScope | ||
, block = this.currentBlock | ||
, vals = [] | ||
, self = this | ||
, body | ||
, obj; | ||
this.return--; | ||
visitEach(each) { | ||
this.return++; | ||
var expr = utils.unwrap(this.visit(each.expr)) | ||
, len = expr.nodes.length | ||
, val = new nodes.Ident(each.val) | ||
, key = new nodes.Ident(each.key || '__index__') | ||
, scope = this.currentScope | ||
, block = this.currentBlock | ||
, vals = [] | ||
, self = this | ||
, body | ||
, obj; | ||
this.return--; | ||
each.block.scope = false; | ||
each.block.scope = false; | ||
function visitBody(key, val) { | ||
scope.add(val); | ||
scope.add(key); | ||
body = self.visit(each.block.clone()); | ||
vals = vals.concat(body.nodes); | ||
} | ||
function visitBody(key, val) { | ||
scope.add(val); | ||
scope.add(key); | ||
body = self.visit(each.block.clone()); | ||
vals = vals.concat(body.nodes); | ||
} | ||
// for prop in obj | ||
if (1 == len && 'object' == expr.nodes[0].nodeName) { | ||
obj = expr.nodes[0]; | ||
for (var prop in obj.vals) { | ||
val.val = new nodes.String(prop); | ||
key.val = obj.get(prop); | ||
visitBody(key, val); | ||
// for prop in obj | ||
if (1 == len && 'object' == expr.nodes[0].nodeName) { | ||
obj = expr.nodes[0]; | ||
for (var prop in obj.vals) { | ||
val.val = new nodes.String(prop); | ||
key.val = obj.get(prop); | ||
visitBody(key, val); | ||
} | ||
} else { | ||
for (var i = 0; i < len; ++i) { | ||
val.val = expr.nodes[i]; | ||
key.val = new nodes.Unit(i); | ||
visitBody(key, val); | ||
} | ||
} | ||
} else { | ||
for (var i = 0; i < len; ++i) { | ||
val.val = expr.nodes[i]; | ||
key.val = new nodes.Unit(i); | ||
visitBody(key, val); | ||
} | ||
} | ||
this.mixin(vals, block); | ||
return vals[vals.length - 1] || nodes.null; | ||
}; | ||
this.mixin(vals, block); | ||
return vals[vals.length - 1] || nodes.null; | ||
}; | ||
/** | ||
* Visit Call. | ||
*/ | ||
/** | ||
* Visit Call. | ||
*/ | ||
Evaluator.prototype.visitCall = function(call){ | ||
debug('call %s', call); | ||
var fn = this.lookup(call.name) | ||
, literal | ||
, ret; | ||
visitCall(call) { | ||
debug('call %s', call); | ||
var fn = this.lookup(call.name) | ||
, literal | ||
, ret; | ||
// url() | ||
this.ignoreColors = 'url' == call.name; | ||
// url() | ||
this.ignoreColors = 'url' == call.name; | ||
// Variable function | ||
if (fn && 'expression' == fn.nodeName) { | ||
fn = fn.nodes[0]; | ||
} | ||
// Variable function | ||
if (fn && 'expression' == fn.nodeName) { | ||
fn = fn.nodes[0]; | ||
} | ||
// Not a function? try user-defined or built-ins | ||
if (fn && 'function' != fn.nodeName) { | ||
fn = this.lookupFunction(call.name); | ||
} | ||
// Not a function? try user-defined or built-ins | ||
if (fn && 'function' != fn.nodeName) { | ||
fn = this.lookupFunction(call.name); | ||
} | ||
// Undefined function? render literal CSS | ||
if (!fn || fn.nodeName != 'function') { | ||
debug('%s is undefined', call); | ||
// Special case for `calc` | ||
if ('calc' == this.unvendorize(call.name)) { | ||
literal = call.args.nodes && call.args.nodes[0]; | ||
if (literal) ret = new nodes.Literal(call.name + literal); | ||
} else { | ||
ret = this.literalCall(call); | ||
// Undefined function? render literal CSS | ||
if (!fn || fn.nodeName != 'function') { | ||
debug('%s is undefined', call); | ||
// Special case for `calc` | ||
if ('calc' == this.unvendorize(call.name)) { | ||
literal = call.args.nodes && call.args.nodes[0]; | ||
if (literal) ret = new nodes.Literal(call.name + literal); | ||
} else { | ||
ret = this.literalCall(call); | ||
} | ||
this.ignoreColors = false; | ||
return ret; | ||
} | ||
this.ignoreColors = false; | ||
return ret; | ||
} | ||
this.calling.push(call.name); | ||
this.calling.push(call.name); | ||
// Massive stack | ||
if (this.calling.length > 200) { | ||
throw new RangeError('Maximum stylus call stack size exceeded'); | ||
} | ||
// Massive stack | ||
if (this.calling.length > 200) { | ||
throw new RangeError('Maximum stylus call stack size exceeded'); | ||
} | ||
// First node in expression | ||
if ('expression' == fn.nodeName) fn = fn.first; | ||
// First node in expression | ||
if ('expression' == fn.nodeName) fn = fn.first; | ||
// Evaluate arguments | ||
this.return++; | ||
var args = this.visit(call.args); | ||
// Evaluate arguments | ||
this.return++; | ||
var args = this.visit(call.args); | ||
for (var key in args.map) { | ||
args.map[key] = this.visit(args.map[key].clone()); | ||
} | ||
this.return--; | ||
for (var key in args.map) { | ||
args.map[key] = this.visit(args.map[key].clone()); | ||
} | ||
this.return--; | ||
// Built-in | ||
if (fn.fn) { | ||
debug('%s is built-in', call); | ||
ret = this.invokeBuiltin(fn.fn, args); | ||
// User-defined | ||
} else if ('function' == fn.nodeName) { | ||
debug('%s is user-defined', call); | ||
// Evaluate mixin block | ||
if (call.block) call.block = this.visit(call.block); | ||
ret = this.invokeFunction(fn, args, call.block); | ||
} | ||
// Built-in | ||
if (fn.fn) { | ||
debug('%s is built-in', call); | ||
ret = this.invokeBuiltin(fn.fn, args); | ||
// User-defined | ||
} else if ('function' == fn.nodeName) { | ||
debug('%s is user-defined', call); | ||
// Evaluate mixin block | ||
if (call.block) call.block = this.visit(call.block); | ||
ret = this.invokeFunction(fn, args, call.block); | ||
} | ||
this.calling.pop(); | ||
this.ignoreColors = false; | ||
return ret; | ||
}; | ||
this.calling.pop(); | ||
this.ignoreColors = false; | ||
return ret; | ||
}; | ||
/** | ||
* Visit Ident. | ||
*/ | ||
/** | ||
* Visit Ident. | ||
*/ | ||
Evaluator.prototype.visitIdent = function(ident){ | ||
var prop; | ||
// Property lookup | ||
if (ident.property) { | ||
if (prop = this.lookupProperty(ident.name)) { | ||
return this.visit(prop.expr.clone()); | ||
visitIdent(ident) { | ||
var prop; | ||
// Property lookup | ||
if (ident.property) { | ||
if (prop = this.lookupProperty(ident.name)) { | ||
return this.visit(prop.expr.clone()); | ||
} | ||
return nodes.null; | ||
// Lookup | ||
} else if (ident.val.isNull) { | ||
var val = this.lookup(ident.name); | ||
// Object or Block mixin | ||
if (val && ident.mixin) this.mixinNode(val); | ||
return val ? this.visit(val) : ident; | ||
// Assign | ||
} else { | ||
this.return++; | ||
ident.val = this.visit(ident.val); | ||
this.return--; | ||
this.currentScope.add(ident); | ||
return ident.val; | ||
} | ||
return nodes.null; | ||
// Lookup | ||
} else if (ident.val.isNull) { | ||
var val = this.lookup(ident.name); | ||
// Object or Block mixin | ||
if (val && ident.mixin) this.mixinNode(val); | ||
return val ? this.visit(val) : ident; | ||
// Assign | ||
} else { | ||
this.return++; | ||
ident.val = this.visit(ident.val); | ||
this.return--; | ||
this.currentScope.add(ident); | ||
return ident.val; | ||
} | ||
}; | ||
}; | ||
/** | ||
* Visit BinOp. | ||
*/ | ||
/** | ||
* Visit BinOp. | ||
*/ | ||
Evaluator.prototype.visitBinOp = function(binop){ | ||
// Special-case "is defined" pseudo binop | ||
if ('is defined' == binop.op) return this.isDefined(binop.left); | ||
visitBinOp(binop) { | ||
// Special-case "is defined" pseudo binop | ||
if ('is defined' == binop.op) return this.isDefined(binop.left); | ||
this.return++; | ||
// Visit operands | ||
var op = binop.op | ||
, left = this.visit(binop.left) | ||
, right = ('||' == op || '&&' == op) | ||
? binop.right : this.visit(binop.right); | ||
this.return++; | ||
// Visit operands | ||
var op = binop.op | ||
, left = this.visit(binop.left) | ||
, right = ('||' == op || '&&' == op) | ||
? binop.right : this.visit(binop.right); | ||
// HACK: ternary | ||
var val = binop.val | ||
? this.visit(binop.val) | ||
: null; | ||
this.return--; | ||
// HACK: ternary | ||
var val = binop.val | ||
? this.visit(binop.val) | ||
: null; | ||
this.return--; | ||
// 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; | ||
case '!=': | ||
return nodes.true; | ||
// 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; | ||
case '!=': | ||
return nodes.true; | ||
} | ||
} | ||
throw err; | ||
} | ||
throw err; | ||
} | ||
}; | ||
}; | ||
/** | ||
* Visit UnaryOp. | ||
*/ | ||
/** | ||
* Visit UnaryOp. | ||
*/ | ||
Evaluator.prototype.visitUnaryOp = function(unary){ | ||
var op = unary.op | ||
, node = this.visit(unary.expr); | ||
visitUnaryOp(unary) { | ||
var op = unary.op | ||
, node = this.visit(unary.expr); | ||
if ('!' != op) { | ||
node = node.first.clone(); | ||
utils.assertType(node, 'unit'); | ||
} | ||
if ('!' != op) { | ||
node = node.first.clone(); | ||
utils.assertType(node, 'unit'); | ||
} | ||
switch (op) { | ||
case '-': | ||
node.val = -node.val; | ||
break; | ||
case '+': | ||
node.val = +node.val; | ||
break; | ||
case '~': | ||
node.val = ~node.val; | ||
break; | ||
case '!': | ||
return node.toBoolean().negate(); | ||
} | ||
switch (op) { | ||
case '-': | ||
node.val = -node.val; | ||
break; | ||
case '+': | ||
node.val = +node.val; | ||
break; | ||
case '~': | ||
node.val = ~node.val; | ||
break; | ||
case '!': | ||
return node.toBoolean().negate(); | ||
} | ||
return node; | ||
}; | ||
return node; | ||
}; | ||
/** | ||
* Visit TernaryOp. | ||
*/ | ||
/** | ||
* Visit TernaryOp. | ||
*/ | ||
Evaluator.prototype.visitTernary = function(ternary){ | ||
var ok = this.visit(ternary.cond).toBoolean(); | ||
return ok.isTrue | ||
? this.visit(ternary.trueExpr) | ||
: this.visit(ternary.falseExpr); | ||
}; | ||
visitTernary(ternary) { | ||
var ok = this.visit(ternary.cond).toBoolean(); | ||
return ok.isTrue | ||
? this.visit(ternary.trueExpr) | ||
: this.visit(ternary.falseExpr); | ||
}; | ||
/** | ||
* Visit Expression. | ||
*/ | ||
/** | ||
* Visit Expression. | ||
*/ | ||
Evaluator.prototype.visitExpression = function(expr){ | ||
for (var i = 0, len = expr.nodes.length; i < len; ++i) { | ||
expr.nodes[i] = this.visit(expr.nodes[i]); | ||
} | ||
visitExpression(expr) { | ||
for (var i = 0, len = expr.nodes.length; i < len; ++i) { | ||
expr.nodes[i] = this.visit(expr.nodes[i]); | ||
} | ||
// support (n * 5)px etc | ||
if (this.castable(expr)) expr = this.cast(expr); | ||
// support (n * 5)px etc | ||
if (this.castable(expr)) expr = this.cast(expr); | ||
return expr; | ||
}; | ||
return expr; | ||
}; | ||
/** | ||
* Visit Arguments. | ||
*/ | ||
/** | ||
* Visit Arguments. | ||
*/ | ||
Evaluator.prototype.visitArguments = Evaluator.prototype.visitExpression; | ||
get visitArguments() { | ||
return this.visitExpression; | ||
} | ||
/** | ||
* Visit Property. | ||
*/ | ||
/** | ||
* Visit Property. | ||
*/ | ||
Evaluator.prototype.visitProperty = function(prop){ | ||
var name = this.interpolate(prop) | ||
, fn = this.lookup(name) | ||
, call = fn && 'function' == fn.first.nodeName | ||
, literal = ~this.calling.indexOf(name) | ||
, _prop = this.property; | ||
visitProperty(prop) { | ||
var name = this.interpolate(prop) | ||
, fn = this.lookup(name) | ||
, call = fn && 'function' == fn.first.nodeName | ||
, literal = ~this.calling.indexOf(name) | ||
, _prop = this.property; | ||
// Function of the same name | ||
if (call && !literal && !prop.literal) { | ||
var args = nodes.Arguments.fromExpression(utils.unwrap(prop.expr.clone())); | ||
prop.name = name; | ||
this.property = prop; | ||
this.return++; | ||
this.property.expr = this.visit(prop.expr); | ||
this.return--; | ||
var ret = this.visit(new nodes.Call(name, args)); | ||
this.property = _prop; | ||
return ret; | ||
// Regular property | ||
} else { | ||
this.return++; | ||
prop.name = name; | ||
prop.literal = true; | ||
this.property = prop; | ||
prop.expr = this.visit(prop.expr); | ||
this.property = _prop; | ||
this.return--; | ||
return prop; | ||
} | ||
}; | ||
// Function of the same name | ||
if (call && !literal && !prop.literal) { | ||
var args = nodes.Arguments.fromExpression(utils.unwrap(prop.expr.clone())); | ||
prop.name = name; | ||
this.property = prop; | ||
this.return++; | ||
this.property.expr = this.visit(prop.expr); | ||
this.return--; | ||
var ret = this.visit(new nodes.Call(name, args)); | ||
this.property = _prop; | ||
return ret; | ||
// Regular property | ||
} else { | ||
this.return++; | ||
prop.name = name; | ||
prop.literal = true; | ||
this.property = prop; | ||
prop.expr = this.visit(prop.expr); | ||
this.property = _prop; | ||
this.return--; | ||
return prop; | ||
} | ||
}; | ||
/** | ||
* Visit Root. | ||
*/ | ||
/** | ||
* Visit Root. | ||
*/ | ||
Evaluator.prototype.visitRoot = function(block){ | ||
// normalize cached imports | ||
if (block != this.root) { | ||
block.constructor = nodes.Block; | ||
return this.visit(block); | ||
} | ||
visitRoot(block) { | ||
// normalize cached imports | ||
if (block != this.root) { | ||
block.constructor = nodes.Block; | ||
return this.visit(block); | ||
} | ||
for (var i = 0; i < block.nodes.length; ++i) { | ||
block.index = i; | ||
block.nodes[i] = this.visit(block.nodes[i]); | ||
} | ||
return block; | ||
}; | ||
for (var i = 0; i < block.nodes.length; ++i) { | ||
block.index = i; | ||
block.nodes[i] = this.visit(block.nodes[i]); | ||
} | ||
return block; | ||
}; | ||
/** | ||
* Visit Block. | ||
*/ | ||
/** | ||
* Visit Block. | ||
*/ | ||
Evaluator.prototype.visitBlock = function(block){ | ||
this.stack.push(new Frame(block)); | ||
for (block.index = 0; block.index < block.nodes.length; ++block.index) { | ||
try { | ||
block.nodes[block.index] = this.visit(block.nodes[block.index]); | ||
} catch (err) { | ||
if ('return' == err.nodeName) { | ||
if (this.return) { | ||
this.stack.pop(); | ||
visitBlock(block) { | ||
this.stack.push(new Frame(block)); | ||
for (block.index = 0; block.index < block.nodes.length; ++block.index) { | ||
try { | ||
block.nodes[block.index] = this.visit(block.nodes[block.index]); | ||
} catch (err) { | ||
if ('return' == err.nodeName) { | ||
if (this.return) { | ||
this.stack.pop(); | ||
throw err; | ||
} else { | ||
block.nodes[block.index] = err; | ||
break; | ||
} | ||
} else { | ||
throw err; | ||
} else { | ||
block.nodes[block.index] = err; | ||
break; | ||
} | ||
} else { | ||
throw err; | ||
} | ||
} | ||
} | ||
this.stack.pop(); | ||
return block; | ||
}; | ||
this.stack.pop(); | ||
return block; | ||
}; | ||
/** | ||
* Visit Atblock. | ||
*/ | ||
/** | ||
* Visit Atblock. | ||
*/ | ||
Evaluator.prototype.visitAtblock = function(atblock){ | ||
atblock.block = this.visit(atblock.block); | ||
return atblock; | ||
}; | ||
visitAtblock(atblock) { | ||
atblock.block = this.visit(atblock.block); | ||
return atblock; | ||
}; | ||
/** | ||
* Visit Atrule. | ||
*/ | ||
/** | ||
* Visit Atrule. | ||
*/ | ||
Evaluator.prototype.visitAtrule = function(atrule){ | ||
atrule.val = this.interpolate(atrule); | ||
if (atrule.block) atrule.block = this.visit(atrule.block); | ||
return atrule; | ||
}; | ||
visitAtrule(atrule) { | ||
atrule.val = this.interpolate(atrule); | ||
if (atrule.block) atrule.block = this.visit(atrule.block); | ||
return atrule; | ||
}; | ||
/** | ||
* Visit Supports. | ||
*/ | ||
/** | ||
* Visit Supports. | ||
*/ | ||
Evaluator.prototype.visitSupports = function(node){ | ||
var condition = node.condition | ||
, val; | ||
visitSupports(node) { | ||
var condition = node.condition | ||
, val; | ||
this.return++; | ||
node.condition = this.visit(condition); | ||
this.return--; | ||
this.return++; | ||
node.condition = this.visit(condition); | ||
this.return--; | ||
val = condition.first; | ||
if (1 == condition.nodes.length | ||
&& 'string' == val.nodeName) { | ||
node.condition = val.string; | ||
} | ||
node.block = this.visit(node.block); | ||
return node; | ||
}; | ||
val = condition.first; | ||
if (1 == condition.nodes.length | ||
&& 'string' == val.nodeName) { | ||
node.condition = val.string; | ||
} | ||
node.block = this.visit(node.block); | ||
return node; | ||
}; | ||
/** | ||
* Visit If. | ||
*/ | ||
/** | ||
* Visit If. | ||
*/ | ||
Evaluator.prototype.visitIf = function(node){ | ||
var ret | ||
, block = this.currentBlock | ||
, negate = node.negate; | ||
visitIf(node) { | ||
var ret | ||
, block = this.currentBlock | ||
, negate = node.negate; | ||
this.return++; | ||
var ok = this.visit(node.cond).first.toBoolean(); | ||
this.return--; | ||
this.return++; | ||
var ok = this.visit(node.cond).first.toBoolean(); | ||
this.return--; | ||
node.block.scope = node.block.hasMedia; | ||
node.block.scope = node.block.hasMedia; | ||
// Evaluate body | ||
if (negate) { | ||
// unless | ||
if (ok.isFalse) { | ||
ret = this.visit(node.block); | ||
} | ||
} else { | ||
// if | ||
if (ok.isTrue) { | ||
ret = this.visit(node.block); | ||
// else | ||
} else if (node.elses.length) { | ||
var elses = node.elses | ||
, len = elses.length | ||
, cond; | ||
for (var i = 0; i < len; ++i) { | ||
// else if | ||
if (elses[i].cond) { | ||
elses[i].block.scope = elses[i].block.hasMedia; | ||
this.return++; | ||
cond = this.visit(elses[i].cond).first.toBoolean(); | ||
this.return--; | ||
if (cond.isTrue) { | ||
ret = this.visit(elses[i].block); | ||
break; | ||
// Evaluate body | ||
if (negate) { | ||
// unless | ||
if (ok.isFalse) { | ||
ret = this.visit(node.block); | ||
} | ||
} else { | ||
// if | ||
if (ok.isTrue) { | ||
ret = this.visit(node.block); | ||
// else | ||
} else if (node.elses.length) { | ||
var elses = node.elses | ||
, len = elses.length | ||
, cond; | ||
for (var i = 0; i < len; ++i) { | ||
// else if | ||
if (elses[i].cond) { | ||
elses[i].block.scope = elses[i].block.hasMedia; | ||
this.return++; | ||
cond = this.visit(elses[i].cond).first.toBoolean(); | ||
this.return--; | ||
if (cond.isTrue) { | ||
ret = this.visit(elses[i].block); | ||
break; | ||
} | ||
// else | ||
} else { | ||
elses[i].scope = elses[i].hasMedia; | ||
ret = this.visit(elses[i]); | ||
} | ||
// else | ||
} else { | ||
elses[i].scope = elses[i].hasMedia; | ||
ret = this.visit(elses[i]); | ||
} | ||
} | ||
} | ||
} | ||
// mixin conditional statements within | ||
// a selector group or at-rule | ||
if (ret && !node.postfix && block.node | ||
&& ~['group' | ||
, 'atrule' | ||
, 'media' | ||
, 'supports' | ||
, 'keyframes'].indexOf(block.node.nodeName)) { | ||
this.mixin(ret.nodes, block); | ||
return nodes.null; | ||
} | ||
// mixin conditional statements within | ||
// a selector group or at-rule | ||
if (ret && !node.postfix && block.node | ||
&& ~['group' | ||
, 'atrule' | ||
, 'media' | ||
, 'supports' | ||
, 'keyframes'].indexOf(block.node.nodeName)) { | ||
this.mixin(ret.nodes, block); | ||
return nodes.null; | ||
} | ||
return ret || nodes.null; | ||
}; | ||
return ret || nodes.null; | ||
}; | ||
/** | ||
* Visit Extend. | ||
*/ | ||
/** | ||
* Visit Extend. | ||
*/ | ||
Evaluator.prototype.visitExtend = function(extend){ | ||
var block = this.currentBlock; | ||
if ('group' != block.node.nodeName) block = this.closestGroup; | ||
extend.selectors.forEach(function(selector){ | ||
block.node.extends.push({ | ||
// Cloning the selector for when we are in a loop and don't want it to affect | ||
// the selector nodes and cause the values to be different to expected | ||
selector: this.interpolate(selector.clone()).trim(), | ||
optional: selector.optional, | ||
lineno: selector.lineno, | ||
column: selector.column | ||
}); | ||
}, this); | ||
return nodes.null; | ||
}; | ||
visitExtend(extend) { | ||
var block = this.currentBlock; | ||
if ('group' != block.node.nodeName) block = this.closestGroup; | ||
extend.selectors.forEach(function (selector) { | ||
block.node.extends.push({ | ||
// Cloning the selector for when we are in a loop and don't want it to affect | ||
// the selector nodes and cause the values to be different to expected | ||
selector: this.interpolate(selector.clone()).trim(), | ||
optional: selector.optional, | ||
lineno: selector.lineno, | ||
column: selector.column | ||
}); | ||
}, this); | ||
return nodes.null; | ||
}; | ||
/** | ||
* Visit Import. | ||
*/ | ||
/** | ||
* Visit Import. | ||
*/ | ||
Evaluator.prototype.visitImport = function(imported){ | ||
this.return++; | ||
visitImport(imported) { | ||
this.return++; | ||
var path = this.visit(imported.path).first | ||
, nodeName = imported.once ? 'require' : 'import' | ||
, found | ||
, literal; | ||
var path = this.visit(imported.path).first | ||
, nodeName = imported.once ? 'require' : 'import' | ||
, found | ||
, literal; | ||
this.return--; | ||
debug('import %s', path); | ||
this.return--; | ||
debug('import %s', path); | ||
// url() passed | ||
if ('url' == path.name) { | ||
if (imported.once) throw new Error('You cannot @require a url'); | ||
// url() passed | ||
if ('url' == path.name) { | ||
if (imported.once) throw new Error('You cannot @require a url'); | ||
return imported; | ||
} | ||
return imported; | ||
} | ||
// Ensure string | ||
if (!path.string) throw new Error('@' + nodeName + ' string expected'); | ||
// Ensure string | ||
if (!path.string) throw new Error('@' + nodeName + ' string expected'); | ||
var name = path = path.string; | ||
var name = path = path.string; | ||
// Absolute URL or hash | ||
if (/(?:url\s*\(\s*)?['"]?(?:#|(?:https?:)?\/\/)/i.test(path)) { | ||
if (imported.once) throw new Error('You cannot @require a url'); | ||
return imported; | ||
} | ||
// Literal | ||
if (/\.css(?:"|$)/.test(path)) { | ||
literal = true; | ||
if (!imported.once && !this.includeCSS) { | ||
// Absolute URL or hash | ||
if (/(?:url\s*\(\s*)?['"]?(?:#|(?:https?:)?\/\/)/i.test(path)) { | ||
if (imported.once) throw new Error('You cannot @require a url'); | ||
return imported; | ||
} | ||
} | ||
// support optional .styl | ||
if (!literal && !/\.styl$/i.test(path)) path += '.styl'; | ||
// Literal | ||
if (/\.css(?:"|$)/.test(path)) { | ||
literal = true; | ||
if (!imported.once && !this.includeCSS) { | ||
return imported; | ||
} | ||
} | ||
// Lookup | ||
found = utils.find(path, this.paths, this.filename); | ||
if (!found) { | ||
found = utils.lookupIndex(name, this.paths, this.filename); | ||
} | ||
// support optional .styl | ||
if (!literal && !/\.styl$/i.test(path)) path += '.styl'; | ||
// Throw if import failed | ||
if (!found) throw new Error('failed to locate @' + nodeName + ' file ' + path); | ||
var block = new nodes.Block; | ||
// Lookup | ||
found = utils.find(path, this.paths, this.filename); | ||
if (!found) { | ||
found = utils.lookupIndex(name, this.paths, this.filename); | ||
} | ||
for (var i = 0, len = found.length; i < len; ++i) { | ||
block.push(importFile.call(this, imported, found[i], literal)); | ||
} | ||
// Throw if import failed | ||
if (!found) throw new Error('failed to locate @' + nodeName + ' file ' + path); | ||
return block; | ||
}; | ||
var block = new nodes.Block; | ||
/** | ||
* Invoke `fn` with `args`. | ||
* | ||
* @param {Function} fn | ||
* @param {Array} args | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
for (var i = 0, len = found.length; i < len; ++i) { | ||
block.push(importFile.call(this, imported, found[i], literal)); | ||
} | ||
Evaluator.prototype.invokeFunction = function(fn, args, content){ | ||
var block = new nodes.Block(fn.block.parent); | ||
return block; | ||
}; | ||
// Clone the function body | ||
// to prevent mutation of subsequent calls | ||
var body = fn.block.clone(block); | ||
/** | ||
* Invoke `fn` with `args`. | ||
* | ||
* @param {Function} fn | ||
* @param {Array} args | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
// mixin block | ||
var mixinBlock = this.stack.currentFrame.block; | ||
invokeFunction(fn, args, content) { | ||
var block = new nodes.Block(fn.block.parent); | ||
// new block scope | ||
this.stack.push(new Frame(block)); | ||
var scope = this.currentScope; | ||
// Clone the function body | ||
// to prevent mutation of subsequent calls | ||
var body = fn.block.clone(block); | ||
// normalize arguments | ||
if ('arguments' != args.nodeName) { | ||
var expr = new nodes.Expression; | ||
expr.push(args); | ||
args = nodes.Arguments.fromExpression(expr); | ||
} | ||
// mixin block | ||
var mixinBlock = this.stack.currentFrame.block; | ||
// arguments local | ||
scope.add(new nodes.Ident('arguments', args)); | ||
// new block scope | ||
this.stack.push(new Frame(block)); | ||
var scope = this.currentScope; | ||
// mixin scope introspection | ||
scope.add(new nodes.Ident('mixin', this.return | ||
? nodes.false | ||
: new nodes.String(mixinBlock.nodeName))); | ||
// normalize arguments | ||
if ('arguments' != args.nodeName) { | ||
var expr = new nodes.Expression; | ||
expr.push(args); | ||
args = nodes.Arguments.fromExpression(expr); | ||
} | ||
// 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)); | ||
} | ||
// arguments local | ||
scope.add(new nodes.Ident('arguments', args)); | ||
// current call stack | ||
var expr = new nodes.Expression; | ||
for (var i = this.calling.length - 1; i-- ; ) { | ||
expr.push(new nodes.Literal(this.calling[i])); | ||
}; | ||
scope.add(new nodes.Ident('called-from', expr)); | ||
// mixin scope introspection | ||
scope.add(new nodes.Ident('mixin', this.return | ||
? nodes.false | ||
: new nodes.String(mixinBlock.nodeName))); | ||
// inject arguments as locals | ||
var i = 0 | ||
, len = args.nodes.length; | ||
fn.params.nodes.forEach(function(node){ | ||
// rest param support | ||
if (node.rest) { | ||
node.val = new nodes.Expression; | ||
for (; i < len; ++i) node.val.push(args.nodes[i]); | ||
node.val.preserve = true; | ||
node.val.isList = args.isList; | ||
// argument default support | ||
// current property | ||
if (this.property) { | ||
var prop = this.propertyExpression(this.property, fn.name); | ||
scope.add(new nodes.Ident('current-property', prop)); | ||
} else { | ||
var arg = args.map[node.name] || args.nodes[i++]; | ||
node = node.clone(); | ||
if (arg) { | ||
arg.isEmpty ? args.nodes[i - 1] = this.visit(node) : node.val = arg; | ||
scope.add(new nodes.Ident('current-property', nodes.null)); | ||
} | ||
// current call stack | ||
var expr = new nodes.Expression; | ||
for (var i = this.calling.length - 1; i--;) { | ||
expr.push(new nodes.Literal(this.calling[i])); | ||
}; | ||
scope.add(new nodes.Ident('called-from', expr)); | ||
// inject arguments as locals | ||
var i = 0 | ||
, len = args.nodes.length; | ||
fn.params.nodes.forEach(function (node) { | ||
// rest param support | ||
if (node.rest) { | ||
node.val = new nodes.Expression; | ||
for (; i < len; ++i) node.val.push(args.nodes[i]); | ||
node.val.preserve = true; | ||
node.val.isList = args.isList; | ||
// argument default support | ||
} else { | ||
args.push(node.val); | ||
} | ||
var arg = args.map[node.name] || args.nodes[i++]; | ||
node = node.clone(); | ||
if (arg) { | ||
arg.isEmpty ? args.nodes[i - 1] = this.visit(node) : node.val = arg; | ||
} else { | ||
args.push(node.val); | ||
} | ||
// required argument not satisfied | ||
if (node.val.isNull) { | ||
throw new Error('argument "' + node + '" required for ' + fn); | ||
// required argument not satisfied | ||
if (node.val.isNull) { | ||
throw new Error('argument "' + node + '" required for ' + fn); | ||
} | ||
} | ||
} | ||
scope.add(node); | ||
}, this); | ||
scope.add(node); | ||
}, this); | ||
// mixin block | ||
if (content) scope.add(new nodes.Ident('block', content, true)); | ||
// mixin block | ||
if (content) scope.add(new nodes.Ident('block', content, true)); | ||
// invoke | ||
return this.invoke(body, true, fn.filename); | ||
}; | ||
// invoke | ||
return this.invoke(body, true, fn.filename); | ||
}; | ||
/** | ||
* Invoke built-in `fn` with `args`. | ||
* | ||
* @param {Function} fn | ||
* @param {Array} args | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Invoke built-in `fn` with `args`. | ||
* | ||
* @param {Function} fn | ||
* @param {Array} args | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.invokeBuiltin = function(fn, args){ | ||
// Map arguments to first node | ||
// providing a nicer js api for | ||
// BIFs. Functions may specify that | ||
// they wish to accept full expressions | ||
// via .raw | ||
if (fn.raw) { | ||
args = args.nodes; | ||
} else { | ||
if (!fn.params) { | ||
fn.params = utils.params(fn); | ||
} | ||
args = fn.params.reduce(function(ret, param){ | ||
var arg = args.map[param] || args.nodes.shift() | ||
if (arg) { | ||
arg = utils.unwrap(arg); | ||
var len = arg.nodes.length; | ||
if (len > 1) { | ||
for (var i = 0; i < len; ++i) { | ||
ret.push(utils.unwrap(arg.nodes[i].first)); | ||
invokeBuiltin(fn, args) { | ||
// Map arguments to first node | ||
// providing a nicer js api for | ||
// BIFs. Functions may specify that | ||
// they wish to accept full expressions | ||
// via .raw | ||
if (fn.raw) { | ||
args = args.nodes; | ||
} else { | ||
if (!fn.params) { | ||
fn.params = utils.params(fn); | ||
} | ||
args = fn.params.reduce(function (ret, param) { | ||
var arg = args.map[param] || args.nodes.shift() | ||
if (arg) { | ||
arg = utils.unwrap(arg); | ||
var len = arg.nodes.length; | ||
if (len > 1) { | ||
for (var i = 0; i < len; ++i) { | ||
ret.push(utils.unwrap(arg.nodes[i].first)); | ||
} | ||
} else { | ||
ret.push(arg.first); | ||
} | ||
} else { | ||
ret.push(arg.first); | ||
} | ||
} | ||
return ret; | ||
}, []); | ||
} | ||
return ret; | ||
}, []); | ||
} | ||
// Invoke the BIF | ||
var body = utils.coerce(fn.apply(this, args)); | ||
// Invoke the BIF | ||
var body = utils.coerce(fn.apply(this, args)); | ||
// Always wrapping allows js functions | ||
// to return several values with a single | ||
// Expression node | ||
var expr = new nodes.Expression; | ||
expr.push(body); | ||
body = expr; | ||
// Always wrapping allows js functions | ||
// to return several values with a single | ||
// Expression node | ||
var expr = new nodes.Expression; | ||
expr.push(body); | ||
body = expr; | ||
// Invoke | ||
return this.invoke(body); | ||
}; | ||
// Invoke | ||
return this.invoke(body); | ||
}; | ||
/** | ||
* Invoke the given function `body`. | ||
* | ||
* @param {Block} body | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Invoke the given function `body`. | ||
* | ||
* @param {Block} body | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.invoke = function(body, stack, filename){ | ||
var self = this | ||
, ret; | ||
invoke(body, stack, filename) { | ||
var self = this | ||
, ret; | ||
if (filename) this.paths.push(dirname(filename)); | ||
if (filename) this.paths.push(dirname(filename)); | ||
// Return | ||
if (this.return) { | ||
ret = this.eval(body.nodes); | ||
if (stack) this.stack.pop(); | ||
// Mixin | ||
} else { | ||
body = this.visit(body); | ||
if (stack) this.stack.pop(); | ||
this.mixin(body.nodes, this.currentBlock); | ||
ret = nodes.null; | ||
} | ||
// Return | ||
if (this.return) { | ||
ret = this.eval(body.nodes); | ||
if (stack) this.stack.pop(); | ||
// Mixin | ||
} else { | ||
body = this.visit(body); | ||
if (stack) this.stack.pop(); | ||
this.mixin(body.nodes, this.currentBlock); | ||
ret = nodes.null; | ||
} | ||
if (filename) this.paths.pop(); | ||
if (filename) this.paths.pop(); | ||
return ret; | ||
}; | ||
return ret; | ||
}; | ||
/** | ||
* Mixin the given `nodes` to the given `block`. | ||
* | ||
* @param {Array} nodes | ||
* @param {Block} block | ||
* @api private | ||
*/ | ||
/** | ||
* Mixin the given `nodes` to the given `block`. | ||
* | ||
* @param {Array} nodes | ||
* @param {Block} block | ||
* @api private | ||
*/ | ||
Evaluator.prototype.mixin = function(nodes, block){ | ||
if (!nodes.length) return; | ||
var len = block.nodes.length | ||
, head = block.nodes.slice(0, block.index) | ||
, tail = block.nodes.slice(block.index + 1, len); | ||
this._mixin(nodes, head, block); | ||
block.index = 0; | ||
block.nodes = head.concat(tail); | ||
}; | ||
mixin(nodes, block) { | ||
if (!nodes.length) return; | ||
var len = block.nodes.length | ||
, head = block.nodes.slice(0, block.index) | ||
, tail = block.nodes.slice(block.index + 1, len); | ||
this._mixin(nodes, head, block); | ||
block.index = 0; | ||
block.nodes = head.concat(tail); | ||
}; | ||
/** | ||
* Mixin the given `items` to the `dest` array. | ||
* | ||
* @param {Array} items | ||
* @param {Array} dest | ||
* @param {Block} block | ||
* @api private | ||
*/ | ||
/** | ||
* Mixin the given `items` to the `dest` array. | ||
* | ||
* @param {Array} items | ||
* @param {Array} dest | ||
* @param {Block} block | ||
* @api private | ||
*/ | ||
Evaluator.prototype._mixin = function(items, dest, block){ | ||
var node | ||
, len = items.length; | ||
for (var i = 0; i < len; ++i) { | ||
switch ((node = items[i]).nodeName) { | ||
case 'return': | ||
return; | ||
case 'block': | ||
this._mixin(node.nodes, dest, block); | ||
break; | ||
case 'media': | ||
// fix link to the parent block | ||
var parentNode = node.block.parent.node; | ||
if (parentNode && 'call' != parentNode.nodeName) { | ||
node.block.parent = block; | ||
} | ||
case 'property': | ||
var val = node.expr; | ||
// prevent `block` mixin recursion | ||
if (node.literal && 'block' == val.first.name) { | ||
val = utils.unwrap(val); | ||
val.nodes[0] = new nodes.Literal('block'); | ||
} | ||
default: | ||
dest.push(node); | ||
_mixin(items, dest, block) { | ||
var node | ||
, len = items.length; | ||
for (var i = 0; i < len; ++i) { | ||
switch ((node = items[i]).nodeName) { | ||
case 'return': | ||
return; | ||
case 'block': | ||
this._mixin(node.nodes, dest, block); | ||
break; | ||
case 'media': | ||
// fix link to the parent block | ||
var parentNode = node.block.parent.node; | ||
if (parentNode && 'call' != parentNode.nodeName) { | ||
node.block.parent = block; | ||
} | ||
case 'property': | ||
var val = node.expr; | ||
// prevent `block` mixin recursion | ||
if (node.literal && 'block' == val.first.name) { | ||
val = utils.unwrap(val); | ||
val.nodes[0] = new nodes.Literal('block'); | ||
} | ||
default: | ||
dest.push(node); | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/** | ||
* Mixin the given `node` to the current block. | ||
* | ||
* @param {Node} node | ||
* @api private | ||
*/ | ||
/** | ||
* Mixin the given `node` to the current block. | ||
* | ||
* @param {Node} node | ||
* @api private | ||
*/ | ||
Evaluator.prototype.mixinNode = function(node){ | ||
node = this.visit(node.first); | ||
switch (node.nodeName) { | ||
case 'object': | ||
this.mixinObject(node); | ||
return nodes.null; | ||
case 'block': | ||
case 'atblock': | ||
this.mixin(node.nodes, this.currentBlock); | ||
return nodes.null; | ||
} | ||
}; | ||
mixinNode(node) { | ||
node = this.visit(node.first); | ||
switch (node.nodeName) { | ||
case 'object': | ||
this.mixinObject(node); | ||
return nodes.null; | ||
case 'block': | ||
case 'atblock': | ||
this.mixin(node.nodes, this.currentBlock); | ||
return nodes.null; | ||
} | ||
}; | ||
/** | ||
* Mixin the given `object` to the current block. | ||
* | ||
* @param {Object} object | ||
* @api private | ||
*/ | ||
/** | ||
* Mixin the given `object` to the current block. | ||
* | ||
* @param {Object} object | ||
* @api private | ||
*/ | ||
Evaluator.prototype.mixinObject = function(object){ | ||
var Parser = require('../parser') | ||
, root = this.root | ||
, str = '$block ' + object.toBlock() | ||
, parser = new Parser(str, utils.merge({ root: block }, this.options)) | ||
, block; | ||
mixinObject(object) { | ||
var Parser = require('../parser') | ||
, root = this.root | ||
, str = '$block ' + object.toBlock() | ||
, parser = new Parser(str, utils.merge({ root: block }, this.options)) | ||
, block; | ||
try { | ||
block = parser.parse(); | ||
} catch (err) { | ||
err.filename = this.filename; | ||
err.lineno = parser.lexer.lineno; | ||
err.column = parser.lexer.column; | ||
err.input = str; | ||
throw err; | ||
} | ||
try { | ||
block = parser.parse(); | ||
} catch (err) { | ||
err.filename = this.filename; | ||
err.lineno = parser.lexer.lineno; | ||
err.column = parser.lexer.column; | ||
err.input = str; | ||
throw err; | ||
} | ||
block.parent = root; | ||
block.scope = false; | ||
var ret = this.visit(block) | ||
, vals = ret.first.nodes; | ||
for (var i = 0, len = vals.length; i < len; ++i) { | ||
if (vals[i].block) { | ||
this.mixin(vals[i].block.nodes, this.currentBlock); | ||
break; | ||
block.parent = root; | ||
block.scope = false; | ||
var ret = this.visit(block) | ||
, vals = ret.first.nodes; | ||
for (var i = 0, len = vals.length; i < len; ++i) { | ||
if (vals[i].block) { | ||
this.mixin(vals[i].block.nodes, this.currentBlock); | ||
break; | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/** | ||
* Evaluate the given `vals`. | ||
* | ||
* @param {Array} vals | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Evaluate the given `vals`. | ||
* | ||
* @param {Array} vals | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.eval = function(vals){ | ||
if (!vals) return nodes.null; | ||
var len = vals.length | ||
, node = nodes.null; | ||
eval(vals) { | ||
if (!vals) return nodes.null; | ||
var len = vals.length | ||
, node = nodes.null; | ||
try { | ||
for (var i = 0; i < len; ++i) { | ||
node = vals[i]; | ||
switch (node.nodeName) { | ||
case 'if': | ||
if ('block' != node.block.nodeName) { | ||
try { | ||
for (var i = 0; i < len; ++i) { | ||
node = vals[i]; | ||
switch (node.nodeName) { | ||
case 'if': | ||
if ('block' != node.block.nodeName) { | ||
node = this.visit(node); | ||
break; | ||
} | ||
case 'each': | ||
case 'block': | ||
node = this.visit(node); | ||
if (node.nodes) node = this.eval(node.nodes); | ||
break; | ||
} | ||
case 'each': | ||
case 'block': | ||
node = this.visit(node); | ||
if (node.nodes) node = this.eval(node.nodes); | ||
break; | ||
default: | ||
node = this.visit(node); | ||
default: | ||
node = this.visit(node); | ||
} | ||
} | ||
} catch (err) { | ||
if ('return' == err.nodeName) { | ||
return err.expr; | ||
} else { | ||
throw err; | ||
} | ||
} | ||
} catch (err) { | ||
if ('return' == err.nodeName) { | ||
return err.expr; | ||
} else { | ||
throw err; | ||
} | ||
} | ||
return node; | ||
}; | ||
return node; | ||
}; | ||
/** | ||
* Literal function `call`. | ||
* | ||
* @param {Call} call | ||
* @return {call} | ||
* @api private | ||
*/ | ||
/** | ||
* Literal function `call`. | ||
* | ||
* @param {Call} call | ||
* @return {call} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.literalCall = function(call){ | ||
call.args = this.visit(call.args); | ||
return call; | ||
}; | ||
literalCall(call) { | ||
call.args = this.visit(call.args); | ||
return call; | ||
}; | ||
/** | ||
* Lookup property `name`. | ||
* | ||
* @param {String} name | ||
* @return {Property} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup property `name`. | ||
* | ||
* @param {String} name | ||
* @return {Property} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.lookupProperty = function(name){ | ||
var i = this.stack.length | ||
, index = this.currentBlock.index | ||
, top = i | ||
, nodes | ||
, block | ||
, len | ||
, other; | ||
lookupProperty(name) { | ||
var i = this.stack.length | ||
, index = this.currentBlock.index | ||
, top = i | ||
, nodes | ||
, block | ||
, len | ||
, other; | ||
while (i--) { | ||
block = this.stack[i].block; | ||
if (!block.node) continue; | ||
switch (block.node.nodeName) { | ||
case 'group': | ||
case 'function': | ||
case 'if': | ||
case 'each': | ||
case 'atrule': | ||
case 'media': | ||
case 'atblock': | ||
case 'call': | ||
nodes = block.nodes; | ||
// scan siblings from the property index up | ||
if (i + 1 == top) { | ||
while (index--) { | ||
// ignore current property | ||
if (this.property == nodes[index]) continue; | ||
other = this.interpolate(nodes[index]); | ||
if (name == other) return nodes[index].clone(); | ||
} | ||
// sequential lookup for non-siblings (for now) | ||
} else { | ||
len = nodes.length; | ||
while (len--) { | ||
if ('property' != nodes[len].nodeName | ||
|| this.property == nodes[len]) continue; | ||
other = this.interpolate(nodes[len]); | ||
if (name == other) return nodes[len].clone(); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
return nodes.null; | ||
}; | ||
/** | ||
* 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) { | ||
while (i--) { | ||
block = this.stack[i].block; | ||
if (!block.node) continue; | ||
switch (block.node.nodeName) { | ||
case 'group': | ||
case 'keyframes': | ||
case 'function': | ||
case 'if': | ||
case 'each': | ||
case 'atrule': | ||
case 'media': | ||
case 'atblock': | ||
case 'media': | ||
case 'call': | ||
return block; | ||
nodes = block.nodes; | ||
// scan siblings from the property index up | ||
if (i + 1 == top) { | ||
while (index--) { | ||
// ignore current property | ||
if (this.property == nodes[index]) continue; | ||
other = this.interpolate(nodes[index]); | ||
if (name == other) return nodes[index].clone(); | ||
} | ||
// sequential lookup for non-siblings (for now) | ||
} else { | ||
len = nodes.length; | ||
while (len--) { | ||
if ('property' != nodes[len].nodeName | ||
|| this.property == nodes[len]) continue; | ||
other = this.interpolate(nodes[len]); | ||
if (name == other) return nodes[len].clone(); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
}); | ||
/** | ||
* Return the closest group block. | ||
* | ||
* @return {Block} | ||
* @api private | ||
*/ | ||
return nodes.null; | ||
}; | ||
Evaluator.prototype.__defineGetter__('closestGroup', function(){ | ||
var i = this.stack.length | ||
, block; | ||
while (i--) { | ||
block = this.stack[i].block; | ||
if (block.node && 'group' == block.node.nodeName) { | ||
return block; | ||
/** | ||
* Return the closest mixin-able `Block`. | ||
* | ||
* @return {Block} | ||
* @api private | ||
*/ | ||
get closestBlock() { | ||
var i = this.stack.length | ||
, block; | ||
while (i--) { | ||
block = this.stack[i].block; | ||
if (block.node) { | ||
switch (block.node.nodeName) { | ||
case 'group': | ||
case 'keyframes': | ||
case 'atrule': | ||
case 'atblock': | ||
case 'media': | ||
case 'call': | ||
return block; | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
}; | ||
/** | ||
* Return the current selectors stack. | ||
* | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
/** | ||
* Return the closest group block. | ||
* | ||
* @return {Block} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.__defineGetter__('selectorStack', function(){ | ||
var block | ||
, stack = []; | ||
for (var i = 0, len = this.stack.length; i < len; ++i) { | ||
block = this.stack[i].block; | ||
if (block.node && 'group' == block.node.nodeName) { | ||
block.node.nodes.forEach(function(selector) { | ||
if (!selector.val) selector.val = this.interpolate(selector); | ||
}, this); | ||
stack.push(block.node.nodes); | ||
get closestGroup() { | ||
var i = this.stack.length | ||
, block; | ||
while (i--) { | ||
block = this.stack[i].block; | ||
if (block.node && 'group' == block.node.nodeName) { | ||
return block; | ||
} | ||
} | ||
} | ||
return stack; | ||
}); | ||
}; | ||
/** | ||
* Lookup `name`, with support for JavaScript | ||
* functions, and BIFs. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Return the current selectors stack. | ||
* | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.lookup = function(name){ | ||
var val; | ||
if (this.ignoreColors && name in colors) return; | ||
if (val = this.stack.lookup(name)) { | ||
return utils.unwrap(val); | ||
} else { | ||
return this.lookupFunction(name); | ||
} | ||
}; | ||
get selectorStack() { | ||
var block | ||
, stack = []; | ||
for (var i = 0, len = this.stack.length; i < len; ++i) { | ||
block = this.stack[i].block; | ||
if (block.node && 'group' == block.node.nodeName) { | ||
block.node.nodes.forEach(function (selector) { | ||
if (!selector.val) selector.val = this.interpolate(selector); | ||
}, this); | ||
stack.push(block.node.nodes); | ||
} | ||
} | ||
return stack; | ||
}; | ||
/** | ||
* Map segments in `node` returning a string. | ||
* | ||
* @param {Node} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup `name`, with support for JavaScript | ||
* functions, and BIFs. | ||
* | ||
* @param {String} name | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.interpolate = function(node){ | ||
var self = this | ||
, isSelector = ('selector' == node.nodeName); | ||
function toString(node) { | ||
switch (node.nodeName) { | ||
case 'function': | ||
case 'ident': | ||
return node.name; | ||
case 'literal': | ||
case 'string': | ||
if (self.prefix && !node.prefixed && !node.val.nodeName) { | ||
node.val = node.val.replace(/\.(?=[\w-])|^\.$/g, '.' + self.prefix); | ||
node.prefixed = true; | ||
} | ||
return node.val; | ||
case 'unit': | ||
// Interpolation inside keyframes | ||
return '%' == node.type ? node.val + '%' : node.val; | ||
case 'member': | ||
return toString(self.visit(node)); | ||
case 'expression': | ||
// Prevent cyclic `selector()` calls. | ||
if (self.calling && ~self.calling.indexOf('selector') && self._selector) return self._selector; | ||
self.return++; | ||
var ret = toString(self.visit(node).first); | ||
self.return--; | ||
if (isSelector) self._selector = ret; | ||
return ret; | ||
lookup(name) { | ||
var val; | ||
if (this.ignoreColors && name in colors) return; | ||
if (val = this.stack.lookup(name)) { | ||
return utils.unwrap(val); | ||
} else { | ||
return this.lookupFunction(name); | ||
} | ||
} | ||
}; | ||
if (node.segments) { | ||
return node.segments.map(toString).join(''); | ||
} else { | ||
return toString(node); | ||
} | ||
}; | ||
/** | ||
* Map segments in `node` returning a string. | ||
* | ||
* @param {Node} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup JavaScript user-defined or built-in function. | ||
* | ||
* @param {String} name | ||
* @return {Function} | ||
* @api private | ||
*/ | ||
interpolate(node) { | ||
var self = this | ||
, isSelector = ('selector' == node.nodeName); | ||
function toString(node) { | ||
switch (node.nodeName) { | ||
case 'function': | ||
case 'ident': | ||
return node.name; | ||
case 'literal': | ||
case 'string': | ||
if (self.prefix && !node.prefixed && !node.val.nodeName) { | ||
node.val = node.val.replace(/\.(?=[\w-])|^\.$/g, '.' + self.prefix); | ||
node.prefixed = true; | ||
} | ||
return node.val; | ||
case 'unit': | ||
// Interpolation inside keyframes | ||
return '%' == node.type ? node.val + '%' : node.val; | ||
case 'member': | ||
return toString(self.visit(node)); | ||
case 'expression': | ||
// Prevent cyclic `selector()` calls. | ||
if (self.calling && ~self.calling.indexOf('selector') && self._selector) return self._selector; | ||
self.return++; | ||
var ret = toString(self.visit(node).first); | ||
self.return--; | ||
if (isSelector) self._selector = ret; | ||
return ret; | ||
} | ||
} | ||
Evaluator.prototype.lookupFunction = function(name){ | ||
var fn = this.functions[name] || bifs[name]; | ||
if (fn) return new nodes.Function(name, fn); | ||
}; | ||
if (node.segments) { | ||
return node.segments.map(toString).join(''); | ||
} else { | ||
return toString(node); | ||
} | ||
}; | ||
/** | ||
* Check if the given `node` is an ident, and if it is defined. | ||
* | ||
* @param {Node} node | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
/** | ||
* Lookup JavaScript user-defined or built-in function. | ||
* | ||
* @param {String} name | ||
* @return {Function} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.isDefined = function(node){ | ||
if ('ident' == node.nodeName) { | ||
return nodes.Boolean(this.lookup(node.name)); | ||
} else { | ||
throw new Error('invalid "is defined" check on non-variable ' + node); | ||
} | ||
}; | ||
lookupFunction(name) { | ||
var fn = this.functions[name] || bifs[name]; | ||
if (fn) return new nodes.Function(name, fn); | ||
}; | ||
/** | ||
* 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 | ||
*/ | ||
/** | ||
* Check if the given `node` is an ident, and if it is defined. | ||
* | ||
* @param {Node} node | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.propertyExpression = function(prop, name){ | ||
var expr = new nodes.Expression | ||
, val = prop.expr.clone(); | ||
isDefined(node) { | ||
if ('ident' == node.nodeName) { | ||
return new nodes.Boolean(this.lookup(node.name)); | ||
} else { | ||
throw new Error('invalid "is defined" check on non-variable ' + node); | ||
} | ||
}; | ||
// name | ||
expr.push(new nodes.String(prop.name)); | ||
/** | ||
* 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 | ||
*/ | ||
// replace cyclic call with __CALL__ | ||
function replace(node) { | ||
if ('call' == node.nodeName && name == node.name) { | ||
return new nodes.Literal('__CALL__'); | ||
propertyExpression(prop, name) { | ||
var expr = new nodes.Expression | ||
, val = prop.expr.clone(); | ||
// name | ||
expr.push(new nodes.String(prop.name)); | ||
// replace cyclic call with __CALL__ | ||
function replace(node) { | ||
if ('call' == node.nodeName && name == node.name) { | ||
return new nodes.Literal('__CALL__'); | ||
} | ||
if (node.nodes) node.nodes = node.nodes.map(replace); | ||
return node; | ||
} | ||
if (node.nodes) node.nodes = node.nodes.map(replace); | ||
return node; | ||
} | ||
replace(val); | ||
expr.push(val); | ||
return expr; | ||
}; | ||
replace(val); | ||
expr.push(val); | ||
return expr; | ||
}; | ||
/** | ||
* Cast `expr` to the trailing ident. | ||
* | ||
* @param {Expression} expr | ||
* @return {Unit} | ||
* @api private | ||
*/ | ||
/** | ||
* Cast `expr` to the trailing ident. | ||
* | ||
* @param {Expression} expr | ||
* @return {Unit} | ||
* @api private | ||
*/ | ||
cast(expr) { | ||
return new nodes.Unit(expr.first.val, expr.nodes[1].name); | ||
}; | ||
Evaluator.prototype.cast = function(expr){ | ||
return new nodes.Unit(expr.first.val, expr.nodes[1].name); | ||
}; | ||
/** | ||
* Check if `expr` is castable. | ||
* | ||
* @param {Expression} expr | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
/** | ||
* Check if `expr` is castable. | ||
* | ||
* @param {Expression} expr | ||
* @return {Boolean} | ||
* @api private | ||
*/ | ||
castable(expr) { | ||
return 2 == expr.nodes.length | ||
&& 'unit' == expr.first.nodeName | ||
&& ~units.indexOf(expr.nodes[1].name); | ||
}; | ||
Evaluator.prototype.castable = function(expr){ | ||
return 2 == expr.nodes.length | ||
&& 'unit' == expr.first.nodeName | ||
&& ~units.indexOf(expr.nodes[1].name); | ||
}; | ||
/** | ||
* Warn with the given `msg`. | ||
* | ||
* @param {String} msg | ||
* @api private | ||
*/ | ||
/** | ||
* Warn with the given `msg`. | ||
* | ||
* @param {String} msg | ||
* @api private | ||
*/ | ||
warn(msg) { | ||
if (!this.warnings) return; | ||
console.warn('\u001b[33mWarning:\u001b[0m ' + msg); | ||
}; | ||
Evaluator.prototype.warn = function(msg){ | ||
if (!this.warnings) return; | ||
console.warn('\u001b[33mWarning:\u001b[0m ' + msg); | ||
}; | ||
/** | ||
* Return the current `Block`. | ||
* | ||
* @return {Block} | ||
* @api private | ||
*/ | ||
/** | ||
* Return the current `Block`. | ||
* | ||
* @return {Block} | ||
* @api private | ||
*/ | ||
get currentBlock() { | ||
return this.stack.currentFrame.block; | ||
}; | ||
Evaluator.prototype.__defineGetter__('currentBlock', function(){ | ||
return this.stack.currentFrame.block; | ||
}); | ||
/** | ||
* Return an array of vendor names. | ||
* | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
/** | ||
* Return an array of vendor names. | ||
* | ||
* @return {Array} | ||
* @api private | ||
*/ | ||
get vendors() { | ||
return this.lookup('vendors').nodes.map(function (node) { | ||
return node.string; | ||
}); | ||
}; | ||
Evaluator.prototype.__defineGetter__('vendors', function(){ | ||
return this.lookup('vendors').nodes.map(function(node){ | ||
return node.string; | ||
}); | ||
}); | ||
/** | ||
* Return the property name without vendor prefix. | ||
* | ||
* @param {String} prop | ||
* @return {String} | ||
* @api public | ||
*/ | ||
/** | ||
* Return the property name without vendor prefix. | ||
* | ||
* @param {String} prop | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Evaluator.prototype.unvendorize = function(prop){ | ||
for (var i = 0, len = this.vendors.length; i < len; i++) { | ||
if ('official' != this.vendors[i]) { | ||
var vendor = '-' + this.vendors[i] + '-'; | ||
if (~prop.indexOf(vendor)) return prop.replace(vendor, ''); | ||
unvendorize(prop) { | ||
for (var i = 0, len = this.vendors.length; i < len; i++) { | ||
if ('official' != this.vendors[i]) { | ||
var vendor = '-' + this.vendors[i] + '-'; | ||
if (~prop.indexOf(vendor)) return prop.replace(vendor, ''); | ||
} | ||
} | ||
} | ||
return prop; | ||
}; | ||
return prop; | ||
}; | ||
/** | ||
* Return the current frame `Scope`. | ||
* | ||
* @return {Scope} | ||
* @api private | ||
*/ | ||
/** | ||
* Return the current frame `Scope`. | ||
* | ||
* @return {Scope} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.__defineGetter__('currentScope', function(){ | ||
return this.stack.currentFrame.scope; | ||
}); | ||
get currentScope() { | ||
return this.stack.currentFrame.scope; | ||
}; | ||
/** | ||
* Return the current `Frame`. | ||
* | ||
* @return {Frame} | ||
* @api private | ||
*/ | ||
/** | ||
* Return the current `Frame`. | ||
* | ||
* @return {Frame} | ||
* @api private | ||
*/ | ||
Evaluator.prototype.__defineGetter__('currentFrame', function(){ | ||
return this.stack.currentFrame; | ||
}); | ||
get currentFrame() { | ||
return this.stack.currentFrame; | ||
}; | ||
}; |
@@ -8,25 +8,26 @@ | ||
/** | ||
* Initialize a new `Visitor` with the given `root` Node. | ||
* | ||
* @param {Node} root | ||
* @api private | ||
*/ | ||
module.exports = class Visitor { | ||
/** | ||
* Initialize a new `Visitor` with the given `root` Node. | ||
* | ||
* @param {Node} root | ||
* @api private | ||
*/ | ||
var Visitor = module.exports = function Visitor(root) { | ||
this.root = root; | ||
}; | ||
constructor(root) { | ||
this.root = root; | ||
} | ||
/** | ||
* Visit the given `node`. | ||
* | ||
* @param {Node|Array} node | ||
* @api public | ||
*/ | ||
/** | ||
* Visit the given `node`. | ||
* | ||
* @param {Node|Array} node | ||
* @api public | ||
*/ | ||
Visitor.prototype.visit = function(node, fn){ | ||
var method = 'visit' + node.constructor.name; | ||
if (this[method]) return this[method](node); | ||
return node; | ||
visit(node, fn) { | ||
var method = 'visit' + node.constructor.name; | ||
if (this[method]) return this[method](node); | ||
return node; | ||
}; | ||
}; | ||
@@ -16,200 +16,141 @@ | ||
/** | ||
* Initialize a new `Normalizer` with the given `root` Node. | ||
* | ||
* This visitor implements the first stage of the duel-stage | ||
* compiler, tasked with stripping the "garbage" from | ||
* the evaluated nodes, ditching null rules, resolving | ||
* ruleset selectors etc. This step performs the logic | ||
* necessary to facilitate the "@extend" functionality, | ||
* as these must be resolved _before_ buffering output. | ||
* | ||
* @param {Node} root | ||
* @api public | ||
*/ | ||
module.exports = class Normalizer extends Visitor { | ||
/** | ||
* Initialize a new `Normalizer` with the given `root` Node. | ||
* | ||
* This visitor implements the first stage of the duel-stage | ||
* compiler, tasked with stripping the "garbage" from | ||
* the evaluated nodes, ditching null rules, resolving | ||
* ruleset selectors etc. This step performs the logic | ||
* necessary to facilitate the "@extend" functionality, | ||
* as these must be resolved _before_ buffering output. | ||
* | ||
* @param {Node} root | ||
* @api public | ||
*/ | ||
var Normalizer = module.exports = function Normalizer(root, options) { | ||
options = options || {}; | ||
Visitor.call(this, root); | ||
this.hoist = options['hoist atrules']; | ||
this.stack = []; | ||
this.map = {}; | ||
this.imports = []; | ||
}; | ||
/** | ||
* Inherit from `Visitor.prototype`. | ||
*/ | ||
Normalizer.prototype.__proto__ = Visitor.prototype; | ||
/** | ||
* Normalize the node tree. | ||
* | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
Normalizer.prototype.normalize = function(){ | ||
var ret = this.visit(this.root); | ||
if (this.hoist) { | ||
// hoist @import | ||
if (this.imports.length) ret.nodes = this.imports.concat(ret.nodes); | ||
// hoist @charset | ||
if (this.charset) ret.nodes = [this.charset].concat(ret.nodes); | ||
constructor(root, options) { | ||
super(root); | ||
options = options || {}; | ||
this.hoist = options['hoist atrules']; | ||
this.stack = []; | ||
this.map = {}; | ||
this.imports = []; | ||
} | ||
return ret; | ||
}; | ||
/** | ||
* Normalize the node tree. | ||
* | ||
* @return {Node} | ||
* @api private | ||
*/ | ||
/** | ||
* Bubble up the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api private | ||
*/ | ||
normalize() { | ||
var ret = this.visit(this.root); | ||
Normalizer.prototype.bubble = function(node){ | ||
var props = [] | ||
, other = [] | ||
, self = this; | ||
if (this.hoist) { | ||
// hoist @import | ||
if (this.imports.length) ret.nodes = this.imports.concat(ret.nodes); | ||
function filterProps(block) { | ||
block.nodes.forEach(function(node) { | ||
node = self.visit(node); | ||
// hoist @charset | ||
if (this.charset) ret.nodes = [this.charset].concat(ret.nodes); | ||
} | ||
switch (node.nodeName) { | ||
case 'property': | ||
props.push(node); | ||
break; | ||
case 'block': | ||
filterProps(node); | ||
break; | ||
default: | ||
other.push(node); | ||
} | ||
}); | ||
} | ||
return ret; | ||
}; | ||
filterProps(node.block); | ||
/** | ||
* Bubble up the given `node`. | ||
* | ||
* @param {Node} node | ||
* @api private | ||
*/ | ||
if (props.length) { | ||
var selector = new nodes.Selector([new nodes.Literal('&')]); | ||
selector.lineno = node.lineno; | ||
selector.column = node.column; | ||
selector.filename = node.filename; | ||
selector.val = '&'; | ||
bubble(node) { | ||
var props = [] | ||
, other = [] | ||
, self = this; | ||
var group = new nodes.Group; | ||
group.lineno = node.lineno; | ||
group.column = node.column; | ||
group.filename = node.filename; | ||
function filterProps(block) { | ||
block.nodes.forEach(function (node) { | ||
node = self.visit(node); | ||
var block = new nodes.Block(node.block, group); | ||
block.lineno = node.lineno; | ||
block.column = node.column; | ||
block.filename = node.filename; | ||
switch (node.nodeName) { | ||
case 'property': | ||
props.push(node); | ||
break; | ||
case 'block': | ||
filterProps(node); | ||
break; | ||
default: | ||
other.push(node); | ||
} | ||
}); | ||
} | ||
props.forEach(function(prop){ | ||
block.push(prop); | ||
}); | ||
filterProps(node.block); | ||
group.push(selector); | ||
group.block = block; | ||
if (props.length) { | ||
var selector = new nodes.Selector([new nodes.Literal('&')]); | ||
selector.lineno = node.lineno; | ||
selector.column = node.column; | ||
selector.filename = node.filename; | ||
selector.val = '&'; | ||
node.block.nodes = []; | ||
node.block.push(group); | ||
other.forEach(function(n){ | ||
node.block.push(n); | ||
}); | ||
var group = new nodes.Group; | ||
group.lineno = node.lineno; | ||
group.column = node.column; | ||
group.filename = node.filename; | ||
var group = this.closestGroup(node.block); | ||
if (group) node.group = group.clone(); | ||
var block = new nodes.Block(node.block, group); | ||
block.lineno = node.lineno; | ||
block.column = node.column; | ||
block.filename = node.filename; | ||
node.bubbled = true; | ||
} | ||
}; | ||
props.forEach(function (prop) { | ||
block.push(prop); | ||
}); | ||
/** | ||
* Return group closest to the given `block`. | ||
* | ||
* @param {Block} block | ||
* @return {Group} | ||
* @api private | ||
*/ | ||
group.push(selector); | ||
group.block = block; | ||
Normalizer.prototype.closestGroup = function(block){ | ||
var parent = block.parent | ||
, node; | ||
while (parent && (node = parent.node)) { | ||
if ('group' == node.nodeName) return node; | ||
parent = node.block && node.block.parent; | ||
} | ||
}; | ||
node.block.nodes = []; | ||
node.block.push(group); | ||
other.forEach(function (n) { | ||
node.block.push(n); | ||
}); | ||
/** | ||
* Visit Root. | ||
*/ | ||
var group = this.closestGroup(node.block); | ||
if (group) node.group = group.clone(); | ||
Normalizer.prototype.visitRoot = function(block){ | ||
var ret = new nodes.Root | ||
, node; | ||
for (var i = 0; i < block.nodes.length; ++i) { | ||
node = block.nodes[i]; | ||
switch (node.nodeName) { | ||
case 'null': | ||
case 'expression': | ||
case 'function': | ||
case 'unit': | ||
case 'atblock': | ||
continue; | ||
default: | ||
this.rootIndex = i; | ||
ret.push(this.visit(node)); | ||
node.bubbled = true; | ||
} | ||
} | ||
}; | ||
return ret; | ||
}; | ||
/** | ||
* Return group closest to the given `block`. | ||
* | ||
* @param {Block} block | ||
* @return {Group} | ||
* @api private | ||
*/ | ||
/** | ||
* Visit Property. | ||
*/ | ||
Normalizer.prototype.visitProperty = function(prop){ | ||
this.visit(prop.expr); | ||
return prop; | ||
}; | ||
/** | ||
* Visit Expression. | ||
*/ | ||
Normalizer.prototype.visitExpression = function(expr){ | ||
expr.nodes = expr.nodes.map(function(node){ | ||
// returns `block` literal if mixin's block | ||
// is used as part of a property value | ||
if ('block' == node.nodeName) { | ||
var literal = new nodes.Literal('block'); | ||
literal.lineno = expr.lineno; | ||
literal.column = expr.column; | ||
return literal; | ||
closestGroup(block) { | ||
var parent = block.parent | ||
, node; | ||
while (parent && (node = parent.node)) { | ||
if ('group' == node.nodeName) return node; | ||
parent = node.block && node.block.parent; | ||
} | ||
return node; | ||
}); | ||
return expr; | ||
}; | ||
}; | ||
/** | ||
* Visit Block. | ||
*/ | ||
/** | ||
* Visit Root. | ||
*/ | ||
Normalizer.prototype.visitBlock = function(block){ | ||
var node; | ||
visitRoot(block) { | ||
var ret = new nodes.Root | ||
, node; | ||
if (block.hasProperties) { | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
for (var i = 0; i < block.nodes.length; ++i) { | ||
node = block.nodes[i]; | ||
@@ -220,3 +161,2 @@ switch (node.nodeName) { | ||
case 'function': | ||
case 'group': | ||
case 'unit': | ||
@@ -226,221 +166,277 @@ case 'atblock': | ||
default: | ||
block.nodes[i] = this.visit(node); | ||
this.rootIndex = i; | ||
ret.push(this.visit(node)); | ||
} | ||
} | ||
} | ||
// nesting | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
node = block.nodes[i]; | ||
block.nodes[i] = this.visit(node); | ||
} | ||
return ret; | ||
}; | ||
return block; | ||
}; | ||
/** | ||
* Visit Property. | ||
*/ | ||
/** | ||
* Visit Group. | ||
*/ | ||
visitProperty(prop) { | ||
this.visit(prop.expr); | ||
return prop; | ||
}; | ||
Normalizer.prototype.visitGroup = function(group){ | ||
var stack = this.stack | ||
, map = this.map | ||
, parts; | ||
/** | ||
* Visit Expression. | ||
*/ | ||
// normalize interpolated selectors with comma | ||
group.nodes.forEach(function(selector, i){ | ||
if (!~selector.val.indexOf(',')) return; | ||
if (~selector.val.indexOf('\\,')) { | ||
selector.val = selector.val.replace(/\\,/g, ','); | ||
return; | ||
} | ||
parts = selector.val.split(','); | ||
var root = '/' == selector.val.charAt(0) | ||
, part, s; | ||
for (var k = 0, len = parts.length; k < len; ++k){ | ||
part = parts[k].trim(); | ||
if (root && k > 0 && !~part.indexOf('&')) { | ||
part = '/' + part; | ||
visitExpression(expr) { | ||
expr.nodes = expr.nodes.map(function (node) { | ||
// returns `block` literal if mixin's block | ||
// is used as part of a property value | ||
if ('block' == node.nodeName) { | ||
var literal = new nodes.Literal('block'); | ||
literal.lineno = expr.lineno; | ||
literal.column = expr.column; | ||
return literal; | ||
} | ||
s = new nodes.Selector([new nodes.Literal(part)]); | ||
s.val = part; | ||
s.block = group.block; | ||
group.nodes[i++] = s; | ||
} | ||
}); | ||
stack.push(group.nodes); | ||
return node; | ||
}); | ||
return expr; | ||
}; | ||
var selectors = utils.compileSelectors(stack, true); | ||
/** | ||
* Visit Block. | ||
*/ | ||
// map for extension lookup | ||
selectors.forEach(function(selector){ | ||
map[selector] = map[selector] || []; | ||
map[selector].push(group); | ||
}); | ||
visitBlock(block) { | ||
var node; | ||
// extensions | ||
this.extend(group, selectors); | ||
if (block.hasProperties) { | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
node = block.nodes[i]; | ||
switch (node.nodeName) { | ||
case 'null': | ||
case 'expression': | ||
case 'function': | ||
case 'group': | ||
case 'unit': | ||
case 'atblock': | ||
continue; | ||
default: | ||
block.nodes[i] = this.visit(node); | ||
} | ||
} | ||
} | ||
stack.pop(); | ||
return group; | ||
}; | ||
// nesting | ||
for (var i = 0, len = block.nodes.length; i < len; ++i) { | ||
node = block.nodes[i]; | ||
block.nodes[i] = this.visit(node); | ||
} | ||
/** | ||
* Visit Function. | ||
*/ | ||
return block; | ||
}; | ||
Normalizer.prototype.visitFunction = function(){ | ||
return nodes.null; | ||
}; | ||
/** | ||
* Visit Group. | ||
*/ | ||
/** | ||
* Visit Media. | ||
*/ | ||
visitGroup(group) { | ||
var stack = this.stack | ||
, map = this.map | ||
, parts; | ||
Normalizer.prototype.visitMedia = function(media){ | ||
var medias = [] | ||
, group = this.closestGroup(media.block) | ||
, parent; | ||
function mergeQueries(block) { | ||
block.nodes.forEach(function(node, i){ | ||
switch (node.nodeName) { | ||
case 'media': | ||
node.val = media.val.merge(node.val); | ||
medias.push(node); | ||
block.nodes[i] = nodes.null; | ||
break; | ||
case 'block': | ||
mergeQueries(node); | ||
break; | ||
default: | ||
if (node.block && node.block.nodes) | ||
mergeQueries(node.block); | ||
// normalize interpolated selectors with comma | ||
group.nodes.forEach(function (selector, i) { | ||
if (!~selector.val.indexOf(',')) return; | ||
if (~selector.val.indexOf('\\,')) { | ||
selector.val = selector.val.replace(/\\,/g, ','); | ||
return; | ||
} | ||
parts = selector.val.split(','); | ||
var root = '/' == selector.val.charAt(0) | ||
, part, s; | ||
for (var k = 0, len = parts.length; k < len; ++k) { | ||
part = parts[k].trim(); | ||
if (root && k > 0 && !~part.indexOf('&')) { | ||
part = '/' + part; | ||
} | ||
s = new nodes.Selector([new nodes.Literal(part)]); | ||
s.val = part; | ||
s.block = group.block; | ||
group.nodes[i++] = s; | ||
} | ||
}); | ||
} | ||
stack.push(group.nodes); | ||
mergeQueries(media.block); | ||
this.bubble(media); | ||
var selectors = utils.compileSelectors(stack, true); | ||
if (medias.length) { | ||
medias.forEach(function(node){ | ||
if (group) { | ||
group.block.push(node); | ||
} else { | ||
this.root.nodes.splice(++this.rootIndex, 0, node); | ||
} | ||
node = this.visit(node); | ||
parent = node.block.parent; | ||
if (node.bubbled && (!group || 'group' == parent.node.nodeName)) { | ||
node.group.block = node.block.nodes[0].block; | ||
node.block.nodes[0] = node.group; | ||
} | ||
}, this); | ||
} | ||
return media; | ||
}; | ||
// map for extension lookup | ||
selectors.forEach(function (selector) { | ||
map[selector] = map[selector] || []; | ||
map[selector].push(group); | ||
}); | ||
/** | ||
* Visit Supports. | ||
*/ | ||
// extensions | ||
this.extend(group, selectors); | ||
Normalizer.prototype.visitSupports = function(node){ | ||
this.bubble(node); | ||
return node; | ||
}; | ||
stack.pop(); | ||
return group; | ||
}; | ||
/** | ||
* Visit Atrule. | ||
*/ | ||
/** | ||
* Visit Function. | ||
*/ | ||
Normalizer.prototype.visitAtrule = function(node){ | ||
if (node.block) node.block = this.visit(node.block); | ||
return node; | ||
}; | ||
visitFunction() { | ||
return nodes.null; | ||
}; | ||
/** | ||
* Visit Keyframes. | ||
*/ | ||
/** | ||
* Visit Media. | ||
*/ | ||
Normalizer.prototype.visitKeyframes = function(node){ | ||
var frames = node.block.nodes.filter(function(frame){ | ||
return frame.block && frame.block.hasProperties; | ||
}); | ||
node.frames = frames.length; | ||
return node; | ||
}; | ||
visitMedia(media) { | ||
var medias = [] | ||
, group = this.closestGroup(media.block) | ||
, parent; | ||
/** | ||
* Visit Import. | ||
*/ | ||
function mergeQueries(block) { | ||
block.nodes.forEach(function (node, i) { | ||
switch (node.nodeName) { | ||
case 'media': | ||
node.val = media.val.merge(node.val); | ||
medias.push(node); | ||
block.nodes[i] = nodes.null; | ||
break; | ||
case 'block': | ||
mergeQueries(node); | ||
break; | ||
default: | ||
if (node.block && node.block.nodes) | ||
mergeQueries(node.block); | ||
} | ||
}); | ||
} | ||
Normalizer.prototype.visitImport = function(node){ | ||
this.imports.push(node); | ||
return this.hoist ? nodes.null : node; | ||
}; | ||
mergeQueries(media.block); | ||
this.bubble(media); | ||
/** | ||
* Visit Charset. | ||
*/ | ||
if (medias.length) { | ||
medias.forEach(function (node) { | ||
if (group) { | ||
group.block.push(node); | ||
} else { | ||
this.root.nodes.splice(++this.rootIndex, 0, node); | ||
} | ||
node = this.visit(node); | ||
parent = node.block.parent; | ||
if (node.bubbled && (!group || 'group' == parent.node.nodeName)) { | ||
node.group.block = node.block.nodes[0].block; | ||
node.block.nodes[0] = node.group; | ||
} | ||
}, this); | ||
} | ||
return media; | ||
}; | ||
Normalizer.prototype.visitCharset = function(node){ | ||
this.charset = node; | ||
return this.hoist ? nodes.null : node; | ||
}; | ||
/** | ||
* Visit Supports. | ||
*/ | ||
/** | ||
* Apply `group` extensions. | ||
* | ||
* @param {Group} group | ||
* @param {Array} selectors | ||
* @api private | ||
*/ | ||
visitSupports(node) { | ||
this.bubble(node); | ||
return node; | ||
}; | ||
Normalizer.prototype.extend = function(group, selectors){ | ||
var map = this.map | ||
, self = this | ||
, parent = this.closestGroup(group.block); | ||
/** | ||
* Visit Atrule. | ||
*/ | ||
group.extends.forEach(function(extend){ | ||
var groups = map[extend.selector]; | ||
if (!groups) { | ||
if (extend.optional) return; | ||
groups = self._checkForPrefixedGroups(extend.selector); | ||
if(!groups) { | ||
var err = new Error('Failed to @extend "' + extend.selector + '"'); | ||
err.lineno = extend.lineno; | ||
err.column = extend.column; | ||
throw err; | ||
visitAtrule(node) { | ||
if (node.block) node.block = this.visit(node.block); | ||
return node; | ||
}; | ||
/** | ||
* Visit Keyframes. | ||
*/ | ||
visitKeyframes(node) { | ||
var frames = node.block.nodes.filter(function (frame) { | ||
return frame.block && frame.block.hasProperties; | ||
}); | ||
node.frames = frames.length; | ||
return node; | ||
}; | ||
/** | ||
* Visit Import. | ||
*/ | ||
visitImport(node) { | ||
this.imports.push(node); | ||
return this.hoist ? nodes.null : node; | ||
}; | ||
/** | ||
* Visit Charset. | ||
*/ | ||
visitCharset(node) { | ||
this.charset = node; | ||
return this.hoist ? nodes.null : node; | ||
}; | ||
/** | ||
* Apply `group` extensions. | ||
* | ||
* @param {Group} group | ||
* @param {Array} selectors | ||
* @api private | ||
*/ | ||
extend(group, selectors) { | ||
var map = this.map | ||
, self = this | ||
, parent = this.closestGroup(group.block); | ||
group.extends.forEach(function (extend) { | ||
var groups = map[extend.selector]; | ||
if (!groups) { | ||
if (extend.optional) return; | ||
groups = self._checkForPrefixedGroups(extend.selector); | ||
if (!groups) { | ||
var err = new Error('Failed to @extend "' + extend.selector + '"'); | ||
err.lineno = extend.lineno; | ||
err.column = extend.column; | ||
throw err; | ||
} | ||
} | ||
} | ||
selectors.forEach(function(selector){ | ||
var node = new nodes.Selector; | ||
node.val = selector; | ||
node.inherits = false; | ||
groups.forEach(function(group){ | ||
// prevent recursive extend | ||
if (!parent || (parent != group)) self.extend(group, selectors); | ||
group.push(node); | ||
selectors.forEach(function (selector) { | ||
var node = new nodes.Selector; | ||
node.val = selector; | ||
node.inherits = false; | ||
groups.forEach(function (group) { | ||
// prevent recursive extend | ||
if (!parent || (parent != group)) self.extend(group, selectors); | ||
group.push(node); | ||
}); | ||
}); | ||
}); | ||
}); | ||
group.block = this.visit(group.block); | ||
group.block = this.visit(group.block); | ||
}; | ||
_checkForPrefixedGroups(selector) { | ||
var prefix = []; | ||
var map = this.map; | ||
var result = null; | ||
for (var i = 0; i < this.stack.length; i++) { | ||
var stackElementArray = this.stack[i]; | ||
var stackElement = stackElementArray[0]; | ||
prefix.push(stackElement.val); | ||
var fullSelector = prefix.join(" ") + " " + selector; | ||
result = map[fullSelector]; | ||
if (result) | ||
break; | ||
} | ||
return result; | ||
}; | ||
}; | ||
Normalizer.prototype._checkForPrefixedGroups = function (selector) { | ||
var prefix = []; | ||
var map = this.map; | ||
var result = null; | ||
for (var i = 0; i < this.stack.length; i++) { | ||
var stackElementArray=this.stack[i]; | ||
var stackElement = stackElementArray[0]; | ||
prefix.push(stackElement.val); | ||
var fullSelector = prefix.join(" ") + " " + selector; | ||
result = map[fullSelector]; | ||
if (result) | ||
break; | ||
} | ||
return result; | ||
}; |
@@ -22,184 +22,178 @@ /*! | ||
/** | ||
* Initialize a new `SourceMapper` generator with the given `root` Node | ||
* and the following `options`. | ||
* | ||
* @param {Node} root | ||
* @api public | ||
*/ | ||
module.exports = class SourceMapper extends Compiler { | ||
/** | ||
* Initialize a new `SourceMapper` generator with the given `root` Node | ||
* and the following `options`. | ||
* | ||
* @param {Node} root | ||
* @api public | ||
*/ | ||
var SourceMapper = module.exports = function SourceMapper(root, options){ | ||
options = options || {}; | ||
this.column = 1; | ||
this.lineno = 1; | ||
this.contents = {}; | ||
this.filename = options.filename; | ||
this.dest = options.dest; | ||
var sourcemap = options.sourcemap; | ||
this.basePath = sourcemap.basePath || '.'; | ||
this.inline = sourcemap.inline; | ||
this.comment = sourcemap.comment; | ||
if (this.dest && extname(this.dest) === '.css') { | ||
this.basename = basename(this.dest); | ||
this.dest = dirname(this.dest); | ||
} else { | ||
this.basename = basename(this.filename, extname(this.filename)) + '.css'; | ||
} | ||
this.utf8 = false; | ||
constructor(root, options) { | ||
super(root, options); | ||
options = options || {}; | ||
this.column = 1; | ||
this.lineno = 1; | ||
this.contents = {}; | ||
this.filename = options.filename; | ||
this.dest = options.dest; | ||
this.map = new SourceMapGenerator({ | ||
file: this.basename, | ||
sourceRoot: sourcemap.sourceRoot || null | ||
}); | ||
Compiler.call(this, root, options); | ||
}; | ||
var sourcemap = options.sourcemap; | ||
this.basePath = sourcemap.basePath || '.'; | ||
this.inline = sourcemap.inline; | ||
this.comment = sourcemap.comment; | ||
if (this.dest && extname(this.dest) === '.css') { | ||
this.basename = basename(this.dest); | ||
this.dest = dirname(this.dest); | ||
} else { | ||
this.basename = basename(this.filename, extname(this.filename)) + '.css'; | ||
} | ||
this.utf8 = false; | ||
/** | ||
* Inherit from `Compiler.prototype`. | ||
*/ | ||
this.map = new SourceMapGenerator({ | ||
file: this.basename, | ||
sourceRoot: sourcemap.sourceRoot || null | ||
}); | ||
} | ||
SourceMapper.prototype.__proto__ = Compiler.prototype; | ||
/** | ||
* Generate and write source map. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Generate and write source map. | ||
* | ||
* @return {String} | ||
* @api private | ||
*/ | ||
compile() { | ||
var css = super.compile.call(this) | ||
, out = this.basename + '.map' | ||
, url = this.normalizePath(this.dest | ||
? join(this.dest, out) | ||
: join(dirname(this.filename), out)) | ||
, map; | ||
var compile = Compiler.prototype.compile; | ||
SourceMapper.prototype.compile = function(){ | ||
var css = compile.call(this) | ||
, out = this.basename + '.map' | ||
, url = this.normalizePath(this.dest | ||
? join(this.dest, out) | ||
: join(dirname(this.filename), out)) | ||
, map; | ||
if (this.inline) { | ||
map = this.map.toString(); | ||
url = 'data:application/json;' | ||
+ (this.utf8 ? 'charset=utf-8;' : '') + 'base64,' | ||
+ Buffer.from(map).toString('base64'); | ||
} | ||
if (this.inline || false !== this.comment) | ||
css += '/*# sourceMappingURL=' + url + ' */'; | ||
return css; | ||
}; | ||
if (this.inline) { | ||
map = this.map.toString(); | ||
url = 'data:application/json;' | ||
+ (this.utf8 ? 'charset=utf-8;' : '') + 'base64,' | ||
+ Buffer.from(map).toString('base64'); | ||
} | ||
if (this.inline || false !== this.comment) | ||
css += '/*# sourceMappingURL=' + url + ' */'; | ||
return css; | ||
}; | ||
/** | ||
* Add mapping information. | ||
* | ||
* @param {String} str | ||
* @param {Node} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Add mapping information. | ||
* | ||
* @param {String} str | ||
* @param {Node} node | ||
* @return {String} | ||
* @api private | ||
*/ | ||
out(str, node) { | ||
if (node && node.lineno) { | ||
var filename = this.normalizePath(node.filename); | ||
SourceMapper.prototype.out = function(str, node){ | ||
if (node && node.lineno) { | ||
var filename = this.normalizePath(node.filename); | ||
this.map.addMapping({ | ||
original: { | ||
line: node.lineno, | ||
column: node.column - 1 | ||
}, | ||
generated: { | ||
line: this.lineno, | ||
column: this.column - 1 | ||
}, | ||
source: filename | ||
}); | ||
this.map.addMapping({ | ||
original: { | ||
line: node.lineno, | ||
column: node.column - 1 | ||
}, | ||
generated: { | ||
line: this.lineno, | ||
column: this.column - 1 | ||
}, | ||
source: filename | ||
}); | ||
if (this.inline && !this.contents[filename]) { | ||
this.map.setSourceContent(filename, fs.readFileSync(node.filename, 'utf-8')); | ||
this.contents[filename] = true; | ||
if (this.inline && !this.contents[filename]) { | ||
this.map.setSourceContent(filename, fs.readFileSync(node.filename, 'utf-8')); | ||
this.contents[filename] = true; | ||
} | ||
} | ||
} | ||
this.move(str); | ||
return str; | ||
}; | ||
this.move(str); | ||
return str; | ||
}; | ||
/** | ||
* Move current line and column position. | ||
* | ||
* @param {String} str | ||
* @api private | ||
*/ | ||
/** | ||
* Move current line and column position. | ||
* | ||
* @param {String} str | ||
* @api private | ||
*/ | ||
SourceMapper.prototype.move = function(str){ | ||
var lines = str.match(/\n/g) | ||
, idx = str.lastIndexOf('\n'); | ||
move(str) { | ||
var lines = str.match(/\n/g) | ||
, idx = str.lastIndexOf('\n'); | ||
if (lines) this.lineno += lines.length; | ||
this.column = ~idx | ||
? str.length - idx | ||
: this.column + str.length; | ||
}; | ||
if (lines) this.lineno += lines.length; | ||
this.column = ~idx | ||
? str.length - idx | ||
: this.column + str.length; | ||
}; | ||
/** | ||
* Normalize the given `path`. | ||
* | ||
* @param {String} path | ||
* @return {String} | ||
* @api private | ||
*/ | ||
/** | ||
* Normalize the given `path`. | ||
* | ||
* @param {String} path | ||
* @return {String} | ||
* @api private | ||
*/ | ||
SourceMapper.prototype.normalizePath = function(path){ | ||
path = relative(this.dest || this.basePath, path); | ||
if ('\\' == sep) { | ||
path = path.replace(/^[a-z]:\\/i, '/') | ||
.replace(/\\/g, '/'); | ||
} | ||
return path; | ||
}; | ||
normalizePath(path) { | ||
path = relative(this.dest || this.basePath, path); | ||
if ('\\' == sep) { | ||
path = path.replace(/^[a-z]:\\/i, '/') | ||
.replace(/\\/g, '/'); | ||
} | ||
return path; | ||
}; | ||
/** | ||
* Visit Literal. | ||
*/ | ||
/** | ||
* Visit Literal. | ||
*/ | ||
var literal = Compiler.prototype.visitLiteral; | ||
SourceMapper.prototype.visitLiteral = function(lit){ | ||
var val = literal.call(this, lit) | ||
, filename = this.normalizePath(lit.filename) | ||
, indentsRe = /^\s+/ | ||
, lines = val.split('\n'); | ||
visitLiteral(lit) { | ||
var val = super.visitLiteral.call(this, lit) | ||
, filename = this.normalizePath(lit.filename) | ||
, indentsRe = /^\s+/ | ||
, lines = val.split('\n'); | ||
// add mappings for multiline literals | ||
if (lines.length > 1) { | ||
lines.forEach(function(line, i) { | ||
var indents = line.match(indentsRe) | ||
, column = indents && indents[0] | ||
// add mappings for multiline literals | ||
if (lines.length > 1) { | ||
lines.forEach(function (line, i) { | ||
var indents = line.match(indentsRe) | ||
, column = indents && indents[0] | ||
? indents[0].length | ||
: 0; | ||
if (lit.css) column += 2; | ||
if (lit.css) column += 2; | ||
this.map.addMapping({ | ||
original: { | ||
line: lit.lineno + i, | ||
column: column | ||
}, | ||
generated: { | ||
line: this.lineno + i, | ||
column: 0 | ||
}, | ||
source: filename | ||
}); | ||
}, this); | ||
} | ||
return val; | ||
}; | ||
this.map.addMapping({ | ||
original: { | ||
line: lit.lineno + i, | ||
column: column | ||
}, | ||
generated: { | ||
line: this.lineno + i, | ||
column: 0 | ||
}, | ||
source: filename | ||
}); | ||
}, this); | ||
} | ||
return val; | ||
}; | ||
/** | ||
* Visit Charset. | ||
*/ | ||
/** | ||
* Visit Charset. | ||
*/ | ||
var charset = Compiler.prototype.visitCharset; | ||
SourceMapper.prototype.visitCharset = function(node){ | ||
this.utf8 = ('utf-8' == node.val.string.toLowerCase()); | ||
return charset.call(this, node); | ||
visitCharset(node) { | ||
this.utf8 = ('utf-8' == node.val.string.toLowerCase()); | ||
return super.visitCharset(node); | ||
}; | ||
}; |
{ | ||
"name": "stylus", | ||
"description": "Robust, expressive, and feature-rich CSS superset", | ||
"version": "0.59.0", | ||
"version": "0.60.0", | ||
"author": "TJ Holowaychuk <tj@vision-media.ca>", | ||
@@ -31,3 +31,3 @@ "keywords": [ | ||
"dependencies": { | ||
"@adobe/css-tools": "^4.0.1", | ||
"@adobe/css-tools": "~4.2.0", | ||
"debug": "^4.3.2", | ||
@@ -34,0 +34,0 @@ "glob": "^7.1.6", |
@@ -62,6 +62,2 @@ <p align="center"><a href="https://stylus-lang.com" target="_blank" rel="noopener noreferrer"><img width="150" src="https://raw.githubusercontent.com/stylus/stylus/dev/graphics/Logos/stylus.png" alt="Stylus logo"></a></p> | ||
### 📖 New Docs (alpha) | ||
Try our new [official documentation website](http://stylus-docs.netlify.app/) and give us feedback via [github issues](https://github.com/stylus/stylus/issues), thanks. | ||
### Community modules | ||
@@ -68,0 +64,0 @@ |
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
366495
13917
87
+ Added@adobe/css-tools@4.2.0(transitive)
- Removed@adobe/css-tools@4.3.3(transitive)
Updated@adobe/css-tools@~4.2.0