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