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.
		
		
		
		
			
				
					604 lines
				
				11 KiB
			
		
		
			
		
	
	
					604 lines
				
				11 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								// http://www.w3.org/TR/CSS21/grammar.html
							 | 
						||
| 
								 | 
							
								// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027
							 | 
						||
| 
								 | 
							
								var commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = function(css, options){
							 | 
						||
| 
								 | 
							
								  options = options || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Positional.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var lineno = 1;
							 | 
						||
| 
								 | 
							
								  var column = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Update lineno and column based on `str`.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function updatePosition(str) {
							 | 
						||
| 
								 | 
							
								    var lines = str.match(/\n/g);
							 | 
						||
| 
								 | 
							
								    if (lines) lineno += lines.length;
							 | 
						||
| 
								 | 
							
								    var i = str.lastIndexOf('\n');
							 | 
						||
| 
								 | 
							
								    column = ~i ? str.length - i : column + str.length;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Mark position and patch `node.position`.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function position() {
							 | 
						||
| 
								 | 
							
								    var start = { line: lineno, column: column };
							 | 
						||
| 
								 | 
							
								    return function(node){
							 | 
						||
| 
								 | 
							
								      node.position = new Position(start);
							 | 
						||
| 
								 | 
							
								      whitespace();
							 | 
						||
| 
								 | 
							
								      return node;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Store position information for a node
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function Position(start) {
							 | 
						||
| 
								 | 
							
								    this.start = start;
							 | 
						||
| 
								 | 
							
								    this.end = { line: lineno, column: column };
							 | 
						||
| 
								 | 
							
								    this.source = options.source;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Non-enumerable source string
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Position.prototype.content = css;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Error `msg`.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var errorsList = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function error(msg) {
							 | 
						||
| 
								 | 
							
								    var err = new Error(options.source + ':' + lineno + ':' + column + ': ' + msg);
							 | 
						||
| 
								 | 
							
								    err.reason = msg;
							 | 
						||
| 
								 | 
							
								    err.filename = options.source;
							 | 
						||
| 
								 | 
							
								    err.line = lineno;
							 | 
						||
| 
								 | 
							
								    err.column = column;
							 | 
						||
| 
								 | 
							
								    err.source = css;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options.silent) {
							 | 
						||
| 
								 | 
							
								      errorsList.push(err);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      throw err;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse stylesheet.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function stylesheet() {
							 | 
						||
| 
								 | 
							
								    var rulesList = rules();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      type: 'stylesheet',
							 | 
						||
| 
								 | 
							
								      stylesheet: {
							 | 
						||
| 
								 | 
							
								        source: options.source,
							 | 
						||
| 
								 | 
							
								        rules: rulesList,
							 | 
						||
| 
								 | 
							
								        parsingErrors: errorsList
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Opening brace.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function open() {
							 | 
						||
| 
								 | 
							
								    return match(/^{\s*/);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Closing brace.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function close() {
							 | 
						||
| 
								 | 
							
								    return match(/^}/);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse ruleset.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function rules() {
							 | 
						||
| 
								 | 
							
								    var node;
							 | 
						||
| 
								 | 
							
								    var rules = [];
							 | 
						||
| 
								 | 
							
								    whitespace();
							 | 
						||
| 
								 | 
							
								    comments(rules);
							 | 
						||
| 
								 | 
							
								    while (css.length && css.charAt(0) != '}' && (node = atrule() || rule())) {
							 | 
						||
| 
								 | 
							
								      if (node !== false) {
							 | 
						||
| 
								 | 
							
								        rules.push(node);
							 | 
						||
| 
								 | 
							
								        comments(rules);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return rules;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Match `re` and return captures.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function match(re) {
							 | 
						||
| 
								 | 
							
								    var m = re.exec(css);
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								    var str = m[0];
							 | 
						||
| 
								 | 
							
								    updatePosition(str);
							 | 
						||
| 
								 | 
							
								    css = css.slice(str.length);
							 | 
						||
| 
								 | 
							
								    return m;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse whitespace.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function whitespace() {
							 | 
						||
| 
								 | 
							
								    match(/^\s*/);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse comments;
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function comments(rules) {
							 | 
						||
| 
								 | 
							
								    var c;
							 | 
						||
| 
								 | 
							
								    rules = rules || [];
							 | 
						||
| 
								 | 
							
								    while (c = comment()) {
							 | 
						||
| 
								 | 
							
								      if (c !== false) {
							 | 
						||
| 
								 | 
							
								        rules.push(c);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return rules;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse comment.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function comment() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    if ('/' != css.charAt(0) || '*' != css.charAt(1)) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var i = 2;
							 | 
						||
| 
								 | 
							
								    while ("" != css.charAt(i) && ('*' != css.charAt(i) || '/' != css.charAt(i + 1))) ++i;
							 | 
						||
| 
								 | 
							
								    i += 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ("" === css.charAt(i-1)) {
							 | 
						||
| 
								 | 
							
								      return error('End of comment missing');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var str = css.slice(2, i - 2);
							 | 
						||
| 
								 | 
							
								    column += 2;
							 | 
						||
| 
								 | 
							
								    updatePosition(str);
							 | 
						||
| 
								 | 
							
								    css = css.slice(i);
							 | 
						||
| 
								 | 
							
								    column += 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'comment',
							 | 
						||
| 
								 | 
							
								      comment: str
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse selector.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function selector() {
							 | 
						||
| 
								 | 
							
								    var m = match(/^([^{]+)/);
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								    /* @fix Remove all comments from selectors
							 | 
						||
| 
								 | 
							
								     * http://ostermiller.org/findcomment.html */
							 | 
						||
| 
								 | 
							
								    return trim(m[0])
							 | 
						||
| 
								 | 
							
								      .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
							 | 
						||
| 
								 | 
							
								      .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function(m) {
							 | 
						||
| 
								 | 
							
								        return m.replace(/,/g, '\u200C');
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      .split(/\s*(?![^(]*\)),\s*/)
							 | 
						||
| 
								 | 
							
								      .map(function(s) {
							 | 
						||
| 
								 | 
							
								        return s.replace(/\u200C/g, ',');
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse declaration.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function declaration() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // prop
							 | 
						||
| 
								 | 
							
								    var prop = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);
							 | 
						||
| 
								 | 
							
								    if (!prop) return;
							 | 
						||
| 
								 | 
							
								    prop = trim(prop[0]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // :
							 | 
						||
| 
								 | 
							
								    if (!match(/^:\s*/)) return error("property missing ':'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // val
							 | 
						||
| 
								 | 
							
								    var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var ret = pos({
							 | 
						||
| 
								 | 
							
								      type: 'declaration',
							 | 
						||
| 
								 | 
							
								      property: prop.replace(commentre, ''),
							 | 
						||
| 
								 | 
							
								      value: val ? trim(val[0]).replace(commentre, '') : ''
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // ;
							 | 
						||
| 
								 | 
							
								    match(/^[;\s]*/);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return ret;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse declarations.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function declarations() {
							 | 
						||
| 
								 | 
							
								    var decls = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("missing '{'");
							 | 
						||
| 
								 | 
							
								    comments(decls);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // declarations
							 | 
						||
| 
								 | 
							
								    var decl;
							 | 
						||
| 
								 | 
							
								    while (decl = declaration()) {
							 | 
						||
| 
								 | 
							
								      if (decl !== false) {
							 | 
						||
| 
								 | 
							
								        decls.push(decl);
							 | 
						||
| 
								 | 
							
								        comments(decls);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("missing '}'");
							 | 
						||
| 
								 | 
							
								    return decls;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse keyframe.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function keyframe() {
							 | 
						||
| 
								 | 
							
								    var m;
							 | 
						||
| 
								 | 
							
								    var vals = [];
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/)) {
							 | 
						||
| 
								 | 
							
								      vals.push(m[1]);
							 | 
						||
| 
								 | 
							
								      match(/^,\s*/);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!vals.length) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'keyframe',
							 | 
						||
| 
								 | 
							
								      values: vals,
							 | 
						||
| 
								 | 
							
								      declarations: declarations()
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse keyframes.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atkeyframes() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@([-\w]+)?keyframes\s*/);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								    var vendor = m[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // identifier
							 | 
						||
| 
								 | 
							
								    var m = match(/^([-\w]+)\s*/);
							 | 
						||
| 
								 | 
							
								    if (!m) return error("@keyframes missing name");
							 | 
						||
| 
								 | 
							
								    var name = m[1];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@keyframes missing '{'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var frame;
							 | 
						||
| 
								 | 
							
								    var frames = comments();
							 | 
						||
| 
								 | 
							
								    while (frame = keyframe()) {
							 | 
						||
| 
								 | 
							
								      frames.push(frame);
							 | 
						||
| 
								 | 
							
								      frames = frames.concat(comments());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@keyframes missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'keyframes',
							 | 
						||
| 
								 | 
							
								      name: name,
							 | 
						||
| 
								 | 
							
								      vendor: vendor,
							 | 
						||
| 
								 | 
							
								      keyframes: frames
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse supports.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atsupports() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@supports *([^{]+)/);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								    var supports = trim(m[1]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@supports missing '{'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var style = comments().concat(rules());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@supports missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'supports',
							 | 
						||
| 
								 | 
							
								      supports: supports,
							 | 
						||
| 
								 | 
							
								      rules: style
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse host.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function athost() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@host\s*/);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@host missing '{'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var style = comments().concat(rules());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@host missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'host',
							 | 
						||
| 
								 | 
							
								      rules: style
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse media.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atmedia() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@media *([^{]+)/);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								    var media = trim(m[1]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@media missing '{'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var style = comments().concat(rules());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@media missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'media',
							 | 
						||
| 
								 | 
							
								      media: media,
							 | 
						||
| 
								 | 
							
								      rules: style
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse custom-media.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atcustommedia() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/);
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'custom-media',
							 | 
						||
| 
								 | 
							
								      name: trim(m[1]),
							 | 
						||
| 
								 | 
							
								      media: trim(m[2])
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse paged media.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atpage() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@page */);
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var sel = selector() || [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@page missing '{'");
							 | 
						||
| 
								 | 
							
								    var decls = comments();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // declarations
							 | 
						||
| 
								 | 
							
								    var decl;
							 | 
						||
| 
								 | 
							
								    while (decl = declaration()) {
							 | 
						||
| 
								 | 
							
								      decls.push(decl);
							 | 
						||
| 
								 | 
							
								      decls = decls.concat(comments());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@page missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'page',
							 | 
						||
| 
								 | 
							
								      selectors: sel,
							 | 
						||
| 
								 | 
							
								      declarations: decls
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse document.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atdocument() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@([-\w]+)?document *([^{]+)/);
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var vendor = trim(m[1]);
							 | 
						||
| 
								 | 
							
								    var doc = trim(m[2]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@document missing '{'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var style = comments().concat(rules());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@document missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'document',
							 | 
						||
| 
								 | 
							
								      document: doc,
							 | 
						||
| 
								 | 
							
								      vendor: vendor,
							 | 
						||
| 
								 | 
							
								      rules: style
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse font-face.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atfontface() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var m = match(/^@font-face\s*/);
							 | 
						||
| 
								 | 
							
								    if (!m) return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!open()) return error("@font-face missing '{'");
							 | 
						||
| 
								 | 
							
								    var decls = comments();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // declarations
							 | 
						||
| 
								 | 
							
								    var decl;
							 | 
						||
| 
								 | 
							
								    while (decl = declaration()) {
							 | 
						||
| 
								 | 
							
								      decls.push(decl);
							 | 
						||
| 
								 | 
							
								      decls = decls.concat(comments());
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!close()) return error("@font-face missing '}'");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'font-face',
							 | 
						||
| 
								 | 
							
								      declarations: decls
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse import
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var atimport = _compileAtrule('import');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse charset
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var atcharset = _compileAtrule('charset');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse namespace
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var atnamespace = _compileAtrule('namespace');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse non-block at-rules
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function _compileAtrule(name) {
							 | 
						||
| 
								 | 
							
								    var re = new RegExp('^@' + name + '\\s*([^;]+);');
							 | 
						||
| 
								 | 
							
								    return function() {
							 | 
						||
| 
								 | 
							
								      var pos = position();
							 | 
						||
| 
								 | 
							
								      var m = match(re);
							 | 
						||
| 
								 | 
							
								      if (!m) return;
							 | 
						||
| 
								 | 
							
								      var ret = { type: name };
							 | 
						||
| 
								 | 
							
								      ret[name] = m[1].trim();
							 | 
						||
| 
								 | 
							
								      return pos(ret);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse at rule.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function atrule() {
							 | 
						||
| 
								 | 
							
								    if (css[0] != '@') return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return atkeyframes()
							 | 
						||
| 
								 | 
							
								      || atmedia()
							 | 
						||
| 
								 | 
							
								      || atcustommedia()
							 | 
						||
| 
								 | 
							
								      || atsupports()
							 | 
						||
| 
								 | 
							
								      || atimport()
							 | 
						||
| 
								 | 
							
								      || atcharset()
							 | 
						||
| 
								 | 
							
								      || atnamespace()
							 | 
						||
| 
								 | 
							
								      || atdocument()
							 | 
						||
| 
								 | 
							
								      || atpage()
							 | 
						||
| 
								 | 
							
								      || athost()
							 | 
						||
| 
								 | 
							
								      || atfontface();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Parse rule.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function rule() {
							 | 
						||
| 
								 | 
							
								    var pos = position();
							 | 
						||
| 
								 | 
							
								    var sel = selector();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!sel) return error('selector missing');
							 | 
						||
| 
								 | 
							
								    comments();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return pos({
							 | 
						||
| 
								 | 
							
								      type: 'rule',
							 | 
						||
| 
								 | 
							
								      selectors: sel,
							 | 
						||
| 
								 | 
							
								      declarations: declarations()
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return addParent(stylesheet());
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Trim `str`.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function trim(str) {
							 | 
						||
| 
								 | 
							
								  return str ? str.replace(/^\s+|\s+$/g, '') : '';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Adds non-enumerable parent node reference to each node.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function addParent(obj, parent) {
							 | 
						||
| 
								 | 
							
								  var isNode = obj && typeof obj.type === 'string';
							 | 
						||
| 
								 | 
							
								  var childParent = isNode ? obj : parent;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (var k in obj) {
							 | 
						||
| 
								 | 
							
								    var value = obj[k];
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(value)) {
							 | 
						||
| 
								 | 
							
								      value.forEach(function(v) { addParent(v, childParent); });
							 | 
						||
| 
								 | 
							
								    } else if (value && typeof value === 'object') {
							 | 
						||
| 
								 | 
							
								      addParent(value, childParent);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (isNode) {
							 | 
						||
| 
								 | 
							
								    Object.defineProperty(obj, 'parent', {
							 | 
						||
| 
								 | 
							
								      configurable: true,
							 | 
						||
| 
								 | 
							
								      writable: true,
							 | 
						||
| 
								 | 
							
								      enumerable: false,
							 | 
						||
| 
								 | 
							
								      value: parent || null
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return obj;
							 | 
						||
| 
								 | 
							
								}
							 |