//.CommonJS var CSSOM = {}; ///CommonJS

/**

* @param {string} token
*/

CSSOM.parse = function parse(token) {

var i = 0;

/**
        "before-selector" or
        "selector" or
        "atRule" or
        "atBlock" or
        "conditionBlock" or
        "before-name" or
        "name" or
        "before-value" or
        "value"
*/
var state = "before-selector";

var index;
var buffer = "";
var valueParenthesisDepth = 0;

var SIGNIFICANT_WHITESPACE = {
        "selector": true,
        "value": true,
        "value-parenthesis": true,
        "atRule": true,
        "importRule-begin": true,
        "importRule": true,
        "atBlock": true,
        "conditionBlock": true,
        'documentRule-begin': true
};

var styleSheet = new CSSOM.CSSStyleSheet();

// @type CSSStyleSheet|CSSMediaRule|CSSSupportsRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
var currentScope = styleSheet;

// @type CSSMediaRule|CSSSupportsRule|CSSKeyframesRule|CSSDocumentRule
var parentRule;

var ancestorRules = [];
var hasAncestors = false;
var prevScope;

var name, priority="", styleRule, mediaRule, supportsRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule;

var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g;

var parseError = function(message) {
        var lines = token.substring(0, i).split('\n');
        var lineCount = lines.length;
        var charCount = lines.pop().length + 1;
        var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')');
        error.line = lineCount;
        /* jshint sub : true */
        error['char'] = charCount;
        error.styleSheet = styleSheet;
        throw error;
};

