Skip to content

Commit 39d7ecf

Browse files
committed
Merge pull request NV#45 from myhere/gh-pages
Parse IE expressions.
2 parents 982da60 + cb43632 commit 39d7ecf

File tree

7 files changed

+470
-13
lines changed

7 files changed

+470
-13
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
/lib/index.js
22
/build/
33
/node_modules/
4+
5+
*.swp

lib/CSSValue.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//.CommonJS
2+
var CSSOM = {};
3+
///CommonJS
4+
5+
6+
/**
7+
* @constructor
8+
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
9+
*
10+
* TODO: add if needed
11+
*/
12+
CSSOM.CSSValue = function CSSValue() {
13+
};
14+
15+
CSSOM.CSSValue.prototype = {
16+
constructor: CSSOM.CSSValue,
17+
18+
// @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
19+
set cssText() {
20+
var name = this._getConstructorName();
21+
22+
throw new Exception('DOMException: property "cssText" of "' + name + '" is readonly!');
23+
},
24+
25+
get cssText() {
26+
var name = this._getConstructorName();
27+
28+
throw new Exception('getter "cssText" of "' + name + '" is not implemented!');
29+
},
30+
31+
_getConstructorName: function() {
32+
var s = this.constructor.toString(),
33+
c = s.match(/function\s([^\(]+)/),
34+
name = c[1];
35+
36+
return name;
37+
}
38+
};
39+
40+
41+
//.CommonJS
42+
exports.CSSValue = CSSOM.CSSValue;
43+
///CommonJS

lib/CSSValueExpression.js

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
//.CommonJS
2+
var CSSOM = {
3+
CSSValue: require('./CSSValue').CSSValue
4+
};
5+
///CommonJS
6+
7+
8+
/**
9+
* @constructor
10+
* @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx
11+
*
12+
*/
13+
CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) {
14+
this._token = token;
15+
this._idx = idx;
16+
};
17+
18+
CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue;
19+
CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression;
20+
21+
/**
22+
* parse css expression() value
23+
*
24+
* @return {Object}
25+
* - error:
26+
* or
27+
* - idx:
28+
* - expression:
29+
*
30+
* Example:
31+
*
32+
* .selector {
33+
* zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto');
34+
* }
35+
*/
36+
CSSOM.CSSValueExpression.prototype.parse = function() {
37+
var token = this._token,
38+
idx = this._idx;
39+
40+
var character = '',
41+
expression = '',
42+
error = '',
43+
info,
44+
paren = [];
45+
46+
47+
for (; ; ++idx) {
48+
character = token.charAt(idx);
49+
50+
// end of token
51+
if (character == '') {
52+
error = 'css expression error: unfinished expression!';
53+
break;
54+
}
55+
56+
switch(character) {
57+
case '(':
58+
paren.push(character);
59+
expression += character;
60+
break;
61+
62+
case ')':
63+
paren.pop(character);
64+
expression += character;
65+
break;
66+
67+
case '/':
68+
if (info = this._parseJSComment(token, idx)) { // comment?
69+
if (info.error) {
70+
error = 'css expression error: unfinished comment in expression!';
71+
} else {
72+
idx = info.idx;
73+
// ignore the comment
74+
}
75+
} else if (info = this._parseJSRexExp(token, idx)) { // regexp
76+
idx = info.idx;
77+
expression += info.text;
78+
} else { // other
79+
expression += character;
80+
}
81+
break;
82+
83+
case "'":
84+
case '"':
85+
info = this._parseJSString(token, idx, character);
86+
if (info) { // string
87+
idx = info.idx;
88+
expression += info.text;
89+
} else {
90+
expression += character;
91+
}
92+
break;
93+
94+
default:
95+
expression += character;
96+
break;
97+
}
98+
99+
if (error) {
100+
break;
101+
}
102+
103+
// end of expression
104+
if (paren.length == 0) {
105+
break;
106+
}
107+
}
108+
109+
var ret;
110+
if (error) {
111+
ret = {
112+
error: error
113+
}
114+
} else {
115+
ret = {
116+
idx: idx,
117+
expression: expression
118+
}
119+
}
120+
121+
return ret;
122+
};
123+
124+
125+
/**
126+
*
127+
* @return {Object|false}
128+
* - idx:
129+
* - text:
130+
* or
131+
* - error:
132+
* or
133+
* false
134+
*
135+
*/
136+
CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) {
137+
var nextChar = token.charAt(idx + 1),
138+
text;
139+
140+
if (nextChar == '/' || nextChar == '*') {
141+
var startIdx = idx,
142+
endIdx,
143+
commentEndChar;
144+
145+
if (nextChar == '/') { // line comment
146+
commentEndChar = '\n';
147+
} else if (nextChar == '*') { // block comment
148+
commentEndChar = '*/';
149+
}
150+
151+
endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1);
152+
if (endIdx !== -1) {
153+
endIdx = endIdx + commentEndChar.length - 1;
154+
text = token.substring(idx, endIdx + 1);
155+
return {
156+
idx: endIdx,
157+
text: text
158+
}
159+
} else {
160+
error = 'css expression error: unfinished comment in expression!';
161+
return {
162+
error: error
163+
}
164+
}
165+
} else {
166+
return false;
167+
}
168+
};
169+
170+
171+
/**
172+
*
173+
* @return {Object|false}
174+
* - idx:
175+
* - text:
176+
* or
177+
* false
178+
*
179+
*/
180+
CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) {
181+
var endIdx = this._findMatchedIdx(token, idx, sep),
182+
text;
183+
184+
if (endIdx === -1) {
185+
return false;
186+
} else {
187+
text = token.substring(idx, endIdx + sep.length);
188+
189+
return {
190+
idx: endIdx,
191+
text: text
192+
}
193+
}
194+
};
195+
196+
197+
/**
198+
* parse regexp in css expression
199+
*
200+
* @return {Object|false}
201+
* - idx:
202+
* - regExp:
203+
* or
204+
* false
205+
*/
206+
207+
/*
208+
209+
all legal RegExp
210+
211+
/a/
212+
(/a/)
213+
[/a/]
214+
[12, /a/]
215+
216+
!/a/
217+
218+
+/a/
219+
-/a/
220+
* /a/
221+
/ /a/
222+
%/a/
223+
224+
===/a/
225+
!==/a/
226+
==/a/
227+
!=/a/
228+
>/a/
229+
>=/a/
230+
</a/
231+
<=/a/
232+
233+
&/a/
234+
|/a/
235+
^/a/
236+
~/a/
237+
<</a/
238+
>>/a/
239+
>>>/a/
240+
241+
&&/a/
242+
||/a/
243+
?/a/
244+
=/a/
245+
,/a/
246+
247+
delete /a/
248+
in /a/
249+
instanceof /a/
250+
new /a/
251+
typeof /a/
252+
void /a/
253+
254+
*/
255+
CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) {
256+
var before = token.substring(0, idx).trimRight(),
257+
legalRegx = [
258+
/^$/,
259+
/\($/,
260+
/\[$/,
261+
/\!$/,
262+
/\+$/,
263+
/\-$/,
264+
/\*$/,
265+
/\/\s+/,
266+
/\%$/,
267+
/\=$/,
268+
/\>$/,
269+
/\<$/,
270+
/\&$/,
271+
/\|$/,
272+
/\^$/,
273+
/\~$/,
274+
/\?$/,
275+
/\,$/,
276+
/delete$/,
277+
/in$/,
278+
/instanceof$/,
279+
/new$/,
280+
/typeof$/,
281+
/void$/,
282+
];
283+
284+
var isLegal = legalRegx.some(function(reg) {
285+
return reg.test(before);
286+
});
287+
288+
if (!isLegal) {
289+
return false;
290+
} else {
291+
var sep = '/';
292+
293+
// same logic as string
294+
return this._parseJSString(token, idx, sep);
295+
}
296+
};
297+
298+
299+
/**
300+
*
301+
* find next sep(same line) index in `token`
302+
*
303+
* @return {Number}
304+
*
305+
*/
306+
CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
307+
var startIdx = idx,
308+
endIdx;
309+
310+
var NOT_FOUND = -1;
311+
312+
while(true) {
313+
endIdx = token.indexOf(sep, startIdx + 1);
314+
315+
if (endIdx === -1) { // not found
316+
endIdx = NOT_FOUND;
317+
break;
318+
} else {
319+
var text = token.substring(idx + 1, endIdx),
320+
matched = text.match(/\\+$/);
321+
if (!matched || matched[0] % 2 == 0) { // not escaped
322+
break;
323+
} else {
324+
startIdx = endIdx;
325+
}
326+
}
327+
}
328+
329+
// boundary must be in the same line(js sting or regexp)
330+
var nextNewLineIdx = token.indexOf('\n', idx + 1);
331+
if (nextNewLineIdx < endIdx) {
332+
endIdx = NOT_FOUND;
333+
}
334+
335+
336+
return endIdx;
337+
}
338+
339+
340+
341+
342+
//.CommonJS
343+
exports.CSSValueExpression = CSSOM.CSSValueExpression;
344+
///CommonJS

0 commit comments

Comments
 (0)