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.
344 lines
5.2 KiB
344 lines
5.2 KiB
//.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
|
|
|