Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / DomQuery.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js">/*
19  * This is code is also distributed under MIT license for use
20  * with jQuery and prototype JavaScript libraries.
21  */
22 <span id='Ext-DomQuery'>/**
23 </span> * @class Ext.DomQuery
24 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
25 &lt;p&gt;
26 DomQuery supports most of the &lt;a href=&quot;http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors&quot;&gt;CSS3 selectors spec&lt;/a&gt;, along with some custom selectors and basic XPath.&lt;/p&gt;
27
28 &lt;p&gt;
29 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example &quot;div.foo:nth-child(odd)[@foo=bar].bar:first&quot; would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
30 &lt;/p&gt;
31 &lt;h4&gt;Element Selectors:&lt;/h4&gt;
32 &lt;ul class=&quot;list&quot;&gt;
33     &lt;li&gt; &lt;b&gt;*&lt;/b&gt; any element&lt;/li&gt;
34     &lt;li&gt; &lt;b&gt;E&lt;/b&gt; an element with the tag E&lt;/li&gt;
35     &lt;li&gt; &lt;b&gt;E F&lt;/b&gt; All descendent elements of E that have the tag F&lt;/li&gt;
36     &lt;li&gt; &lt;b&gt;E &gt; F&lt;/b&gt; or &lt;b&gt;E/F&lt;/b&gt; all direct children elements of E that have the tag F&lt;/li&gt;
37     &lt;li&gt; &lt;b&gt;E + F&lt;/b&gt; all elements with the tag F that are immediately preceded by an element with the tag E&lt;/li&gt;
38     &lt;li&gt; &lt;b&gt;E ~ F&lt;/b&gt; all elements with the tag F that are preceded by a sibling element with the tag E&lt;/li&gt;
39 &lt;/ul&gt;
40 &lt;h4&gt;Attribute Selectors:&lt;/h4&gt;
41 &lt;p&gt;The use of &amp;#64; and quotes are optional. For example, div[&amp;#64;foo='bar'] is also a valid attribute selector.&lt;/p&gt;
42 &lt;ul class=&quot;list&quot;&gt;
43     &lt;li&gt; &lt;b&gt;E[foo]&lt;/b&gt; has an attribute &quot;foo&quot;&lt;/li&gt;
44     &lt;li&gt; &lt;b&gt;E[foo=bar]&lt;/b&gt; has an attribute &quot;foo&quot; that equals &quot;bar&quot;&lt;/li&gt;
45     &lt;li&gt; &lt;b&gt;E[foo^=bar]&lt;/b&gt; has an attribute &quot;foo&quot; that starts with &quot;bar&quot;&lt;/li&gt;
46     &lt;li&gt; &lt;b&gt;E[foo$=bar]&lt;/b&gt; has an attribute &quot;foo&quot; that ends with &quot;bar&quot;&lt;/li&gt;
47     &lt;li&gt; &lt;b&gt;E[foo*=bar]&lt;/b&gt; has an attribute &quot;foo&quot; that contains the substring &quot;bar&quot;&lt;/li&gt;
48     &lt;li&gt; &lt;b&gt;E[foo%=2]&lt;/b&gt; has an attribute &quot;foo&quot; that is evenly divisible by 2&lt;/li&gt;
49     &lt;li&gt; &lt;b&gt;E[foo!=bar]&lt;/b&gt; attribute &quot;foo&quot; does not equal &quot;bar&quot;&lt;/li&gt;
50 &lt;/ul&gt;
51 &lt;h4&gt;Pseudo Classes:&lt;/h4&gt;
52 &lt;ul class=&quot;list&quot;&gt;
53     &lt;li&gt; &lt;b&gt;E:first-child&lt;/b&gt; E is the first child of its parent&lt;/li&gt;
54     &lt;li&gt; &lt;b&gt;E:last-child&lt;/b&gt; E is the last child of its parent&lt;/li&gt;
55     &lt;li&gt; &lt;b&gt;E:nth-child(&lt;i&gt;n&lt;/i&gt;)&lt;/b&gt; E is the &lt;i&gt;n&lt;/i&gt;th child of its parent (1 based as per the spec)&lt;/li&gt;
56     &lt;li&gt; &lt;b&gt;E:nth-child(odd)&lt;/b&gt; E is an odd child of its parent&lt;/li&gt;
57     &lt;li&gt; &lt;b&gt;E:nth-child(even)&lt;/b&gt; E is an even child of its parent&lt;/li&gt;
58     &lt;li&gt; &lt;b&gt;E:only-child&lt;/b&gt; E is the only child of its parent&lt;/li&gt;
59     &lt;li&gt; &lt;b&gt;E:checked&lt;/b&gt; E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) &lt;/li&gt;
60     &lt;li&gt; &lt;b&gt;E:first&lt;/b&gt; the first E in the resultset&lt;/li&gt;
61     &lt;li&gt; &lt;b&gt;E:last&lt;/b&gt; the last E in the resultset&lt;/li&gt;
62     &lt;li&gt; &lt;b&gt;E:nth(&lt;i&gt;n&lt;/i&gt;)&lt;/b&gt; the &lt;i&gt;n&lt;/i&gt;th E in the resultset (1 based)&lt;/li&gt;
63     &lt;li&gt; &lt;b&gt;E:odd&lt;/b&gt; shortcut for :nth-child(odd)&lt;/li&gt;
64     &lt;li&gt; &lt;b&gt;E:even&lt;/b&gt; shortcut for :nth-child(even)&lt;/li&gt;
65     &lt;li&gt; &lt;b&gt;E:contains(foo)&lt;/b&gt; E's innerHTML contains the substring &quot;foo&quot;&lt;/li&gt;
66     &lt;li&gt; &lt;b&gt;E:nodeValue(foo)&lt;/b&gt; E contains a textNode with a nodeValue that equals &quot;foo&quot;&lt;/li&gt;
67     &lt;li&gt; &lt;b&gt;E:not(S)&lt;/b&gt; an E element that does not match simple selector S&lt;/li&gt;
68     &lt;li&gt; &lt;b&gt;E:has(S)&lt;/b&gt; an E element that has a descendent that matches simple selector S&lt;/li&gt;
69     &lt;li&gt; &lt;b&gt;E:next(S)&lt;/b&gt; an E element whose next sibling matches simple selector S&lt;/li&gt;
70     &lt;li&gt; &lt;b&gt;E:prev(S)&lt;/b&gt; an E element whose previous sibling matches simple selector S&lt;/li&gt;
71     &lt;li&gt; &lt;b&gt;E:any(S1|S2|S2)&lt;/b&gt; an E element which matches any of the simple selectors S1, S2 or S3//\\&lt;/li&gt;
72 &lt;/ul&gt;
73 &lt;h4&gt;CSS Value Selectors:&lt;/h4&gt;
74 &lt;ul class=&quot;list&quot;&gt;
75     &lt;li&gt; &lt;b&gt;E{display=none}&lt;/b&gt; css value &quot;display&quot; that equals &quot;none&quot;&lt;/li&gt;
76     &lt;li&gt; &lt;b&gt;E{display^=none}&lt;/b&gt; css value &quot;display&quot; that starts with &quot;none&quot;&lt;/li&gt;
77     &lt;li&gt; &lt;b&gt;E{display$=none}&lt;/b&gt; css value &quot;display&quot; that ends with &quot;none&quot;&lt;/li&gt;
78     &lt;li&gt; &lt;b&gt;E{display*=none}&lt;/b&gt; css value &quot;display&quot; that contains the substring &quot;none&quot;&lt;/li&gt;
79     &lt;li&gt; &lt;b&gt;E{display%=2}&lt;/b&gt; css value &quot;display&quot; that is evenly divisible by 2&lt;/li&gt;
80     &lt;li&gt; &lt;b&gt;E{display!=none}&lt;/b&gt; css value &quot;display&quot; that does not equal &quot;none&quot;&lt;/li&gt;
81 &lt;/ul&gt;
82  * @singleton
83  */
84 Ext.ns('Ext.core');
85
86 Ext.core.DomQuery = Ext.DomQuery = function(){
87     var cache = {},
88         simpleCache = {},
89         valueCache = {},
90         nonSpace = /\S/,
91         trimRe = /^\s+|\s+$/g,
92         tplRe = /\{(\d+)\}/g,
93         modeRe = /^(\s?[\/&gt;+~]\s?|\s|$)/,
94         tagTokenRe = /^(#)?([\w-\*]+)/,
95         nthRe = /(\d*)n\+?(\d*)/,
96         nthRe2 = /\D/,
97         startIdRe = /^\s*\#/,
98         // This is for IE MSXML which does not support expandos.
99     // IE runs the same speed using setAttribute, however FF slows way down
100     // and Safari completely fails so they need to continue to use expandos.
101     isIE = window.ActiveXObject ? true : false,
102     key = 30803;
103
104     // this eval is stop the compressor from
105     // renaming the variable to something shorter
106     eval(&quot;var batch = 30803;&quot;);
107
108     // Retrieve the child node from a particular
109     // parent at the specified index.
110     function child(parent, index){
111         var i = 0,
112             n = parent.firstChild;
113         while(n){
114             if(n.nodeType == 1){
115                if(++i == index){
116                    return n;
117                }
118             }
119             n = n.nextSibling;
120         }
121         return null;
122     }
123
124     // retrieve the next element node
125     function next(n){
126         while((n = n.nextSibling) &amp;&amp; n.nodeType != 1);
127         return n;
128     }
129
130     // retrieve the previous element node
131     function prev(n){
132         while((n = n.previousSibling) &amp;&amp; n.nodeType != 1);
133         return n;
134     }
135
136     // Mark each child node with a nodeIndex skipping and
137     // removing empty text nodes.
138     function children(parent){
139         var n = parent.firstChild,
140         nodeIndex = -1,
141         nextNode;
142         while(n){
143             nextNode = n.nextSibling;
144             // clean worthless empty nodes.
145             if(n.nodeType == 3 &amp;&amp; !nonSpace.test(n.nodeValue)){
146             parent.removeChild(n);
147             }else{
148             // add an expando nodeIndex
149             n.nodeIndex = ++nodeIndex;
150             }
151             n = nextNode;
152         }
153         return this;
154     }
155
156
157     // nodeSet - array of nodes
158     // cls - CSS Class
159     function byClassName(nodeSet, cls){
160         if(!cls){
161             return nodeSet;
162         }
163         var result = [], ri = -1;
164         for(var i = 0, ci; ci = nodeSet[i]; i++){
165             if((' '+ci.className+' ').indexOf(cls) != -1){
166                 result[++ri] = ci;
167             }
168         }
169         return result;
170     };
171
172     function attrValue(n, attr){
173         // if its an array, use the first node.
174         if(!n.tagName &amp;&amp; typeof n.length != &quot;undefined&quot;){
175             n = n[0];
176         }
177         if(!n){
178             return null;
179         }
180
181         if(attr == &quot;for&quot;){
182             return n.htmlFor;
183         }
184         if(attr == &quot;class&quot; || attr == &quot;className&quot;){
185             return n.className;
186         }
187         return n.getAttribute(attr) || n[attr];
188
189     };
190
191
192     // ns - nodes
193     // mode - false, /, &gt;, +, ~
194     // tagName - defaults to &quot;*&quot;
195     function getNodes(ns, mode, tagName){
196         var result = [], ri = -1, cs;
197         if(!ns){
198             return result;
199         }
200         tagName = tagName || &quot;*&quot;;
201         // convert to array
202         if(typeof ns.getElementsByTagName != &quot;undefined&quot;){
203             ns = [ns];
204         }
205
206         // no mode specified, grab all elements by tagName
207         // at any depth
208         if(!mode){
209             for(var i = 0, ni; ni = ns[i]; i++){
210                 cs = ni.getElementsByTagName(tagName);
211                 for(var j = 0, ci; ci = cs[j]; j++){
212                     result[++ri] = ci;
213                 }
214             }
215         // Direct Child mode (/ or &gt;)
216         // E &gt; F or E/F all direct children elements of E that have the tag
217         } else if(mode == &quot;/&quot; || mode == &quot;&gt;&quot;){
218             var utag = tagName.toUpperCase();
219             for(var i = 0, ni, cn; ni = ns[i]; i++){
220                 cn = ni.childNodes;
221                 for(var j = 0, cj; cj = cn[j]; j++){
222                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
223                         result[++ri] = cj;
224                     }
225                 }
226             }
227         // Immediately Preceding mode (+)
228         // E + F all elements with the tag F that are immediately preceded by an element with the tag E
229         }else if(mode == &quot;+&quot;){
230             var utag = tagName.toUpperCase();
231             for(var i = 0, n; n = ns[i]; i++){
232                 while((n = n.nextSibling) &amp;&amp; n.nodeType != 1);
233                 if(n &amp;&amp; (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
234                     result[++ri] = n;
235                 }
236             }
237         // Sibling mode (~)
238         // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
239         }else if(mode == &quot;~&quot;){
240             var utag = tagName.toUpperCase();
241             for(var i = 0, n; n = ns[i]; i++){
242                 while((n = n.nextSibling)){
243                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
244                         result[++ri] = n;
245                     }
246                 }
247             }
248         }
249         return result;
250     }
251
252     function concat(a, b){
253         if(b.slice){
254             return a.concat(b);
255         }
256         for(var i = 0, l = b.length; i &lt; l; i++){
257             a[a.length] = b[i];
258         }
259         return a;
260     }
261
262     function byTag(cs, tagName){
263         if(cs.tagName || cs == document){
264             cs = [cs];
265         }
266         if(!tagName){
267             return cs;
268         }
269         var result = [], ri = -1;
270         tagName = tagName.toLowerCase();
271         for(var i = 0, ci; ci = cs[i]; i++){
272             if(ci.nodeType == 1 &amp;&amp; ci.tagName.toLowerCase() == tagName){
273                 result[++ri] = ci;
274             }
275         }
276         return result;
277     }
278
279     function byId(cs, id){
280         if(cs.tagName || cs == document){
281             cs = [cs];
282         }
283         if(!id){
284             return cs;
285         }
286         var result = [], ri = -1;
287         for(var i = 0, ci; ci = cs[i]; i++){
288             if(ci &amp;&amp; ci.id == id){
289                 result[++ri] = ci;
290                 return result;
291             }
292         }
293         return result;
294     }
295
296     // operators are =, !=, ^=, $=, *=, %=, |= and ~=
297     // custom can be &quot;{&quot;
298     function byAttribute(cs, attr, value, op, custom){
299         var result = [],
300             ri = -1,
301             useGetStyle = custom == &quot;{&quot;,
302             fn = Ext.DomQuery.operators[op],
303             a,
304             xml,
305             hasXml;
306
307         for(var i = 0, ci; ci = cs[i]; i++){
308             // skip non-element nodes.
309             if(ci.nodeType != 1){
310                 continue;
311             }
312             // only need to do this for the first node
313             if(!hasXml){
314                 xml = Ext.DomQuery.isXml(ci);
315                 hasXml = true;
316             }
317
318             // we only need to change the property names if we're dealing with html nodes, not XML
319             if(!xml){
320                 if(useGetStyle){
321                     a = Ext.DomQuery.getStyle(ci, attr);
322                 } else if (attr == &quot;class&quot; || attr == &quot;className&quot;){
323                     a = ci.className;
324                 } else if (attr == &quot;for&quot;){
325                     a = ci.htmlFor;
326                 } else if (attr == &quot;href&quot;){
327                     // getAttribute href bug
328                     // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
329                     a = ci.getAttribute(&quot;href&quot;, 2);
330                 } else{
331                     a = ci.getAttribute(attr);
332                 }
333             }else{
334                 a = ci.getAttribute(attr);
335             }
336             if((fn &amp;&amp; fn(a, value)) || (!fn &amp;&amp; a)){
337                 result[++ri] = ci;
338             }
339         }
340         return result;
341     }
342
343     function byPseudo(cs, name, value){
344         return Ext.DomQuery.pseudos[name](cs, value);
345     }
346
347     function nodupIEXml(cs){
348         var d = ++key,
349             r;
350         cs[0].setAttribute(&quot;_nodup&quot;, d);
351         r = [cs[0]];
352         for(var i = 1, len = cs.length; i &lt; len; i++){
353             var c = cs[i];
354             if(!c.getAttribute(&quot;_nodup&quot;) != d){
355                 c.setAttribute(&quot;_nodup&quot;, d);
356                 r[r.length] = c;
357             }
358         }
359         for(var i = 0, len = cs.length; i &lt; len; i++){
360             cs[i].removeAttribute(&quot;_nodup&quot;);
361         }
362         return r;
363     }
364
365     function nodup(cs){
366         if(!cs){
367             return [];
368         }
369         var len = cs.length, c, i, r = cs, cj, ri = -1;
370         if(!len || typeof cs.nodeType != &quot;undefined&quot; || len == 1){
371             return cs;
372         }
373         if(isIE &amp;&amp; typeof cs[0].selectSingleNode != &quot;undefined&quot;){
374             return nodupIEXml(cs);
375         }
376         var d = ++key;
377         cs[0]._nodup = d;
378         for(i = 1; c = cs[i]; i++){
379             if(c._nodup != d){
380                 c._nodup = d;
381             }else{
382                 r = [];
383                 for(var j = 0; j &lt; i; j++){
384                     r[++ri] = cs[j];
385                 }
386                 for(j = i+1; cj = cs[j]; j++){
387                     if(cj._nodup != d){
388                         cj._nodup = d;
389                         r[++ri] = cj;
390                     }
391                 }
392                 return r;
393             }
394         }
395         return r;
396     }
397
398     function quickDiffIEXml(c1, c2){
399         var d = ++key,
400             r = [];
401         for(var i = 0, len = c1.length; i &lt; len; i++){
402             c1[i].setAttribute(&quot;_qdiff&quot;, d);
403         }
404         for(var i = 0, len = c2.length; i &lt; len; i++){
405             if(c2[i].getAttribute(&quot;_qdiff&quot;) != d){
406                 r[r.length] = c2[i];
407             }
408         }
409         for(var i = 0, len = c1.length; i &lt; len; i++){
410            c1[i].removeAttribute(&quot;_qdiff&quot;);
411         }
412         return r;
413     }
414
415     function quickDiff(c1, c2){
416         var len1 = c1.length,
417             d = ++key,
418             r = [];
419         if(!len1){
420             return c2;
421         }
422         if(isIE &amp;&amp; typeof c1[0].selectSingleNode != &quot;undefined&quot;){
423             return quickDiffIEXml(c1, c2);
424         }
425         for(var i = 0; i &lt; len1; i++){
426             c1[i]._qdiff = d;
427         }
428         for(var i = 0, len = c2.length; i &lt; len; i++){
429             if(c2[i]._qdiff != d){
430                 r[r.length] = c2[i];
431             }
432         }
433         return r;
434     }
435
436     function quickId(ns, mode, root, id){
437         if(ns == root){
438            var d = root.ownerDocument || root;
439            return d.getElementById(id);
440         }
441         ns = getNodes(ns, mode, &quot;*&quot;);
442         return byId(ns, id);
443     }
444
445     return {
446         getStyle : function(el, name){
447             return Ext.fly(el).getStyle(name);
448         },
449 <span id='Ext-DomQuery-method-compile'>        /**
450 </span>         * Compiles a selector/xpath query into a reusable function. The returned function
451          * takes one parameter &quot;root&quot; (optional), which is the context node from where the query should start.
452          * @param {String} selector The selector/xpath query
453          * @param {String} type (optional) Either &quot;select&quot; (the default) or &quot;simple&quot; for a simple selector match
454          * @return {Function}
455          */
456         compile : function(path, type){
457             type = type || &quot;select&quot;;
458
459             // setup fn preamble
460             var fn = [&quot;var f = function(root){\n var mode; ++batch; var n = root || document;\n&quot;],
461                 mode,
462                 lastPath,
463                 matchers = Ext.DomQuery.matchers,
464                 matchersLn = matchers.length,
465                 modeMatch,
466                 // accept leading mode switch
467                 lmode = path.match(modeRe);
468
469             if(lmode &amp;&amp; lmode[1]){
470                 fn[fn.length] = 'mode=&quot;'+lmode[1].replace(trimRe, &quot;&quot;)+'&quot;;';
471                 path = path.replace(lmode[1], &quot;&quot;);
472             }
473
474             // strip leading slashes
475             while(path.substr(0, 1)==&quot;/&quot;){
476                 path = path.substr(1);
477             }
478
479             while(path &amp;&amp; lastPath != path){
480                 lastPath = path;
481                 var tokenMatch = path.match(tagTokenRe);
482                 if(type == &quot;select&quot;){
483                     if(tokenMatch){
484                         // ID Selector
485                         if(tokenMatch[1] == &quot;#&quot;){
486                             fn[fn.length] = 'n = quickId(n, mode, root, &quot;'+tokenMatch[2]+'&quot;);';
487                         }else{
488                             fn[fn.length] = 'n = getNodes(n, mode, &quot;'+tokenMatch[2]+'&quot;);';
489                         }
490                         path = path.replace(tokenMatch[0], &quot;&quot;);
491                     }else if(path.substr(0, 1) != '@'){
492                         fn[fn.length] = 'n = getNodes(n, mode, &quot;*&quot;);';
493                     }
494                 // type of &quot;simple&quot;
495                 }else{
496                     if(tokenMatch){
497                         if(tokenMatch[1] == &quot;#&quot;){
498                             fn[fn.length] = 'n = byId(n, &quot;'+tokenMatch[2]+'&quot;);';
499                         }else{
500                             fn[fn.length] = 'n = byTag(n, &quot;'+tokenMatch[2]+'&quot;);';
501                         }
502                         path = path.replace(tokenMatch[0], &quot;&quot;);
503                     }
504                 }
505                 while(!(modeMatch = path.match(modeRe))){
506                     var matched = false;
507                     for(var j = 0; j &lt; matchersLn; j++){
508                         var t = matchers[j];
509                         var m = path.match(t.re);
510                         if(m){
511                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
512                                 return m[i];
513                             });
514                             path = path.replace(m[0], &quot;&quot;);
515                             matched = true;
516                             break;
517                         }
518                     }
519                     // prevent infinite loop on bad selector
520                     if(!matched){
521                         //&lt;debug&gt;
522                         Ext.Error.raise({
523                             sourceClass: 'Ext.DomQuery',
524                             sourceMethod: 'compile',
525                             msg: 'Error parsing selector. Parsing failed at &quot;' + path + '&quot;'
526                         });
527                         //&lt;/debug&gt;
528                     }
529                 }
530                 if(modeMatch[1]){
531                     fn[fn.length] = 'mode=&quot;'+modeMatch[1].replace(trimRe, &quot;&quot;)+'&quot;;';
532                     path = path.replace(modeMatch[1], &quot;&quot;);
533                 }
534             }
535             // close fn out
536             fn[fn.length] = &quot;return nodup(n);\n}&quot;;
537
538             // eval fn and return it
539             eval(fn.join(&quot;&quot;));
540             return f;
541         },
542
543 <span id='Ext-DomQuery-method-jsSelect'>        /**
544 </span>         * Selects an array of DOM nodes using JavaScript-only implementation.
545          *
546          * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
547          *
548          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
549          * @param {HTMLElement/String} root (optional) The start of the query (defaults to document).
550          * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
551          * no matches, and empty Array is returned.
552          */
553         jsSelect: function(path, root, type){
554             // set root to doc if not specified.
555             root = root || document;
556
557             if(typeof root == &quot;string&quot;){
558                 root = document.getElementById(root);
559             }
560             var paths = path.split(&quot;,&quot;),
561                 results = [];
562
563             // loop over each selector
564             for(var i = 0, len = paths.length; i &lt; len; i++){
565                 var subPath = paths[i].replace(trimRe, &quot;&quot;);
566                 // compile and place in cache
567                 if(!cache[subPath]){
568                     cache[subPath] = Ext.DomQuery.compile(subPath);
569                     if(!cache[subPath]){
570                         //&lt;debug&gt;
571                         Ext.Error.raise({
572                             sourceClass: 'Ext.DomQuery',
573                             sourceMethod: 'jsSelect',
574                             msg: subPath + ' is not a valid selector'
575                         });
576                         //&lt;/debug&gt;
577                     }
578                 }
579                 var result = cache[subPath](root);
580                 if(result &amp;&amp; result != document){
581                     results = results.concat(result);
582                 }
583             }
584
585             // if there were multiple selectors, make sure dups
586             // are eliminated
587             if(paths.length &gt; 1){
588                 return nodup(results);
589             }
590             return results;
591         },
592
593         isXml: function(el) {
594             var docEl = (el ? el.ownerDocument || el : 0).documentElement;
595             return docEl ? docEl.nodeName !== &quot;HTML&quot; : false;
596         },
597
598 <span id='Ext-DomQuery-method-select'>        /**
599 </span>         * Selects an array of DOM nodes by CSS/XPath selector.
600          *
601          * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
602          * {@link Ext.DomQuery#jsSelect} to do the work.
603          *
604          * Aliased as {@link Ext#query}.
605          *
606          * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
607          *
608          * @param {String} path The selector/xpath query
609          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
610          * @return {HTMLElement[]} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
611          * Empty array when no matches.
612          * @method
613          */
614         select : document.querySelectorAll ? function(path, root, type) {
615             root = root || document;
616             /* 
617              * Safari 3.x can't handle uppercase or unicode characters when in quirks mode.
618              */
619             if (!Ext.DomQuery.isXml(root) &amp;&amp; !(Ext.isSafari3 &amp;&amp; !Ext.isStrict)) { 
620                 try {
621                     /*
622                      * This checking here is to &quot;fix&quot; the behaviour of querySelectorAll
623                      * for non root document queries. The way qsa works is intentional,
624                      * however it's definitely not the expected way it should work.
625                      * More info: http://ejohn.org/blog/thoughts-on-queryselectorall/
626                      *
627                      * We only modify the path for single selectors (ie, no multiples),
628                      * without a full parser it makes it difficult to do this correctly.
629                      */
630                     var isDocumentRoot = root.nodeType === 9,
631                         _path = path,
632                         _root = root;
633
634                     if (!isDocumentRoot &amp;&amp; path.indexOf(',') === -1 &amp;&amp; !startIdRe.test(path)) {
635                         _path = '#' + Ext.id(root) + ' ' + path;
636                         _root = root.parentNode;
637                     }
638                     return Ext.Array.toArray(_root.querySelectorAll(_path));
639                 }
640                 catch (e) {
641                 }
642             }
643             return Ext.DomQuery.jsSelect.call(this, path, root, type);
644         } : function(path, root, type) {
645             return Ext.DomQuery.jsSelect.call(this, path, root, type);
646         },
647
648 <span id='Ext-DomQuery-method-selectNode'>        /**
649 </span>         * Selects a single element.
650          * @param {String} selector The selector/xpath query
651          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
652          * @return {HTMLElement} The DOM element which matched the selector.
653          */
654         selectNode : function(path, root){
655             return Ext.DomQuery.select(path, root)[0];
656         },
657
658 <span id='Ext-DomQuery-method-selectValue'>        /**
659 </span>         * Selects the value of a node, optionally replacing null with the defaultValue.
660          * @param {String} selector The selector/xpath query
661          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
662          * @param {String} defaultValue (optional) When specified, this is return as empty value.
663          * @return {String}
664          */
665         selectValue : function(path, root, defaultValue){
666             path = path.replace(trimRe, &quot;&quot;);
667             if(!valueCache[path]){
668                 valueCache[path] = Ext.DomQuery.compile(path, &quot;select&quot;);
669             }
670             var n = valueCache[path](root), v;
671             n = n[0] ? n[0] : n;
672
673             // overcome a limitation of maximum textnode size
674             // Rumored to potentially crash IE6 but has not been confirmed.
675             // http://reference.sitepoint.com/javascript/Node/normalize
676             // https://developer.mozilla.org/En/DOM/Node.normalize
677             if (typeof n.normalize == 'function') n.normalize();
678
679             v = (n &amp;&amp; n.firstChild ? n.firstChild.nodeValue : null);
680             return ((v === null||v === undefined||v==='') ? defaultValue : v);
681         },
682
683 <span id='Ext-DomQuery-method-selectNumber'>        /**
684 </span>         * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
685          * @param {String} selector The selector/xpath query
686          * @param {HTMLElement} root (optional) The start of the query (defaults to document).
687          * @param {Number} defaultValue (optional) When specified, this is return as empty value.
688          * @return {Number}
689          */
690         selectNumber : function(path, root, defaultValue){
691             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
692             return parseFloat(v);
693         },
694
695 <span id='Ext-DomQuery-method-is'>        /**
696 </span>         * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
697          * @param {String/HTMLElement/HTMLElement[]} el An element id, element or array of elements
698          * @param {String} selector The simple selector to test
699          * @return {Boolean}
700          */
701         is : function(el, ss){
702             if(typeof el == &quot;string&quot;){
703                 el = document.getElementById(el);
704             }
705             var isArray = Ext.isArray(el),
706                 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
707             return isArray ? (result.length == el.length) : (result.length &gt; 0);
708         },
709
710 <span id='Ext-DomQuery-method-filter'>        /**
711 </span>         * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
712          * @param {HTMLElement[]} el An array of elements to filter
713          * @param {String} selector The simple selector to test
714          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
715          * the selector instead of the ones that match
716          * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
717          * no matches, and empty Array is returned.
718          */
719         filter : function(els, ss, nonMatches){
720             ss = ss.replace(trimRe, &quot;&quot;);
721             if(!simpleCache[ss]){
722                 simpleCache[ss] = Ext.DomQuery.compile(ss, &quot;simple&quot;);
723             }
724             var result = simpleCache[ss](els);
725             return nonMatches ? quickDiff(result, els) : result;
726         },
727
728 <span id='Ext-DomQuery-property-matchers'>        /**
729 </span>         * Collection of matching regular expressions and code snippets.
730          * Each capture group within () will be replace the {} in the select
731          * statement as specified by their index.
732          */
733         matchers : [{
734                 re: /^\.([\w-]+)/,
735                 select: 'n = byClassName(n, &quot; {1} &quot;);'
736             }, {
737                 re: /^\:([\w-]+)(?:\(((?:[^\s&gt;\/]*|.*?))\))?/,
738                 select: 'n = byPseudo(n, &quot;{1}&quot;, &quot;{2}&quot;);'
739             },{
740                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['&quot;]?(.*?)[&quot;']?)?[\]\}])/,
741                 select: 'n = byAttribute(n, &quot;{2}&quot;, &quot;{4}&quot;, &quot;{3}&quot;, &quot;{1}&quot;);'
742             }, {
743                 re: /^#([\w-]+)/,
744                 select: 'n = byId(n, &quot;{1}&quot;);'
745             },{
746                 re: /^@([\w-]+)/,
747                 select: 'return {firstChild:{nodeValue:attrValue(n, &quot;{1}&quot;)}};'
748             }
749         ],
750
751 <span id='Ext-DomQuery-property-operators'>        /**
752 </span>         * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
753          * New operators can be added as long as the match the format &lt;i&gt;c&lt;/i&gt;= where &lt;i&gt;c&lt;/i&gt; is any character other than space, &amp;gt; &amp;lt;.
754          */
755         operators : {
756             &quot;=&quot; : function(a, v){
757                 return a == v;
758             },
759             &quot;!=&quot; : function(a, v){
760                 return a != v;
761             },
762             &quot;^=&quot; : function(a, v){
763                 return a &amp;&amp; a.substr(0, v.length) == v;
764             },
765             &quot;$=&quot; : function(a, v){
766                 return a &amp;&amp; a.substr(a.length-v.length) == v;
767             },
768             &quot;*=&quot; : function(a, v){
769                 return a &amp;&amp; a.indexOf(v) !== -1;
770             },
771             &quot;%=&quot; : function(a, v){
772                 return (a % v) == 0;
773             },
774             &quot;|=&quot; : function(a, v){
775                 return a &amp;&amp; (a == v || a.substr(0, v.length+1) == v+'-');
776             },
777             &quot;~=&quot; : function(a, v){
778                 return a &amp;&amp; (' '+a+' ').indexOf(' '+v+' ') != -1;
779             }
780         },
781
782 <span id='Ext-DomQuery-property-pseudos'>        /**
783 </span>Object hash of &quot;pseudo class&quot; filter functions which are used when filtering selections.
784 Each function is passed two parameters:
785
786 - **c** : Array
787     An Array of DOM elements to filter.
788
789 - **v** : String
790     The argument (if any) supplied in the selector.
791
792 A filter function returns an Array of DOM elements which conform to the pseudo class.
793 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
794 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
795
796 For example, to filter `a` elements to only return links to __external__ resources:
797
798     Ext.DomQuery.pseudos.external = function(c, v){
799         var r = [], ri = -1;
800         for(var i = 0, ci; ci = c[i]; i++){
801             // Include in result set only if it's a link to an external resource
802             if(ci.hostname != location.hostname){
803                 r[++ri] = ci;
804             }
805         }
806         return r;
807     };
808
809 Then external links could be gathered with the following statement:
810
811     var externalLinks = Ext.select(&quot;a:external&quot;);
812
813         * @markdown
814         */
815         pseudos : {
816             &quot;first-child&quot; : function(c){
817                 var r = [], ri = -1, n;
818                 for(var i = 0, ci; ci = n = c[i]; i++){
819                     while((n = n.previousSibling) &amp;&amp; n.nodeType != 1);
820                     if(!n){
821                         r[++ri] = ci;
822                     }
823                 }
824                 return r;
825             },
826
827             &quot;last-child&quot; : function(c){
828                 var r = [], ri = -1, n;
829                 for(var i = 0, ci; ci = n = c[i]; i++){
830                     while((n = n.nextSibling) &amp;&amp; n.nodeType != 1);
831                     if(!n){
832                         r[++ri] = ci;
833                     }
834                 }
835                 return r;
836             },
837
838             &quot;nth-child&quot; : function(c, a) {
839                 var r = [], ri = -1,
840                     m = nthRe.exec(a == &quot;even&quot; &amp;&amp; &quot;2n&quot; || a == &quot;odd&quot; &amp;&amp; &quot;2n+1&quot; || !nthRe2.test(a) &amp;&amp; &quot;n+&quot; + a || a),
841                     f = (m[1] || 1) - 0, l = m[2] - 0;
842                 for(var i = 0, n; n = c[i]; i++){
843                     var pn = n.parentNode;
844                     if (batch != pn._batch) {
845                         var j = 0;
846                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
847                             if(cn.nodeType == 1){
848                                cn.nodeIndex = ++j;
849                             }
850                         }
851                         pn._batch = batch;
852                     }
853                     if (f == 1) {
854                         if (l == 0 || n.nodeIndex == l){
855                             r[++ri] = n;
856                         }
857                     } else if ((n.nodeIndex + l) % f == 0){
858                         r[++ri] = n;
859                     }
860                 }
861
862                 return r;
863             },
864
865             &quot;only-child&quot; : function(c){
866                 var r = [], ri = -1;;
867                 for(var i = 0, ci; ci = c[i]; i++){
868                     if(!prev(ci) &amp;&amp; !next(ci)){
869                         r[++ri] = ci;
870                     }
871                 }
872                 return r;
873             },
874
875             &quot;empty&quot; : function(c){
876                 var r = [], ri = -1;
877                 for(var i = 0, ci; ci = c[i]; i++){
878                     var cns = ci.childNodes, j = 0, cn, empty = true;
879                     while(cn = cns[j]){
880                         ++j;
881                         if(cn.nodeType == 1 || cn.nodeType == 3){
882                             empty = false;
883                             break;
884                         }
885                     }
886                     if(empty){
887                         r[++ri] = ci;
888                     }
889                 }
890                 return r;
891             },
892
893             &quot;contains&quot; : function(c, v){
894                 var r = [], ri = -1;
895                 for(var i = 0, ci; ci = c[i]; i++){
896                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
897                         r[++ri] = ci;
898                     }
899                 }
900                 return r;
901             },
902
903             &quot;nodeValue&quot; : function(c, v){
904                 var r = [], ri = -1;
905                 for(var i = 0, ci; ci = c[i]; i++){
906                     if(ci.firstChild &amp;&amp; ci.firstChild.nodeValue == v){
907                         r[++ri] = ci;
908                     }
909                 }
910                 return r;
911             },
912
913             &quot;checked&quot; : function(c){
914                 var r = [], ri = -1;
915                 for(var i = 0, ci; ci = c[i]; i++){
916                     if(ci.checked == true){
917                         r[++ri] = ci;
918                     }
919                 }
920                 return r;
921             },
922
923             &quot;not&quot; : function(c, ss){
924                 return Ext.DomQuery.filter(c, ss, true);
925             },
926
927             &quot;any&quot; : function(c, selectors){
928                 var ss = selectors.split('|'),
929                     r = [], ri = -1, s;
930                 for(var i = 0, ci; ci = c[i]; i++){
931                     for(var j = 0; s = ss[j]; j++){
932                         if(Ext.DomQuery.is(ci, s)){
933                             r[++ri] = ci;
934                             break;
935                         }
936                     }
937                 }
938                 return r;
939             },
940
941             &quot;odd&quot; : function(c){
942                 return this[&quot;nth-child&quot;](c, &quot;odd&quot;);
943             },
944
945             &quot;even&quot; : function(c){
946                 return this[&quot;nth-child&quot;](c, &quot;even&quot;);
947             },
948
949             &quot;nth&quot; : function(c, a){
950                 return c[a-1] || [];
951             },
952
953             &quot;first&quot; : function(c){
954                 return c[0] || [];
955             },
956
957             &quot;last&quot; : function(c){
958                 return c[c.length-1] || [];
959             },
960
961             &quot;has&quot; : function(c, ss){
962                 var s = Ext.DomQuery.select,
963                     r = [], ri = -1;
964                 for(var i = 0, ci; ci = c[i]; i++){
965                     if(s(ss, ci).length &gt; 0){
966                         r[++ri] = ci;
967                     }
968                 }
969                 return r;
970             },
971
972             &quot;next&quot; : function(c, ss){
973                 var is = Ext.DomQuery.is,
974                     r = [], ri = -1;
975                 for(var i = 0, ci; ci = c[i]; i++){
976                     var n = next(ci);
977                     if(n &amp;&amp; is(n, ss)){
978                         r[++ri] = ci;
979                     }
980                 }
981                 return r;
982             },
983
984             &quot;prev&quot; : function(c, ss){
985                 var is = Ext.DomQuery.is,
986                     r = [], ri = -1;
987                 for(var i = 0, ci; ci = c[i]; i++){
988                     var n = prev(ci);
989                     if(n &amp;&amp; is(n, ss)){
990                         r[++ri] = ci;
991                     }
992                 }
993                 return r;
994             }
995         }
996     };
997 }();
998
999 <span id='Ext-method-query'>/**
1000 </span> * Shorthand of {@link Ext.DomQuery#select}
1001  * @member Ext
1002  * @method query
1003  * @alias Ext.DomQuery#select
1004  */
1005 Ext.query = Ext.DomQuery.select;
1006 </pre>
1007 </body>
1008 </html>