//.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
-