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