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