]> git.wh0rd.org - 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 });