borderRadius getter, specs and consistency
[mootools.git] / Source / Slick / Slick.Parser.js
blobe9e78f7a34265fd108272b341cf1c7e41ec804fc
1 /*
2 ---
3 name: Slick.Parser
4 description: Standalone CSS3 Selector parser
5 provides: Slick.Parser
6 ...
7 */
9 ;(function(){
11 var parsed,
12         separatorIndex,
13         combinatorIndex,
14         reversed,
15         cache = {},
16         reverseCache = {},
17         reUnescape = /\\/g;
19 var parse = function(expression, isReversed){
20         if (expression == null) return null;
21         if (expression.Slick === true) return expression;
22         expression = ('' + expression).replace(/^\s+|\s+$/g, '');
23         reversed = !!isReversed;
24         var currentCache = (reversed) ? reverseCache : cache;
25         if (currentCache[expression]) return currentCache[expression];
26         parsed = {
27                 Slick: true,
28                 expressions: [],
29                 raw: expression,
30                 reverse: function(){
31                         return parse(this.raw, true);
32                 }
33         };
34         separatorIndex = -1;
35         while (expression != (expression = expression.replace(regexp, parser)));
36         parsed.length = parsed.expressions.length;
37         return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
40 var reverseCombinator = function(combinator){
41         if (combinator === '!') return ' ';
42         else if (combinator === ' ') return '!';
43         else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
44         else return '!' + combinator;
47 var reverse = function(expression){
48         var expressions = expression.expressions;
49         for (var i = 0; i < expressions.length; i++){
50                 var exp = expressions[i];
51                 var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
53                 for (var j = 0; j < exp.length; j++){
54                         var cexp = exp[j];
55                         if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
56                         cexp.combinator = cexp.reverseCombinator;
57                         delete cexp.reverseCombinator;
58                 }
60                 exp.reverse().push(last);
61         }
62         return expression;
65 var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
66         return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match){
67                 return '\\' + match;
68         });
71 var regexp = new RegExp(
73 #!/usr/bin/env ruby
74 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
75 __END__
76         "(?x)^(?:\
77           \\s* ( , ) \\s*               # Separator          \n\
78         | \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
79         |      ( \\s+ )                 # CombinatorChildren \n\
80         |      ( <unicode>+ | \\* )     # Tag                \n\
81         | \\#  ( <unicode>+       )     # ID                 \n\
82         | \\.  ( <unicode>+       )     # ClassName          \n\
83         |                               # Attribute          \n\
84         \\[  \
85                 \\s* (<unicode1>+)  (?:  \
86                         \\s* ([*^$!~|]?=)  (?:  \
87                                 \\s* (?:\
88                                         ([\"']?)(.*?)\\9 \
89                                 )\
90                         )  \
91                 )?  \\s*  \
92         \\](?!\\]) \n\
93         |   :+ ( <unicode>+ )(?:\
94         \\( (?:\
95                 (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
96         ) \\)\
97         )?\
98         )"
100         "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)"
101         .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
102         .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
103         .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
106 function parser(
107         rawMatch,
109         separator,
110         combinator,
111         combinatorChildren,
113         tagName,
114         id,
115         className,
117         attributeKey,
118         attributeOperator,
119         attributeQuote,
120         attributeValue,
122         pseudoMarker,
123         pseudoClass,
124         pseudoQuote,
125         pseudoClassQuotedValue,
126         pseudoClassValue
128         if (separator || separatorIndex === -1){
129                 parsed.expressions[++separatorIndex] = [];
130                 combinatorIndex = -1;
131                 if (separator) return '';
132         }
134         if (combinator || combinatorChildren || combinatorIndex === -1){
135                 combinator = combinator || ' ';
136                 var currentSeparator = parsed.expressions[separatorIndex];
137                 if (reversed && currentSeparator[combinatorIndex])
138                         currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
139                 currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
140         }
142         var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
144         if (tagName){
145                 currentParsed.tag = tagName.replace(reUnescape, '');
147         } else if (id){
148                 currentParsed.id = id.replace(reUnescape, '');
150         } else if (className){
151                 className = className.replace(reUnescape, '');
153                 if (!currentParsed.classList) currentParsed.classList = [];
154                 if (!currentParsed.classes) currentParsed.classes = [];
155                 currentParsed.classList.push(className);
156                 currentParsed.classes.push({
157                         value: className,
158                         regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
159                 });
161         } else if (pseudoClass){
162                 pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
163                 pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
165                 if (!currentParsed.pseudos) currentParsed.pseudos = [];
166                 currentParsed.pseudos.push({
167                         key: pseudoClass.replace(reUnescape, ''),
168                         value: pseudoClassValue,
169                         type: pseudoMarker.length == 1 ? 'class' : 'element'
170                 });
172         } else if (attributeKey){
173                 attributeKey = attributeKey.replace(reUnescape, '');
174                 attributeValue = (attributeValue || '').replace(reUnescape, '');
176                 var test, regexp;
178                 switch (attributeOperator){
179                         case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
180                         case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
181                         case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
182                         case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
183                         case  '=' : test = function(value){
184                                 return attributeValue == value;
185                         }; break;
186                         case '*=' : test = function(value){
187                                 return value && value.indexOf(attributeValue) > -1;
188                         }; break;
189                         case '!=' : test = function(value){
190                                 return attributeValue != value;
191                         }; break;
192                         default   : test = function(value){
193                                 return !!value;
194                         };
195                 }
197                 if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function(){
198                         return false;
199                 };
201                 if (!test) test = function(value){
202                         return value && regexp.test(value);
203                 };
205                 if (!currentParsed.attributes) currentParsed.attributes = [];
206                 currentParsed.attributes.push({
207                         key: attributeKey,
208                         operator: attributeOperator,
209                         value: attributeValue,
210                         test: test
211                 });
213         }
215         return '';
218 // Slick NS
220 var Slick = (this.Slick || {});
222 Slick.parse = function(expression){
223         return parse(expression);
226 Slick.escapeRegExp = escapeRegExp;
228 if (!this.Slick) this.Slick = Slick;
230 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);