You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
882 lines
18 KiB
882 lines
18 KiB
|
|
/*! |
|
* Stylus - Lexer |
|
* Copyright (c) Automattic <developer.wordpress.com> |
|
* MIT Licensed |
|
*/ |
|
|
|
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
var Token = require('./token') |
|
, nodes = require('./nodes') |
|
, errors = require('./errors'); |
|
|
|
/** |
|
* Expose `Lexer`. |
|
*/ |
|
|
|
exports = module.exports = Lexer; |
|
|
|
/** |
|
* Operator aliases. |
|
*/ |
|
|
|
var alias = { |
|
'and': '&&' |
|
, 'or': '||' |
|
, 'is': '==' |
|
, 'isnt': '!=' |
|
, 'is not': '!=' |
|
, ':=': '?=' |
|
}; |
|
|
|
/** |
|
* 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; |
|
|
|
// 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 ('/' == s[i] && '/' == s[i + 1]) { |
|
inComment = !single && !double; |
|
break; |
|
} |
|
++i; |
|
} |
|
} |
|
|
|
return inComment |
|
? str |
|
: val + '\r'; |
|
}; |
|
|
|
// 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); |
|
}; |
|
|
|
/** |
|
* Lexer prototype. |
|
*/ |
|
|
|
Lexer.prototype = { |
|
|
|
/** |
|
* Custom inspect. |
|
*/ |
|
|
|
inspect: function(){ |
|
var tok |
|
, tmp = this.str |
|
, buf = []; |
|
while ('eos' != (tok = this.next()).type) { |
|
buf.push(tok.inspect()); |
|
} |
|
this.str = tmp; |
|
return buf.concat(tok.inspect()).join('\n'); |
|
}, |
|
|
|
/** |
|
* Lookahead `n` tokens. |
|
* |
|
* @param {Number} n |
|
* @return {Object} |
|
* @api private |
|
*/ |
|
|
|
lookahead: function(n){ |
|
var fetch = n - this.stash.length; |
|
while (fetch-- > 0) this.stash.push(this.advance()); |
|
return this.stash[--n]; |
|
}, |
|
|
|
/** |
|
* Consume the given `len`. |
|
* |
|
* @param {Number|Array} len |
|
* @api private |
|
*/ |
|
|
|
skip: function(len){ |
|
var chunk = len[0]; |
|
len = chunk ? chunk.length : len; |
|
this.str = this.str.substr(len); |
|
if (chunk) { |
|
this.move(chunk); |
|
} else { |
|
this.column += len; |
|
} |
|
}, |
|
|
|
/** |
|
* Move current line and column position. |
|
* |
|
* @param {String} str |
|
* @api private |
|
*/ |
|
|
|
move: function(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; |
|
}, |
|
|
|
/** |
|
* Fetch next token including those stashed by peek. |
|
* |
|
* @return {Token} |
|
* @api private |
|
*/ |
|
|
|
next: function() { |
|
var tok = this.stashed() || this.advance(); |
|
this.prev = tok; |
|
return tok; |
|
}, |
|
|
|
/** |
|
* Check if the current token is a part of selector. |
|
* |
|
* @return {Boolean} |
|
* @api private |
|
*/ |
|
|
|
isPartOfSelector: function() { |
|
var tok = this.stash[this.stash.length - 1] || this.prev; |
|
switch (tok && tok.type) { |
|
// #for |
|
case 'color': |
|
return 2 == tok.val.raw.length; |
|
// .or |
|
case '.': |
|
// [is] |
|
case '[': |
|
return true; |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Fetch next token. |
|
* |
|
* @return {Token} |
|
* @api private |
|
*/ |
|
|
|
advance: function() { |
|
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() |
|
|| this.eol() |
|
|| this.space() |
|
|| this.selector(); |
|
tok.lineno = line; |
|
tok.column = column; |
|
return tok; |
|
}, |
|
|
|
/** |
|
* Lookahead a single token. |
|
* |
|
* @return {Token} |
|
* @api private |
|
*/ |
|
|
|
peek: function() { |
|
return this.lookahead(1); |
|
}, |
|
|
|
/** |
|
* Return the next possibly stashed token. |
|
* |
|
* @return {Token} |
|
* @api private |
|
*/ |
|
|
|
stashed: function() { |
|
return this.stash.shift(); |
|
}, |
|
|
|
/** |
|
* EOS | trailing outdents. |
|
*/ |
|
|
|
eos: function() { |
|
if (this.str.length) return; |
|
if (this.indentStack.length) { |
|
this.indentStack.shift(); |
|
return new Token('outdent'); |
|
} else { |
|
return new Token('eos'); |
|
} |
|
}, |
|
|
|
/** |
|
* url char |
|
*/ |
|
|
|
urlchars: function() { |
|
var captures; |
|
if (!this.isURL) return; |
|
if (captures = /^[\/:@.;?&=*!,<>#%0-9]+/.exec(this.str)) { |
|
this.skip(captures); |
|
return new Token('literal', new nodes.Literal(captures[0])); |
|
} |
|
}, |
|
|
|
/** |
|
* ';' [ \t]* |
|
*/ |
|
|
|
sep: function() { |
|
var captures; |
|
if (captures = /^;[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
return new Token(';'); |
|
} |
|
}, |
|
|
|
/** |
|
* '\r' |
|
*/ |
|
|
|
eol: function() { |
|
if ('\r' == this.str[0]) { |
|
++this.lineno; |
|
this.skip(1); |
|
return this.advance(); |
|
} |
|
}, |
|
|
|
/** |
|
* ' '+ |
|
*/ |
|
|
|
space: function() { |
|
var captures; |
|
if (captures = /^([ \t]+)/.exec(this.str)) { |
|
this.skip(captures); |
|
return new Token('space'); |
|
} |
|
}, |
|
|
|
/** |
|
* '\\' . ' '* |
|
*/ |
|
|
|
escaped: function() { |
|
var captures; |
|
if (captures = /^\\(.)[ \t]*/.exec(this.str)) { |
|
var c = captures[1]; |
|
this.skip(captures); |
|
return new Token('ident', new nodes.Literal(c)); |
|
} |
|
}, |
|
|
|
/** |
|
* '@css' ' '* '{' .* '}' ' '* |
|
*/ |
|
|
|
literal: function() { |
|
// HACK attack !!! |
|
var captures; |
|
if (captures = /^@css[ \t]*\{/.exec(this.str)) { |
|
this.skip(captures); |
|
var c |
|
, braces = 1 |
|
, css = '' |
|
, node; |
|
while (c = this.str[0]) { |
|
this.str = this.str.substr(1); |
|
switch (c) { |
|
case '{': ++braces; break; |
|
case '}': --braces; break; |
|
case '\n': |
|
case '\r': |
|
++this.lineno; |
|
break; |
|
} |
|
css += c; |
|
if (!braces) break; |
|
} |
|
css = css.replace(/\s*}$/, ''); |
|
node = new nodes.Literal(css); |
|
node.css = true; |
|
return new Token('literal', node); |
|
} |
|
}, |
|
|
|
/** |
|
* '!important' ' '* |
|
*/ |
|
|
|
important: function() { |
|
var captures; |
|
if (captures = /^!important[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
return new Token('ident', new nodes.Literal('!important')); |
|
} |
|
}, |
|
|
|
/** |
|
* '{' | '}' |
|
*/ |
|
|
|
brace: function() { |
|
var captures; |
|
if (captures = /^([{}])/.exec(this.str)) { |
|
this.skip(1); |
|
var brace = captures[1]; |
|
return new Token(brace, brace); |
|
} |
|
}, |
|
|
|
/** |
|
* '(' | ')' ' '* |
|
*/ |
|
|
|
paren: function() { |
|
var captures; |
|
if (captures = /^([()])([ \t]*)/.exec(this.str)) { |
|
var paren = captures[1]; |
|
this.skip(captures); |
|
if (')' == paren) this.isURL = false; |
|
var tok = new Token(paren, paren); |
|
tok.space = captures[2]; |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* 'null' |
|
*/ |
|
|
|
null: function() { |
|
var captures |
|
, tok; |
|
if (captures = /^(null)\b[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
if (this.isPartOfSelector()) { |
|
tok = new Token('ident', new nodes.Ident(captures[0])); |
|
} else { |
|
tok = new Token('null', nodes.null); |
|
} |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* 'if' |
|
* | 'else' |
|
* | 'unless' |
|
* | 'return' |
|
* | 'for' |
|
* | 'in' |
|
*/ |
|
|
|
keyword: function() { |
|
var captures |
|
, tok; |
|
if (captures = /^(return|if|else|unless|for|in)\b[ \t]*/.exec(this.str)) { |
|
var keyword = captures[1]; |
|
this.skip(captures); |
|
if (this.isPartOfSelector()) { |
|
tok = new Token('ident', new nodes.Ident(captures[0])); |
|
} else { |
|
tok = new Token(keyword, keyword); |
|
} |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* 'not' |
|
* | 'and' |
|
* | 'or' |
|
* | 'is' |
|
* | 'is not' |
|
* | 'isnt' |
|
* | 'is a' |
|
* | 'is defined' |
|
*/ |
|
|
|
namedop: function() { |
|
var captures |
|
, tok; |
|
if (captures = /^(not|and|or|is a|is defined|isnt|is not|is)(?!-)\b([ \t]*)/.exec(this.str)) { |
|
var op = captures[1]; |
|
this.skip(captures); |
|
if (this.isPartOfSelector()) { |
|
tok = new Token('ident', new nodes.Ident(captures[0])); |
|
} else { |
|
op = alias[op] || op; |
|
tok = new Token(op, op); |
|
} |
|
tok.space = captures[2]; |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* ',' |
|
* | '+' |
|
* | '+=' |
|
* | '-' |
|
* | '-=' |
|
* | '*' |
|
* | '*=' |
|
* | '/' |
|
* | '/=' |
|
* | '%' |
|
* | '%=' |
|
* | '**' |
|
* | '!' |
|
* | '&' |
|
* | '&&' |
|
* | '||' |
|
* | '>' |
|
* | '>=' |
|
* | '<' |
|
* | '<=' |
|
* | '=' |
|
* | '==' |
|
* | '!=' |
|
* | '!' |
|
* | '~' |
|
* | '?=' |
|
* | ':=' |
|
* | '?' |
|
* | ':' |
|
* | '[' |
|
* | ']' |
|
* | '.' |
|
* | '..' |
|
* | '...' |
|
*/ |
|
|
|
op: function() { |
|
var captures; |
|
if (captures = /^([.]{1,3}|&&|\|\||[!<>=?:]=|\*\*|[-+*\/%]=?|[,=?:!~<>&\[\]])([ \t]*)/.exec(this.str)) { |
|
var op = captures[1]; |
|
this.skip(captures); |
|
op = alias[op] || op; |
|
var tok = new Token(op, op); |
|
tok.space = captures[2]; |
|
this.isURL = false; |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* '@(' |
|
*/ |
|
|
|
anonFunc: function() { |
|
var tok; |
|
if ('@' == this.str[0] && '(' == this.str[1]) { |
|
this.skip(2); |
|
tok = new Token('function', new nodes.Ident('anonymous')); |
|
tok.anonymous = true; |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* '@' (-(\w+)-)?[a-zA-Z0-9-_]+ |
|
*/ |
|
|
|
atrule: function() { |
|
var captures; |
|
if (captures = /^@(?:-(\w+)-)?([a-zA-Z0-9-_]+)[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var vendor = captures[1] |
|
, type = captures[2] |
|
, tok; |
|
switch (type) { |
|
case 'require': |
|
case 'import': |
|
case 'charset': |
|
case 'namespace': |
|
case 'media': |
|
case 'scope': |
|
case 'supports': |
|
return new Token(type); |
|
case 'document': |
|
return new Token('-moz-document'); |
|
case 'block': |
|
return new Token('atblock'); |
|
case 'extend': |
|
case 'extends': |
|
return new Token('extend'); |
|
case 'keyframes': |
|
return new Token(type, vendor); |
|
default: |
|
return new Token('atrule', (vendor ? '-' + vendor + '-' + type : type)); |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* '//' * |
|
*/ |
|
|
|
comment: function() { |
|
// Single line |
|
if ('/' == this.str[0] && '/' == this.str[1]) { |
|
var end = this.str.indexOf('\n'); |
|
if (-1 == end) end = this.str.length; |
|
this.skip(end); |
|
return this.advance(); |
|
} |
|
|
|
// Multi-line |
|
if ('/' == this.str[0] && '*' == this.str[1]) { |
|
var end = this.str.indexOf('*/'); |
|
if (-1 == end) end = this.str.length; |
|
var str = this.str.substr(0, end + 2) |
|
, lines = str.split(/\n|\r/).length - 1 |
|
, suppress = true |
|
, inline = false; |
|
this.lineno += lines; |
|
this.skip(end + 2); |
|
// output |
|
if ('!' == str[2]) { |
|
str = str.replace('*!', '*'); |
|
suppress = false; |
|
} |
|
if (this.prev && ';' == this.prev.type) inline = true; |
|
return new Token('comment', new nodes.Comment(str, suppress, inline)); |
|
} |
|
}, |
|
|
|
/** |
|
* 'true' | 'false' |
|
*/ |
|
|
|
boolean: function() { |
|
var captures; |
|
if (captures = /^(true|false)\b([ \t]*)/.exec(this.str)) { |
|
var val = nodes.Boolean('true' == captures[1]); |
|
this.skip(captures); |
|
var tok = new Token('boolean', val); |
|
tok.space = captures[2]; |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* 'U+' [0-9A-Fa-f?]{1,6}(?:-[0-9A-Fa-f]{1,6})? |
|
*/ |
|
|
|
unicode: function() { |
|
var captures; |
|
if (captures = /^u\+[0-9a-f?]{1,6}(?:-[0-9a-f]{1,6})?/i.exec(this.str)) { |
|
this.skip(captures); |
|
return new Token('literal', new nodes.Literal(captures[0])); |
|
} |
|
}, |
|
|
|
/** |
|
* -*[_a-zA-Z$] [-\w\d$]* '(' |
|
*/ |
|
|
|
function: function() { |
|
var captures; |
|
if (captures = /^(-*[_a-zA-Z$][-\w\d$]*)\(([ \t]*)/.exec(this.str)) { |
|
var name = captures[1]; |
|
this.skip(captures); |
|
this.isURL = 'url' == name; |
|
var tok = new Token('function', new nodes.Ident(name)); |
|
tok.space = captures[2]; |
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* -*[_a-zA-Z$] [-\w\d$]* |
|
*/ |
|
|
|
ident: function() { |
|
var captures; |
|
if (captures = /^-*[_a-zA-Z$][-\w\d$]*/.exec(this.str)) { |
|
this.skip(captures); |
|
return new Token('ident', new nodes.Ident(captures[0])); |
|
} |
|
}, |
|
|
|
/** |
|
* '\n' ' '+ |
|
*/ |
|
|
|
newline: function() { |
|
var captures, re; |
|
|
|
// we have established the indentation regexp |
|
if (this.indentRe){ |
|
captures = this.indentRe.exec(this.str); |
|
// figure out if we are using tabs or spaces |
|
} else { |
|
// try tabs |
|
re = /^\n([\t]*)[ \t]*/; |
|
captures = re.exec(this.str); |
|
|
|
// nope, try spaces |
|
if (captures && !captures[1].length) { |
|
re = /^\n([ \t]*)/; |
|
captures = re.exec(this.str); |
|
} |
|
|
|
// established |
|
if (captures && captures[1].length) this.indentRe = re; |
|
} |
|
|
|
|
|
if (captures) { |
|
var tok |
|
, indents = captures[1].length; |
|
|
|
this.skip(captures); |
|
if (this.str[0] === ' ' || this.str[0] === '\t') { |
|
throw new errors.SyntaxError('Invalid indentation. You can use tabs or spaces to indent, but not both.'); |
|
} |
|
|
|
// Blank line |
|
if ('\n' == this.str[0]) return this.advance(); |
|
|
|
// Outdent |
|
if (this.indentStack.length && indents < this.indentStack[0]) { |
|
while (this.indentStack.length && this.indentStack[0] > indents) { |
|
this.stash.push(new Token('outdent')); |
|
this.indentStack.shift(); |
|
} |
|
tok = this.stash.pop(); |
|
// Indent |
|
} else if (indents && indents != this.indentStack[0]) { |
|
this.indentStack.unshift(indents); |
|
tok = new Token('indent'); |
|
// Newline |
|
} else { |
|
tok = new Token('newline'); |
|
} |
|
|
|
return tok; |
|
} |
|
}, |
|
|
|
/** |
|
* '-'? (digit+ | digit* '.' digit+) unit |
|
*/ |
|
|
|
unit: function() { |
|
var captures; |
|
if (captures = /^(-)?(\d+\.\d+|\d+|\.\d+)(%|[a-zA-Z]+)?[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var n = parseFloat(captures[2]); |
|
if ('-' == captures[1]) n = -n; |
|
var node = new nodes.Unit(n, captures[3]); |
|
node.raw = captures[0]; |
|
return new Token('unit', node); |
|
} |
|
}, |
|
|
|
/** |
|
* '"' [^"]+ '"' | "'"" [^']+ "'" |
|
*/ |
|
|
|
string: function() { |
|
var captures; |
|
if (captures = /^("[^"]*"|'[^']*')[ \t]*/.exec(this.str)) { |
|
var str = captures[1] |
|
, quote = captures[0][0]; |
|
this.skip(captures); |
|
str = str.slice(1,-1).replace(/\\n/g, '\n'); |
|
return new Token('string', new nodes.String(str, quote)); |
|
} |
|
}, |
|
|
|
/** |
|
* #rrggbbaa | #rrggbb | #rgba | #rgb | #nn | #n |
|
*/ |
|
|
|
color: function() { |
|
return this.rrggbbaa() |
|
|| this.rrggbb() |
|
|| this.rgba() |
|
|| this.rgb() |
|
|| this.nn() |
|
|| this.n() |
|
}, |
|
|
|
/** |
|
* #n |
|
*/ |
|
|
|
n: function() { |
|
var captures; |
|
if (captures = /^#([a-fA-F0-9]{1})[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var n = parseInt(captures[1] + captures[1], 16) |
|
, color = new nodes.RGBA(n, n, n, 1); |
|
color.raw = captures[0]; |
|
return new Token('color', color); |
|
} |
|
}, |
|
|
|
/** |
|
* #nn |
|
*/ |
|
|
|
nn: function() { |
|
var captures; |
|
if (captures = /^#([a-fA-F0-9]{2})[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var n = parseInt(captures[1], 16) |
|
, color = new nodes.RGBA(n, n, n, 1); |
|
color.raw = captures[0]; |
|
return new Token('color', color); |
|
} |
|
}, |
|
|
|
/** |
|
* #rgb |
|
*/ |
|
|
|
rgb: function() { |
|
var captures; |
|
if (captures = /^#([a-fA-F0-9]{3})[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var rgb = captures[1] |
|
, r = parseInt(rgb[0] + rgb[0], 16) |
|
, g = parseInt(rgb[1] + rgb[1], 16) |
|
, b = parseInt(rgb[2] + rgb[2], 16) |
|
, color = new nodes.RGBA(r, g, b, 1); |
|
color.raw = captures[0]; |
|
return new Token('color', color); |
|
} |
|
}, |
|
|
|
/** |
|
* #rgba |
|
*/ |
|
|
|
rgba: function() { |
|
var captures; |
|
if (captures = /^#([a-fA-F0-9]{4})[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var rgb = captures[1] |
|
, r = parseInt(rgb[0] + rgb[0], 16) |
|
, g = parseInt(rgb[1] + rgb[1], 16) |
|
, b = parseInt(rgb[2] + rgb[2], 16) |
|
, a = parseInt(rgb[3] + rgb[3], 16) |
|
, color = new nodes.RGBA(r, g, b, a/255); |
|
color.raw = captures[0]; |
|
return new Token('color', color); |
|
} |
|
}, |
|
|
|
/** |
|
* #rrggbb |
|
*/ |
|
|
|
rrggbb: function() { |
|
var captures; |
|
if (captures = /^#([a-fA-F0-9]{6})[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var rgb = captures[1] |
|
, r = parseInt(rgb.substr(0, 2), 16) |
|
, g = parseInt(rgb.substr(2, 2), 16) |
|
, b = parseInt(rgb.substr(4, 2), 16) |
|
, color = new nodes.RGBA(r, g, b, 1); |
|
color.raw = captures[0]; |
|
return new Token('color', color); |
|
} |
|
}, |
|
|
|
/** |
|
* #rrggbbaa |
|
*/ |
|
|
|
rrggbbaa: function() { |
|
var captures; |
|
if (captures = /^#([a-fA-F0-9]{8})[ \t]*/.exec(this.str)) { |
|
this.skip(captures); |
|
var rgb = captures[1] |
|
, r = parseInt(rgb.substr(0, 2), 16) |
|
, g = parseInt(rgb.substr(2, 2), 16) |
|
, b = parseInt(rgb.substr(4, 2), 16) |
|
, a = parseInt(rgb.substr(6, 2), 16) |
|
, color = new nodes.RGBA(r, g, b, a/255); |
|
color.raw = captures[0]; |
|
return new Token('color', color); |
|
} |
|
}, |
|
|
|
/** |
|
* ^|[^\n,;]+ |
|
*/ |
|
|
|
selector: function() { |
|
var captures; |
|
if (captures = /^\^|.*?(?=\/\/(?![^\[]*\])|[,\n{])/.exec(this.str)) { |
|
var selector = captures[0]; |
|
this.skip(captures); |
|
return new Token('selector', selector); |
|
} |
|
} |
|
};
|
|
|