text: /*
   * Copyright (c) 2012-2015 S-Core Co., Ltd.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *     http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  define(function (require, exports, module) {
  
  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 };
      if (!options.position) return positionNoop;
  
      return function(node){
        node.position = {
          start: start,
          end: { line: lineno, column: column },
          source: options.source
        };
  
        whitespace();
        return node;
      }
    }
  
    /**
     * Return `node`.
     */
  
    function positionNoop(node) {
      whitespace();
      return node;
    }
  
    /**
     * Error `msg`.
     */
  
    function error(msg) {
      var err = new Error(msg + ' near line ' + lineno + ':' + column);
      err.filename = options.source;
      err.line = lineno;
      err.column = column;
      err.source = css;
      throw err;
    }
  
    /**
     * Parse stylesheet.
     */
  
    function stylesheet() {
      return {
        type: 'stylesheet',
        stylesheet: {
          rules: rules()
        }
      };
    }
  
    /**
     * 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.charAt(0) != '}' && (node = atrule() || rule())) {
        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()) rules.push(c);
      return rules;
    }
  
    /**
     * Parse comment.
     */
  
    function comment() {
      var pos = position();
      if ('/' != css.charAt(0) || '*' != css.charAt(1)) return;
  
      var i = 2;
      while (null != css.charAt(i) && ('*' != css.charAt(i) || '/' != css.charAt(i + 1))) ++i;
      i += 2;
  
      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;
      return trim(m[0]).split(/\s*,\s*/);
    }
  
    /**
     * 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(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/);
      if (!val) return error('property missing value');
  
      var ret = pos({
        type: 'declaration',
        property: prop,
        value: trim(val[0])
      });
  
      // ;
      match(/^[;\s]*/);
  
      return ret;
    }
  
    /**
     * Parse declarations.
     */
  
    function declarations() {
      var decls = [];
  
      if (!open()) return error("missing '{'");
      comments(decls);
  
      // declarations
      var decl;
      while (decl = declaration()) {
        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 */);
  
      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 */);
  
      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 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 import
     */
  
    function atimport() {
      return _atrule('import');
    }
  
    /**
     * Parse charset
     */
  
    function atcharset() {
      return _atrule('charset');
    }
  
    /**
     * Parse namespace
     */
  
    function atnamespace() {
      return _atrule('namespace')
    }
  
    /**
     * Parse non-block at-rules
     */
  
    function _atrule(name) {
      var pos = position();
      var m = match(new RegExp('^@' + name + ' *([^;\\n]+);'));
      if (!m) return;
      var ret = { type: name };
      ret[name] = trim(m[1]);
      return pos(ret);
    }
  
    /**
     * Parse at rule.
     */
  
    function atrule() {
      if (css[0] != '@') return;
  
      return atkeyframes()
        || atmedia()
        || atsupports()
        || atimport()
        || atcharset()
        || atnamespace()
        || atdocument()
        || atpage()
        || athost();
    }
  
    /**
     * Parse rule.
     */
  
    function rule() {
      var pos = position();
      var sel = selector();
  
      if (!sel) return;
      comments();
  
      return pos({
        type: 'rule',
        selectors: sel,
        declarations: declarations()
      });
    }
  
    return stylesheet();
  };
  
  /**
   * Trim `str`.
   */
  
  function trim(str) {
    return str ? str.replace(/^\s+|\s+$/g, '') : '';
  }
  
  });
  