Fix for Lighthouse bug: 328
[mootools/dkf.git] / Source / Slick / Slick.Parser.js
blob55167f0e90d80186449fe9a486cfad5ed206f593
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) 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 = {Slick: true, expressions: [], raw: expression, reverse: function(){
27                 return parse(this.raw, true);
28         }};
29         separatorIndex = -1;
30         while (expression != (expression = expression.replace(regexp, parser)));
31         parsed.length = parsed.expressions.length;
32         return currentCache[expression] = (reversed) ? reverse(parsed) : parsed;
35 var reverseCombinator = function(combinator){
36         if (combinator === '!') return ' ';
37         else if (combinator === ' ') return '!';
38         else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
39         else return '!' + combinator;
42 var reverse = function(expression){
43         var expressions = expression.expressions;
44         for (var i = 0; i < expressions.length; i++){
45                 var exp = expressions[i];
46                 var last = {parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator)};
48                 for (var j = 0; j < exp.length; j++){
49                         var cexp = exp[j];
50                         if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
51                         cexp.combinator = cexp.reverseCombinator;
52                         delete cexp.reverseCombinator;
53                 }
55                 exp.reverse().push(last);
56         }
57         return expression;
60 var escapeRegExp = function(string){// Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
61         return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&");
64 var regexp = new RegExp(
66 #!/usr/bin/env ruby
67 puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
68 __END__
69         "(?x)^(?:\
70           \\s* ( , ) \\s*               # Separator          \n\
71         | \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
72         |      ( \\s+ )                 # CombinatorChildren \n\
73         |      ( <unicode>+ | \\* )     # Tag                \n\
74         | \\#  ( <unicode>+       )     # ID                 \n\
75         | \\.  ( <unicode>+       )     # ClassName          \n\
76         |                               # Attribute          \n\
77         \\[  \
78                 \\s* (<unicode1>+)  (?:  \
79                         \\s* ([*^$!~|]?=)  (?:  \
80                                 \\s* (?:\
81                                         ([\"']?)(.*?)\\9 \
82                                 )\
83                         )  \
84                 )?  \\s*  \
85         \\](?!\\]) \n\
86         |   :+ ( <unicode>+ )(?:\
87         \\( (?:\
88                  ([\"']?)((?:\\([^\\)]+\\)|[^\\(\\)]*)+)\\12\
89         ) \\)\
90         )?\
91         )"
93         "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|:+(<unicode>+)(?:\\((?:([\"']?)((?:\\([^\\)]+\\)|[^\\(\\)]*)+)\\12)\\))?)"
94         .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']')
95         .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
96         .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])')
99 function parser(
100         rawMatch,
102         separator,
103         combinator,
104         combinatorChildren,
106         tagName,
107         id,
108         className,
110         attributeKey,
111         attributeOperator,
112         attributeQuote,
113         attributeValue,
115         pseudoClass,
116         pseudoQuote,
117         pseudoClassValue
119         if (separator || separatorIndex === -1){
120                 parsed.expressions[++separatorIndex] = [];
121                 combinatorIndex = -1;
122                 if (separator) return '';
123         }
125         if (combinator || combinatorChildren || combinatorIndex === -1){
126                 combinator = combinator || ' ';
127                 var currentSeparator = parsed.expressions[separatorIndex];
128                 if (reversed && currentSeparator[combinatorIndex])
129                         currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
130                 currentSeparator[++combinatorIndex] = {combinator: combinator, tag: '*'};
131         }
133         var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];
135         if (tagName){
136                 currentParsed.tag = tagName.replace(reUnescape, '');
138         } else if (id){
139                 currentParsed.id = id.replace(reUnescape, '');
141         } else if (className){
142                 className = className.replace(reUnescape, '');
144                 if (!currentParsed.classList) currentParsed.classList = [];
145                 if (!currentParsed.classes) currentParsed.classes = [];
146                 currentParsed.classList.push(className);
147                 currentParsed.classes.push({
148                         value: className,
149                         regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
150                 });
152         } else if (pseudoClass){
153                 pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;
155                 if (!currentParsed.pseudos) currentParsed.pseudos = [];
156                 currentParsed.pseudos.push({
157                         key: pseudoClass.replace(reUnescape, ''),
158                         value: pseudoClassValue
159                 });
161         } else if (attributeKey){
162                 attributeKey = attributeKey.replace(reUnescape, '');
163                 attributeValue = (attributeValue || '').replace(reUnescape, '');
165                 var test, regexp;
167                 switch (attributeOperator){
168                         case '^=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue)            ); break;
169                         case '$=' : regexp = new RegExp(            escapeRegExp(attributeValue) +'$'       ); break;
170                         case '~=' : regexp = new RegExp( '(^|\\s)'+ escapeRegExp(attributeValue) +'(\\s|$)' ); break;
171                         case '|=' : regexp = new RegExp(       '^'+ escapeRegExp(attributeValue) +'(-|$)'   ); break;
172                         case  '=' : test = function(value){
173                                 return attributeValue == value;
174                         }; break;
175                         case '*=' : test = function(value){
176                                 return value && value.indexOf(attributeValue) > -1;
177                         }; break;
178                         case '!=' : test = function(value){
179                                 return attributeValue != value;
180                         }; break;
181                         default   : test = function(value){
182                                 return !!value;
183                         };
184                 }
186                 if (!test) test = function(value){
187                         return value && regexp.test(value);
188                 };
190                 if (!currentParsed.attributes) currentParsed.attributes = [];
191                 currentParsed.attributes.push({
192                         key: attributeKey,
193                         operator: attributeOperator,
194                         value: attributeValue,
195                         test: test
196                 });
198         }
200         return '';
203 // Slick NS
205 var Slick = (this.Slick || {});
207 Slick.parse = function(expression){
208         return parse(expression);
211 Slick.escapeRegExp = escapeRegExp;
213 if (!this.Slick) this.Slick = Slick;
215 }).apply(/*<CommonJS>*/(typeof exports != 'undefined') ? exports : /*</CommonJS>*/this);