UNPKG

5.37 kBJavaScriptView Raw
1//.CommonJS
2var 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 */
13CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) {
14 this._token = token;
15 this._idx = idx;
16};
17
18CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue();
19CSSOM.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 */
36CSSOM.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 */
136CSSOM.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 var 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 */
180CSSOM.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
209all 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/
249instanceof /a/
250 new /a/
251 typeof /a/
252 void /a/
253
254*/
255CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) {
256 var before = token.substring(0, idx).replace(/\s+$/, ""),
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 */
306CSSOM.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
343exports.CSSValueExpression = CSSOM.CSSValueExpression;
344///CommonJS
345
\No newline at end of file