]> git.wh0rd.org Git - tt-rss.git/blob - lib/dojo/selector/lite.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dojo / selector / lite.js.uncompressed.js
1 define("dojo/selector/lite", ["../has", "../_base/kernel"], function(has, dojo){
2 "use strict";
3 // summary:
4 //              A small lightweight query selector engine that implements CSS2.1 selectors 
5 //              minus pseudo-classes and the sibling combinator, plus CSS3 attribute selectors
6 var testDiv = document.createElement("div");
7 var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector; // IE9, WebKit, Firefox have this, but not Opera yet
8 var querySelectorAll = testDiv.querySelectorAll;
9 has.add("dom-matches-selector", !!matchesSelector);
10 has.add("dom-qsa", !!querySelectorAll); 
11
12 // this is a simple query engine. It has handles basic selectors, and for simple
13 // common selectors is extremely fast
14 var liteEngine = function(selector, root){
15         if(combine && selector.indexOf(',') > -1){
16                 return combine(selector, root);
17         }
18         var match = (querySelectorAll ? 
19                 /^([\w]*)#([\w\-]+$)|^(\.)([\w\-\*]+$)|^(\w+$)/ : // this one only matches on simple queries where we can beat qSA with specific methods
20                 /^([\w]*)#([\w\-]+)(?:\s+(.*))?$|(?:^|(>|.+\s+))([\w\-\*]+)(\S*$)/) // this one matches parts of the query that we can use to speed up manual filtering
21                         .exec(selector);
22         root = root || document;
23         if(match){
24                 // fast path regardless of whether or not querySelectorAll exists
25                 if(match[2]){
26                         // an #id
27                         // use dojo.byId if available as it fixes the id retrieval in IE
28                         var found = dojo.byId ? dojo.byId(match[2]) : document.getElementById(match[2]);
29                         if(!found || (match[1] && match[1] != found.tagName.toLowerCase())){
30                                 // if there is a tag qualifer and it doesn't match, no matches
31                                 return [];
32                         }
33                         if(root != document){
34                                 // there is a root element, make sure we are a child of it
35                                 var parent = found;
36                                 while(parent != root){
37                                         parent = parent.parentNode;
38                                         if(!parent){
39                                                 return [];
40                                         }
41                                 }
42                         }
43                         return match[3] ?
44                                         liteEngine(match[3], found) 
45                                         : [found];
46                 }
47                 if(match[3] && root.getElementsByClassName){
48                         // a .class
49                         return root.getElementsByClassName(match[4]);
50                 }
51                 var found;
52                 if(match[5]){
53                         // a tag
54                         found = root.getElementsByTagName(match[5]);
55                         if(match[4] || match[6]){
56                                 selector = (match[4] || "") + match[6];
57                         }else{
58                                 // that was the entirety of the query, return results
59                                 return found;
60                         }
61                 }
62         }
63         if(querySelectorAll){
64                 // qSA works strangely on Element-rooted queries
65                 // We can work around this by specifying an extra ID on the root
66                 // and working up from there (Thanks to Andrew Dupont for the technique)
67                 // IE 8 doesn't work on object elements
68                 if (root.nodeType === 1 && root.nodeName.toLowerCase() !== "object"){                           
69                         return useRoot(root, selector, root.querySelectorAll);
70                 }else{
71                         // we can use the native qSA
72                         return root.querySelectorAll(selector);
73                 }
74         }else if(!found){
75                 // search all children and then filter
76                 found = root.getElementsByTagName("*");
77         }
78         // now we filter the nodes that were found using the matchesSelector
79         var results = [];
80         for(var i = 0, l = found.length; i < l; i++){
81                 var node = found[i];
82                 if(node.nodeType == 1 && jsMatchesSelector(node, selector, root)){
83                         // keep the nodes that match the selector
84                         results.push(node);
85                 }
86         }
87         return results;
88 };
89 var useRoot = function(context, query, method){
90         // this function creates a temporary id so we can do rooted qSA queries, this is taken from sizzle
91         var oldContext = context,
92                 old = context.getAttribute( "id" ),
93                 nid = old || "__dojo__",
94                 hasParent = context.parentNode,
95                 relativeHierarchySelector = /^\s*[+~]/.test( query );
96
97         if(relativeHierarchySelector && !hasParent){
98                 return [];
99         }
100         if(!old){
101                 context.setAttribute("id", nid);
102         }else{
103                 nid = nid.replace(/'/g, "\\$&");
104         }
105         if(relativeHierarchySelector && hasParent){
106                 context = context.parentNode;
107         }
108
109         try {
110                 return method.call(context, "[id='" + nid + "'] " + query );
111         } finally {
112                 if ( !old ) {
113                         oldContext.removeAttribute( "id" );
114                 }
115         }
116 };
117
118 if(!has("dom-matches-selector")){
119         var jsMatchesSelector = (function(){
120                 // a JS implementation of CSS selector matching, first we start with the various handlers
121                 var caseFix = testDiv.tagName == "div" ? "toLowerCase" : "toUpperCase";
122                 function tag(tagName){
123                         tagName = tagName[caseFix]();
124                         return function(node){
125                                 return node.tagName == tagName;
126                         }
127                 }
128                 function className(className){
129                         var classNameSpaced = ' ' + className + ' ';
130                         return function(node){
131                                 return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
132                         }
133                 }
134                 var attrComparators = {
135                         "^=": function(attrValue, value){
136                                 return attrValue.indexOf(value) == 0;
137                         },
138                         "*=": function(attrValue, value){
139                                 return attrValue.indexOf(value) > -1;
140                         },
141                         "$=": function(attrValue, value){
142                                 return attrValue.substring(attrValue.length - value.length, attrValue.length) == value;
143                         },
144                         "~=": function(attrValue, value){
145                                 return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
146                         },
147                         "|=": function(attrValue, value){
148                                 return (attrValue + '-').indexOf(value + '-') == 0;
149                         },
150                         "=": function(attrValue, value){
151                                 return attrValue == value;
152                         },
153                         "": function(attrValue, value){
154                                 return true;
155                         }
156                 };
157                 function attr(name, value, type){
158                         if(value.match(/['"]/)){
159                                 // it is quoted, do an eval to parse the string (CSS and JS parsing are close enough)
160                                 value = eval(value);
161                         }
162                         var comparator = attrComparators[type || ""];
163                         return function(node){
164                                 var attrValue = node.getAttribute(name);
165                                 return attrValue && comparator(attrValue, value);
166                         }
167                 }
168                 function ancestor(matcher){
169                         return function(node, root){
170                                 while((node = node.parentNode) != root){
171                                         if(matcher(node, root)){
172                                                 return true;
173                                         }
174                                 }
175                         };
176                 }
177                 function parent(matcher){
178                         return function(node, root){
179                                 node = node.parentNode;
180                                 return matcher ? 
181                                         node != root && matcher(node, root)
182                                         : node == root;
183                         };
184                 }
185                 var cache = {};
186                 function and(matcher, next){
187                         return matcher ?
188                                 function(node, root){
189                                         return next(node) && matcher(node, root);
190                                 }
191                                 : next;
192                 }
193                 return function(node, selector, root){
194                         // this returns true or false based on if the node matches the selector (optionally within the given root)
195                         var matcher = cache[selector]; // check to see if we have created a matcher function for the given selector
196                         if(!matcher){
197                                 // create a matcher function for the given selector
198                                 // parse the selectors
199                                 if(selector.replace(/(?:\s*([> ])\s*)|(\.)?([\w-]+)|\[([\w-]+)\s*(.?=)?\s*([^\]]*)\]/g, function(t, combinator, type, value, attrName, attrType, attrValue){
200                                         if(value){
201                                                 if(type == "."){
202                                                         matcher = and(matcher, className(value));
203                                                 }
204                                                 else{
205                                                         matcher = and(matcher, tag(value));
206                                                 }
207                                         }
208                                         else if(combinator){
209                                                 matcher = (combinator == " " ? ancestor : parent)(matcher);
210                                         }
211                                         else if(attrName){
212                                                 matcher = and(matcher, attr(attrName, attrValue, attrType));
213                                         }
214                                         return "";
215                                 })){
216                                         throw new Error("Syntax error in query");
217                                 }
218                                 if(!matcher){
219                                         return true;
220                                 }
221                                 cache[selector] = matcher;
222                         }
223                         // now run the matcher function on the node
224                         return matcher(node, root);
225                 };
226         })();
227 }
228 if(!has("dom-qsa")){
229         var combine = function(selector, root){
230                 // combined queries
231                 selector = selector.split(/\s*,\s*/);
232                 var indexed = [];
233                 // add all results and keep unique ones, this only runs in IE, so we take advantage 
234                 // of known IE features, particularly sourceIndex which is unique and allows us to 
235                 // order the results 
236                 for(var i = 0; i < selector.length; i++){
237                         var results = liteEngine(selector[i], root);
238                         for(var j = 0, l = results.length; j < l; j++){
239                                 var node = results[j];
240                                 indexed[node.sourceIndex] = node;
241                         }
242                 }
243                 // now convert from a sparse array to a dense array
244                 var totalResults = [];
245                 for(i in indexed){
246                         totalResults.push(indexed[i]);
247                 }
248                 return totalResults;
249         };
250 }
251
252 liteEngine.match = matchesSelector ? function(node, selector, root){
253         if(root){
254                 // doesn't support three args, use rooted id trick
255                 return useRoot(root, selector, function(query){
256                         return matchesSelector.call(node, query);
257                 });
258         }
259         // we have a native matchesSelector, use that
260         return matchesSelector.call(node, selector);
261 } : jsMatchesSelector; // otherwise use the JS matches impl
262
263 return liteEngine;
264 });