for (var character; (character = token.charAt(i)); i++) {

        switch (character) {

        case " ":
        case "\t":
        case "\r":
        case "\n":
        case "\f":
                if (SIGNIFICANT_WHITESPACE[state]) {
                        buffer += character;
                }
                break;

        // String
        case '"':
                index = i + 1;
                do {
                        index = token.indexOf('"', index) + 1;
                        if (!index) {
                                parseError('Unmatched "');
                        }
                } while (token[index - 2] === '\\');
                buffer += token.slice(i, index);
                i = index - 1;
                switch (state) {
                        case 'before-value':
                                state = 'value';
                                break;
                        case 'importRule-begin':
                                state = 'importRule';
                                break;
                }
                break;

        case "'":
                index = i + 1;
                do {
                        index = token.indexOf("'", index) + 1;
                        if (!index) {
                                parseError("Unmatched '");
                        }
                } while (token[index - 2] === '\\');
                buffer += token.slice(i, index);
                i = index - 1;
                switch (state) {
                        case 'before-value':
                                state = 'value';
                                break;
                        case 'importRule-begin':
                                state = 'importRule';
                                break;
                }
                break;

        // Comment
        case "/":
                if (token.charAt(i + 1) === "*") {
                        i += 2;
                        index = token.indexOf("*/", i);
                        if (index === -1) {
                                parseError("Missing */");
                        } else {
                                i = index + 1;
                        }
                } else {
                        buffer += character;
                }
                if (state === "importRule-begin") {
                        buffer += " ";
                        state = "importRule";
                }
                break;

        // At-rule
        case "@":
                if (token.indexOf("@-moz-document", i) === i) {
                        state = "documentRule-begin";
                        documentRule = new CSSOM.CSSDocumentRule();
                        documentRule.__starts = i;
                        i += "-moz-document".length;
                        buffer = "";
                        break;
                } else if (token.indexOf("@media", i) === i) {
                        state = "atBlock";
                        mediaRule = new CSSOM.CSSMediaRule();
                        mediaRule.__starts = i;
                        i += "media".length;
                        buffer = "";
                        break;
                } else if (token.indexOf("@supports", i) === i) {
                        state = "conditionBlock";
                        supportsRule = new CSSOM.CSSSupportsRule();
                        supportsRule.__starts = i;
                        i += "supports".length;
                        buffer = "";
                        break;
                } else if (token.indexOf("@host", i) === i) {
                        state = "hostRule-begin";
                        i += "host".length;
                        hostRule = new CSSOM.CSSHostRule();
                        hostRule.__starts = i;
                        buffer = "";
                        break;
                } else if (token.indexOf("@import", i) === i) {
                        state = "importRule-begin";
                        i += "import".length;
                        buffer += "@import";
                        break;
                } else if (token.indexOf("@font-face", i) === i) {
                        state = "fontFaceRule-begin";
                        i += "font-face".length;
                        fontFaceRule = new CSSOM.CSSFontFaceRule();
                        fontFaceRule.__starts = i;
                        buffer = "";
                        break;
                } else {
                        atKeyframesRegExp.lastIndex = i;
                        var matchKeyframes = atKeyframesRegExp.exec(token);
                        if (matchKeyframes && matchKeyframes.index === i) {
                                state = "keyframesRule-begin";
                                keyframesRule = new CSSOM.CSSKeyframesRule();
                                keyframesRule.__starts = i;
                                keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found
                                i += matchKeyframes[0].length - 1;
                                buffer = "";
                                break;
                        } else if (state === "selector") {
                                state = "atRule";
                        }
                }
                buffer += character;
                break;

        case "{":
                if (state === "selector" || state === "atRule") {
                        styleRule.selectorText = buffer.trim();
                        styleRule.style.__starts = i;
                        buffer = "";
                        state = "before-name";
                } else if (state === "atBlock") {
                        mediaRule.media.mediaText = buffer.trim();

                        if (parentRule) {
                                ancestorRules.push(parentRule);
                        }

                        currentScope = parentRule = mediaRule;
                        mediaRule.parentStyleSheet = styleSheet;
                        buffer = "";
                        state = "before-selector";
                } else if (state === "conditionBlock") {
                        supportsRule.conditionText = buffer.trim();

                        if (parentRule) {
                                ancestorRules.push(parentRule);
                        }

                        currentScope = parentRule = supportsRule;
                        supportsRule.parentStyleSheet = styleSheet;
                        buffer = "";
                        state = "before-selector";
                } else if (state === "hostRule-begin") {
                        if (parentRule) {
                                ancestorRules.push(parentRule);
                        }

                        currentScope = parentRule = hostRule;
                        hostRule.parentStyleSheet = styleSheet;
                        buffer = "";
                        state = "before-selector";
                } else if (state === "fontFaceRule-begin") {
                        if (parentRule) {
                                ancestorRules.push(parentRule);
                                fontFaceRule.parentRule = parentRule;
                        }
                        fontFaceRule.parentStyleSheet = styleSheet;
                        styleRule = fontFaceRule;
                        buffer = "";
                        state = "before-name";
                } else if (state === "keyframesRule-begin") {
                        keyframesRule.name = buffer.trim();
                        if (parentRule) {
                                ancestorRules.push(parentRule);
                                keyframesRule.parentRule = parentRule;
                        }
                        keyframesRule.parentStyleSheet = styleSheet;
                        currentScope = parentRule = keyframesRule;
                        buffer = "";
                        state = "keyframeRule-begin";
                } else if (state === "keyframeRule-begin") {
                        styleRule = new CSSOM.CSSKeyframeRule();
                        styleRule.keyText = buffer.trim();
                        styleRule.__starts = i;
                        buffer = "";
                        state = "before-name";
                } else if (state === "documentRule-begin") {
                        // FIXME: what if this '{' is in the url text of the match function?
                        documentRule.matcher.matcherText = buffer.trim();
                        if (parentRule) {
                                ancestorRules.push(parentRule);
                                documentRule.parentRule = parentRule;
                        }
                        currentScope = parentRule = documentRule;
                        documentRule.parentStyleSheet = styleSheet;
                        buffer = "";
                        state = "before-selector";
                }
                break;

        case ":":
                if (state === "name") {
                        name = buffer.trim();
                        buffer = "";
                        state = "before-value";
                } else {
                        buffer += character;
                }
                break;

        case "(":
                if (state === 'value') {
                        // ie css expression mode
                        if (buffer.trim() === 'expression') {
                                var info = (new CSSOM.CSSValueExpression(token, i)).parse();

                                if (info.error) {
                                        parseError(info.error);
                                } else {
                                        buffer += info.expression;
                                        i = info.idx;
                                }
                        } else {
                                state = 'value-parenthesis';
                                //always ensure this is reset to 1 on transition
                                //from value to value-parenthesis
                                valueParenthesisDepth = 1;
                                buffer += character;
                        }
                } else if (state === 'value-parenthesis') {
                        valueParenthesisDepth++;
                        buffer += character;
                } else {
                        buffer += character;
                }
                break;

        case ")":
                if (state === 'value-parenthesis') {
                        valueParenthesisDepth--;
                        if (valueParenthesisDepth === 0) state = 'value';
                }
                buffer += character;
                break;

        case "!":
                if (state === "value" && token.indexOf("!important", i) === i) {
                        priority = "important";
                        i += "important".length;
                } else {
                        buffer += character;
                }
                break;

        case ";":
                switch (state) {
                        case "value":
                                styleRule.style.setProperty(name, buffer.trim(), priority);
                                priority = "";
                                buffer = "";
                                state = "before-name";
                                break;
                        case "atRule":
                                buffer = "";
                                state = "before-selector";
                                break;
                        case "importRule":
                                importRule = new CSSOM.CSSImportRule();
                                importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet;
                                importRule.cssText = buffer + character;
                                styleSheet.cssRules.push(importRule);
                                buffer = "";
                                state = "before-selector";
                                break;
                        default:
                                buffer += character;
                                break;
                }
                break;

        case "}":
                switch (state) {
                        case "value":
                                styleRule.style.setProperty(name, buffer.trim(), priority);
                                priority = "";
                                /* falls through */
                        case "before-name":
                        case "name":
                                styleRule.__ends = i + 1;
                                if (parentRule) {
                                        styleRule.parentRule = parentRule;
                                }
                                styleRule.parentStyleSheet = styleSheet;
                                currentScope.cssRules.push(styleRule);
                                buffer = "";
                                if (currentScope.constructor === CSSOM.CSSKeyframesRule) {
                                        state = "keyframeRule-begin";
                                } else {
                                        state = "before-selector";
                                }
                                break;
                        case "keyframeRule-begin":
                        case "before-selector":
                        case "selector":
                                // End of media/supports/document rule.
                                if (!parentRule) {
                                        parseError("Unexpected }");
                                }

                                // Handle rules nested in @media or @supports
                                hasAncestors = ancestorRules.length > 0;

                                while (ancestorRules.length > 0) {
                                        parentRule = ancestorRules.pop();

                                        if (
                                                parentRule.constructor.name === "CSSMediaRule"
                                                || parentRule.constructor.name === "CSSSupportsRule"
                                        ) {
                                                prevScope = currentScope;
                                                currentScope = parentRule;
                                                currentScope.cssRules.push(prevScope);
                                                break;
                                        }

                                        if (ancestorRules.length === 0) {
                                                hasAncestors = false;
                                        }
                                }

                                if (!hasAncestors) {
                                        currentScope.__ends = i + 1;
                                        styleSheet.cssRules.push(currentScope);
                                        currentScope = styleSheet;
                                        parentRule = null;
                                }

                                buffer = "";
                                state = "before-selector";
                                break;
                }
                break;

        default:
                switch (state) {
                        case "before-selector":
                                state = "selector";
                                styleRule = new CSSOM.CSSStyleRule();
                                styleRule.__starts = i;
                                break;
                        case "before-name":
                                state = "name";
                                break;
                        case "before-value":
                                state = "value";
                                break;
                        case "importRule-begin":
                                state = "importRule";
                                break;
                }
                buffer += character;
                break;
        }
}

return styleSheet;

};

//.CommonJS exports.parse = CSSOM.parse; // The following modules cannot be included sooner due to the mutual dependency with parse.js CSSOM.CSSStyleSheet = require(“./CSSStyleSheet”).CSSStyleSheet; CSSOM.CSSStyleRule = require(“./CSSStyleRule”).CSSStyleRule; CSSOM.CSSImportRule = require(“./CSSImportRule”).CSSImportRule; CSSOM.CSSMediaRule = require(“./CSSMediaRule”).CSSMediaRule; CSSOM.CSSSupportsRule = require(“./CSSSupportsRule”).CSSSupportsRule; CSSOM.CSSFontFaceRule = require(“./CSSFontFaceRule”).CSSFontFaceRule; CSSOM.CSSHostRule = require(“./CSSHostRule”).CSSHostRule; CSSOM.CSSStyleDeclaration = require('./CSSStyleDeclaration').CSSStyleDeclaration; CSSOM.CSSKeyframeRule = require('./CSSKeyframeRule').CSSKeyframeRule; CSSOM.CSSKeyframesRule = require('./CSSKeyframesRule').CSSKeyframesRule; CSSOM.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression; CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule; ///CommonJS