//.CommonJS var CSSOM = {

CSSValue: require('./CSSValue').CSSValue

}; ///CommonJS

/**

* @constructor
* @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx
*
*/

CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) {

this._token = token;
this._idx = idx;

};

CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue(); CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression;

/**

* parse css expression() value
*
* @return {Object}
*         - error:
*         or
*         - idx:
*         - expression:
*
* Example:
*
* .selector {
*              zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto');
* }
*/

CSSOM.CSSValueExpression.prototype.parse = function() {

var token = this._token,
                idx = this._idx;

var character = '',
                expression = '',
                error = '',
                info,
                paren = [];

for (; ; ++idx) {
        character = token.charAt(idx);

        // end of token
        if (character === '') {
                error = 'css expression error: unfinished expression!';
                break;
        }

        switch(character) {
                case '(':
                        paren.push(character);
                        expression += character;
                        break;

                case ')':
                        paren.pop(character);
                        expression += character;
                        break;

                case '/':
                        if ((info = this._parseJSComment(token, idx))) { // comment?
                                if (info.error) {
                                        error = 'css expression error: unfinished comment in expression!';
                                } else {
                                        idx = info.idx;
                                        // ignore the comment
                                }
                        } else if ((info = this._parseJSRexExp(token, idx))) { // regexp
                                idx = info.idx;
                                expression += info.text;
                        } else { // other
                                expression += character;
                        }
                        break;

                case "'":
                case '"':
                        info = this._parseJSString(token, idx, character);
                        if (info) { // string
                                idx = info.idx;
                                expression += info.text;
                        } else {
                                expression += character;
                        }
                        break;

                default:
                        expression += character;
                        break;
        }

        if (error) {
                break;
        }

        // end of expression
        if (paren.length === 0) {
                break;
        }
}

var ret;
if (error) {
        ret = {
                error: error
        };
} else {
        ret = {
                idx: idx,
                expression: expression
        };
}

return ret;

};

/**

*
* @return {Object|false}
*          - idx:
*          - text:
*          or
*          - error:
*          or
*          false
*
*/

CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) {

var nextChar = token.charAt(idx + 1),
                text;

if (nextChar === '/' || nextChar === '*') {
        var startIdx = idx,
                        endIdx,
                        commentEndChar;

        if (nextChar === '/') { // line comment
                commentEndChar = '\n';
        } else if (nextChar === '*') { // block comment
                commentEndChar = '*/';
        }

        endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1);
        if (endIdx !== -1) {
                endIdx = endIdx + commentEndChar.length - 1;
                text = token.substring(idx, endIdx + 1);
                return {
                        idx: endIdx,
                        text: text
                };
        } else {
                var error = 'css expression error: unfinished comment in expression!';
                return {
                        error: error
                };
        }
} else {
        return false;
}

};

/**

*
* @return {Object|false}
*                                      - idx:
*                                      - text:
*                                      or 
*                                      false
*
*/

CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) {

var endIdx = this._findMatchedIdx(token, idx, sep),
                text;

if (endIdx === -1) {
        return false;
} else {
        text = token.substring(idx, endIdx + sep.length);

        return {
                idx: endIdx,
                text: text
        };
}

};

/**

* parse regexp in css expression
*
* @return {Object|false}
*                              - idx:
*                              - regExp:
*                              or 
*                              false
*/

/*

all legal RegExp

/a/ (/a/)

/a/
12, /a/

!/a/

+/a/ -/a/

  • /a/

/ /a/ %/a/

/a/

!==/a/

/a/

!=/a/ >/a/ >=/a/ </a/ <=/a/

&/a/ |/a/ ^/a/ ~/a/ <</a/ >>/a/ >>>/a/

&&/a/ ||/a/ ?/a/

/a/

,/a/

delete /a/
                in /a/

instanceof /a/

                new /a/
typeof /a/
        void /a/

*/ CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) {

var before = token.substring(0, idx).replace(/\s+$/, ""),
                legalRegx = [
                        /^$/,
                        /\($/,
                        /\[$/,
                        /\!$/,
                        /\+$/,
                        /\-$/,
                        /\*$/,
                        /\/\s+/,
                        /\%$/,
                        /\=$/,
                        /\>$/,
                        /<$/,
                        /\&$/,
                        /\|$/,
                        /\^$/,
                        /\~$/,
                        /\?$/,
                        /\,$/,
                        /delete$/,
                        /in$/,
                        /instanceof$/,
                        /new$/,
                        /typeof$/,
                        /void$/
                ];

var isLegal = legalRegx.some(function(reg) {
        return reg.test(before);
});

if (!isLegal) {
        return false;
} else {
        var sep = '/';

        // same logic as string
        return this._parseJSString(token, idx, sep);
}

};

/**

*
* find next sep(same line) index in `token`
*
* @return {Number}
*
*/

CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {

var startIdx = idx,
                endIdx;

var NOT_FOUND = -1;

while(true) {
        endIdx = token.indexOf(sep, startIdx + 1);

        if (endIdx === -1) { // not found
                endIdx = NOT_FOUND;
                break;
        } else {
                var text = token.substring(idx + 1, endIdx),
                                matched = text.match(/\\+$/);
                if (!matched || matched[0] % 2 === 0) { // not escaped
                        break;
                } else {
                        startIdx = endIdx;
                }
        }
}

// boundary must be in the same line(js sting or regexp)
var nextNewLineIdx = token.indexOf('\n', idx + 1);
if (nextNewLineIdx < endIdx) {
        endIdx = NOT_FOUND;
}

return endIdx;

};

//.CommonJS exports.CSSValueExpression = CSSOM.CSSValueExpression; ///CommonJS