123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- /*!
- * Stylus - Selector Parser
- * Copyright (c) Automattic <developer.wordpress.com>
- * MIT Licensed
- */
- var COMBINATORS = ['>', '+', '~'];
- /**
- * 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;
- };
- /**
- * 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;
- };
- /**
- * Consume spaces.
- */
- SelectorParser.prototype.skipSpaces = function() {
- while (' ' == this.str[0]) this.skip(1);
- };
- /**
- * 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();
- };
- /**
- * '/'
- */
- SelectorParser.prototype.root = function() {
- 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;
- }
- }
- }
- };
- /**
- * '~/'
- */
- SelectorParser.prototype.initial = function() {
- 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;
- }
- }
- };
- /**
- * '&'
- */
- SelectorParser.prototype.parent = function() {
- 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;
- }
- }
- this.skip(1);
- if (!this.raw)
- return this.stack[this.stack.length - 1];
- }
- };
- /**
- * '^[' 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;
- }
- }
- };
- /**
- * '-'? 0-9+
- */
- SelectorParser.prototype.number = function() {
- 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++];
- if (ret) {
- this.skip(i);
- return Number(ret);
- }
- };
- /**
- * number ('..' number)?
- */
- SelectorParser.prototype.range = function() {
- var start = this.number()
- , ret;
- 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 > 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 (ret) {
- return ret;
- } else {
- this.ignore = true;
- }
- };
- /**
- * .+
- */
- SelectorParser.prototype.char = function() {
- var char = this.str[0];
- this.skip(1);
- return char;
- };
- /**
- * 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;
- }
- }
- return { val: val.trimRight(), nested: this.nested };
- };
|