1 define("dojo/selector/lite", ["../has", "../_base/kernel"], function(has
, dojo
){
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
);
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
){
15 // A small lightweight query selector engine that implements CSS2.1 selectors
16 // minus pseudo-classes and the sibling combinator, plus CSS3 attribute selectors
18 if(combine
&& selector
.indexOf(',') > -1){
19 return combine(selector
, root
);
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
32 // fast path regardless of whether or not querySelectorAll exists
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
42 // there is a root element, make sure we are a child of it
44 while(parent
!= root
){
45 parent
= parent
.parentNode
;
52 liteEngine(match
[3], found
)
55 if(match
[3] && root
.getElementsByClassName
){
57 return root
.getElementsByClassName(match
[4]);
62 found
= root
.getElementsByTagName(match
[5]);
63 if(match
[4] || match
[6]){
64 selector
= (match
[4] || "") + match
[6];
66 // that was the entirety of the query, return results
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
);
79 // we can use the native qSA
80 return root
.querySelectorAll(selector
);
83 // search all children and then filter
84 found
= root
.getElementsByTagName("*");
86 // now we filter the nodes that were found using the matchesSelector
88 for(var i
= 0, l
= found
.length
; i
< l
; i
++){
90 if(node
.nodeType
== 1 && jsMatchesSelector(node
, selector
, root
)){
91 // keep the nodes that match the selector
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
);
105 if(relativeHierarchySelector
&& !hasParent
){
109 context
.setAttribute("id", nid
);
111 nid
= nid
.replace(/'/g, "\\$&");
113 if(relativeHierarchySelector && hasParent){
114 context = context.parentNode;
116 var selectors = query.match(unionSplit);
117 for(var i = 0; i < selectors.length; i++){
118 selectors[i] = "[id='" + nid + "'] " + selectors[i];
120 query = selectors.join(",");
123 return method.call(context, query);
126 oldContext.removeAttribute("id");
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;
142 ".": function(className){
143 var classNameSpaced = ' ' + className + ' ';
144 return function(node){
145 return node.className.indexOf(className) > -1 && (' ' + node.className + ' ').indexOf(classNameSpaced) > -1;
149 return function(node){
150 return node.id == id;
154 var attrComparators = {
155 "^=": function(attrValue, value){
156 return attrValue.indexOf(value) == 0;
158 "*=": function(attrValue, value){
159 return attrValue.indexOf(value) > -1;
161 "$=": function(attrValue, value){
162 return attrValue.substring(attrValue.length - value.length, attrValue.length) == value;
164 "~=": function(attrValue, value){
165 return (' ' + attrValue + ' ').indexOf(' ' + value + ' ') > -1;
167 "|=": function(attrValue, value){
168 return (attrValue + '-').indexOf(value + '-') == 0;
170 "=": function(attrValue, value){
171 return attrValue == value;
173 "": function(attrValue, value){
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);
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);
190 function ancestor(matcher){
191 return function(node, root){
192 while((node = node.parentNode) != root){
193 if(matcher(node, root)){
199 function parent(matcher){
200 return function(node, root){
201 node = node.parentNode;
203 node != root && matcher(node, root)
208 function and(matcher, next){
210 function(node, root){
211 return next(node) && matcher(node, root);
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
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
){
223 matcher
= and(matcher
, selectorTypes
[type
|| ""](value
.replace(/\\/g
, '')));
226 matcher
= (combinator
== " " ? ancestor
: parent
)(matcher
);
229 matcher
= and(matcher
, attr(attrName
, attrValue
, attrType
));
233 throw new Error("Syntax error in query");
238 cache
[selector
] = matcher
;
240 // now run the matcher function on the node
241 return matcher(node
, root
);
246 var combine = function(selector
, root
){
248 var selectors
= selector
.match(unionSplit
);
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
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
;
262 // now convert from a sparse array to a dense array
263 var totalResults
= [];
265 totalResults
.push(indexed
[i
]);
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
);
278 // we have a native matchesSelector, use that
279 return matchesSelector
.call(node
, selector
);
280 } : jsMatchesSelector
; // otherwise use the JS matches impl