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