3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * This is code is also distributed under MIT license for use
17 * with jQuery and prototype JavaScript libraries.
21 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).
23 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
26 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" 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.
28 <h4>Element Selectors:</h4>
30 <li> <b>*</b> any element</li>
31 <li> <b>E</b> an element with the tag E</li>
32 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
33 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
34 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
35 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
37 <h4>Attribute Selectors:</h4>
38 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
40 <li> <b>E[foo]</b> has an attribute "foo"</li>
41 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
42 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
43 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
44 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
45 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
46 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
48 <h4>Pseudo Classes:</h4>
50 <li> <b>E:first-child</b> E is the first child of its parent</li>
51 <li> <b>E:last-child</b> E is the last child of its parent</li>
52 <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
53 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
54 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
55 <li> <b>E:only-child</b> E is the only child of its parent</li>
56 <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
57 <li> <b>E:first</b> the first E in the resultset</li>
58 <li> <b>E:last</b> the last E in the resultset</li>
59 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
60 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
61 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
62 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
63 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
64 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
65 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
66 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
67 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
68 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
70 <h4>CSS Value Selectors:</h4>
72 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
73 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
74 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
75 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
76 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
77 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
83 Ext.core.DomQuery = Ext.DomQuery = function(){
88 trimRe = /^\s+|\s+$/g,
90 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
91 tagTokenRe = /^(#)?([\w-\*]+)/,
92 nthRe = /(\d*)n\+?(\d*)/,
95 // This is for IE MSXML which does not support expandos.
96 // IE runs the same speed using setAttribute, however FF slows way down
97 // and Safari completely fails so they need to continue to use expandos.
98 isIE = window.ActiveXObject ? true : false,
101 // this eval is stop the compressor from
102 // renaming the variable to something shorter
103 eval("var batch = 30803;");
105 // Retrieve the child node from a particular
106 // parent at the specified index.
107 function child(parent, index){
109 n = parent.firstChild;
121 // retrieve the next element node
123 while((n = n.nextSibling) && n.nodeType != 1);
127 // retrieve the previous element node
129 while((n = n.previousSibling) && n.nodeType != 1);
133 // Mark each child node with a nodeIndex skipping and
134 // removing empty text nodes.
135 function children(parent){
136 var n = parent.firstChild,
140 nextNode = n.nextSibling;
141 // clean worthless empty nodes.
142 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
143 parent.removeChild(n);
145 // add an expando nodeIndex
146 n.nodeIndex = ++nodeIndex;
154 // nodeSet - array of nodes
156 function byClassName(nodeSet, cls){
160 var result = [], ri = -1;
161 for(var i = 0, ci; ci = nodeSet[i]; i++){
162 if((' '+ci.className+' ').indexOf(cls) != -1){
169 function attrValue(n, attr){
170 // if its an array, use the first node.
171 if(!n.tagName && typeof n.length != "undefined"){
181 if(attr == "class" || attr == "className"){
184 return n.getAttribute(attr) || n[attr];
190 // mode - false, /, >, +, ~
191 // tagName - defaults to "*"
192 function getNodes(ns, mode, tagName){
193 var result = [], ri = -1, cs;
197 tagName = tagName || "*";
199 if(typeof ns.getElementsByTagName != "undefined"){
203 // no mode specified, grab all elements by tagName
206 for(var i = 0, ni; ni = ns[i]; i++){
207 cs = ni.getElementsByTagName(tagName);
208 for(var j = 0, ci; ci = cs[j]; j++){
212 // Direct Child mode (/ or >)
213 // E > F or E/F all direct children elements of E that have the tag
214 } else if(mode == "/" || mode == ">"){
215 var utag = tagName.toUpperCase();
216 for(var i = 0, ni, cn; ni = ns[i]; i++){
218 for(var j = 0, cj; cj = cn[j]; j++){
219 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
224 // Immediately Preceding mode (+)
225 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
226 }else if(mode == "+"){
227 var utag = tagName.toUpperCase();
228 for(var i = 0, n; n = ns[i]; i++){
229 while((n = n.nextSibling) && n.nodeType != 1);
230 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
235 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
236 }else if(mode == "~"){
237 var utag = tagName.toUpperCase();
238 for(var i = 0, n; n = ns[i]; i++){
239 while((n = n.nextSibling)){
240 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
249 function concat(a, b){
253 for(var i = 0, l = b.length; i < l; i++){
259 function byTag(cs, tagName){
260 if(cs.tagName || cs == document){
266 var result = [], ri = -1;
267 tagName = tagName.toLowerCase();
268 for(var i = 0, ci; ci = cs[i]; i++){
269 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
276 function byId(cs, id){
277 if(cs.tagName || cs == document){
283 var result = [], ri = -1;
284 for(var i = 0, ci; ci = cs[i]; i++){
285 if(ci && ci.id == id){
293 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
295 function byAttribute(cs, attr, value, op, custom){
298 useGetStyle = custom == "{",
299 fn = Ext.DomQuery.operators[op],
304 for(var i = 0, ci; ci = cs[i]; i++){
305 // skip non-element nodes.
306 if(ci.nodeType != 1){
309 // only need to do this for the first node
311 xml = Ext.DomQuery.isXml(ci);
315 // we only need to change the property names if we're dealing with html nodes, not XML
318 a = Ext.DomQuery.getStyle(ci, attr);
319 } else if (attr == "class" || attr == "className"){
321 } else if (attr == "for"){
323 } else if (attr == "href"){
324 // getAttribute href bug
325 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
326 a = ci.getAttribute("href", 2);
328 a = ci.getAttribute(attr);
331 a = ci.getAttribute(attr);
333 if((fn && fn(a, value)) || (!fn && a)){
340 function byPseudo(cs, name, value){
341 return Ext.DomQuery.pseudos[name](cs, value);
344 function nodupIEXml(cs){
347 cs[0].setAttribute("_nodup", d);
349 for(var i = 1, len = cs.length; i < len; i++){
351 if(!c.getAttribute("_nodup") != d){
352 c.setAttribute("_nodup", d);
356 for(var i = 0, len = cs.length; i < len; i++){
357 cs[i].removeAttribute("_nodup");
366 var len = cs.length, c, i, r = cs, cj, ri = -1;
367 if(!len || typeof cs.nodeType != "undefined" || len == 1){
370 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
371 return nodupIEXml(cs);
375 for(i = 1; c = cs[i]; i++){
380 for(var j = 0; j < i; j++){
383 for(j = i+1; cj = cs[j]; j++){
395 function quickDiffIEXml(c1, c2){
398 for(var i = 0, len = c1.length; i < len; i++){
399 c1[i].setAttribute("_qdiff", d);
401 for(var i = 0, len = c2.length; i < len; i++){
402 if(c2[i].getAttribute("_qdiff") != d){
406 for(var i = 0, len = c1.length; i < len; i++){
407 c1[i].removeAttribute("_qdiff");
412 function quickDiff(c1, c2){
413 var len1 = c1.length,
419 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
420 return quickDiffIEXml(c1, c2);
422 for(var i = 0; i < len1; i++){
425 for(var i = 0, len = c2.length; i < len; i++){
426 if(c2[i]._qdiff != d){
433 function quickId(ns, mode, root, id){
435 var d = root.ownerDocument || root;
436 return d.getElementById(id);
438 ns = getNodes(ns, mode, "*");
443 getStyle : function(el, name){
444 return Ext.fly(el).getStyle(name);
447 * Compiles a selector/xpath query into a reusable function. The returned function
448 * takes one parameter "root" (optional), which is the context node from where the query should start.
449 * @param {String} selector The selector/xpath query
450 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
453 compile : function(path, type){
454 type = type || "select";
457 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
460 matchers = Ext.DomQuery.matchers,
461 matchersLn = matchers.length,
463 // accept leading mode switch
464 lmode = path.match(modeRe);
466 if(lmode && lmode[1]){
467 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
468 path = path.replace(lmode[1], "");
471 // strip leading slashes
472 while(path.substr(0, 1)=="/"){
473 path = path.substr(1);
476 while(path && lastPath != path){
478 var tokenMatch = path.match(tagTokenRe);
479 if(type == "select"){
482 if(tokenMatch[1] == "#"){
483 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
485 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
487 path = path.replace(tokenMatch[0], "");
488 }else if(path.substr(0, 1) != '@'){
489 fn[fn.length] = 'n = getNodes(n, mode, "*");';
494 if(tokenMatch[1] == "#"){
495 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
497 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
499 path = path.replace(tokenMatch[0], "");
502 while(!(modeMatch = path.match(modeRe))){
504 for(var j = 0; j < matchersLn; j++){
506 var m = path.match(t.re);
508 fn[fn.length] = t.select.replace(tplRe, function(x, i){
511 path = path.replace(m[0], "");
516 // prevent infinite loop on bad selector
520 sourceClass: 'Ext.DomQuery',
521 sourceMethod: 'compile',
522 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
528 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
529 path = path.replace(modeMatch[1], "");
533 fn[fn.length] = "return nodup(n);\n}";
535 // eval fn and return it
541 * Selects an array of DOM nodes using JavaScript-only implementation.
543 * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
545 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
546 * @param {HTMLElement/String} root (optional) The start of the query (defaults to document).
547 * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
548 * no matches, and empty Array is returned.
550 jsSelect: function(path, root, type){
551 // set root to doc if not specified.
552 root = root || document;
554 if(typeof root == "string"){
555 root = document.getElementById(root);
557 var paths = path.split(","),
560 // loop over each selector
561 for(var i = 0, len = paths.length; i < len; i++){
562 var subPath = paths[i].replace(trimRe, "");
563 // compile and place in cache
565 cache[subPath] = Ext.DomQuery.compile(subPath);
569 sourceClass: 'Ext.DomQuery',
570 sourceMethod: 'jsSelect',
571 msg: subPath + ' is not a valid selector'
576 var result = cache[subPath](root);
577 if(result && result != document){
578 results = results.concat(result);
582 // if there were multiple selectors, make sure dups
584 if(paths.length > 1){
585 return nodup(results);
590 isXml: function(el) {
591 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
592 return docEl ? docEl.nodeName !== "HTML" : false;
596 * Selects an array of DOM nodes by CSS/XPath selector.
598 * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
599 * {@link Ext.DomQuery#jsSelect} to do the work.
601 * Aliased as {@link Ext#query}.
603 * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
605 * @param {String} path The selector/xpath query
606 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
607 * @return {HTMLElement[]} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
608 * Empty array when no matches.
611 select : document.querySelectorAll ? function(path, root, type) {
612 root = root || document;
614 * Safari 3.x can't handle uppercase or unicode characters when in quirks mode.
616 if (!Ext.DomQuery.isXml(root) && !(Ext.isSafari3 && !Ext.isStrict)) {
619 * This checking here is to "fix" the behaviour of querySelectorAll
620 * for non root document queries. The way qsa works is intentional,
621 * however it's definitely not the expected way it should work.
622 * More info: http://ejohn.org/blog/thoughts-on-queryselectorall/
624 * We only modify the path for single selectors (ie, no multiples),
625 * without a full parser it makes it difficult to do this correctly.
627 var isDocumentRoot = root.nodeType === 9,
631 if (!isDocumentRoot && path.indexOf(',') === -1 && !startIdRe.test(path)) {
632 _path = '#' + Ext.id(root) + ' ' + path;
633 _root = root.parentNode;
635 return Ext.Array.toArray(_root.querySelectorAll(_path));
640 return Ext.DomQuery.jsSelect.call(this, path, root, type);
641 } : function(path, root, type) {
642 return Ext.DomQuery.jsSelect.call(this, path, root, type);
646 * Selects a single element.
647 * @param {String} selector The selector/xpath query
648 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
649 * @return {HTMLElement} The DOM element which matched the selector.
651 selectNode : function(path, root){
652 return Ext.DomQuery.select(path, root)[0];
656 * Selects the value of a node, optionally replacing null with the defaultValue.
657 * @param {String} selector The selector/xpath query
658 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
659 * @param {String} defaultValue (optional) When specified, this is return as empty value.
662 selectValue : function(path, root, defaultValue){
663 path = path.replace(trimRe, "");
664 if(!valueCache[path]){
665 valueCache[path] = Ext.DomQuery.compile(path, "select");
667 var n = valueCache[path](root), v;
670 // overcome a limitation of maximum textnode size
671 // Rumored to potentially crash IE6 but has not been confirmed.
672 // http://reference.sitepoint.com/javascript/Node/normalize
673 // https://developer.mozilla.org/En/DOM/Node.normalize
674 if (typeof n.normalize == 'function') n.normalize();
676 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
677 return ((v === null||v === undefined||v==='') ? defaultValue : v);
681 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
682 * @param {String} selector The selector/xpath query
683 * @param {HTMLElement} root (optional) The start of the query (defaults to document).
684 * @param {Number} defaultValue (optional) When specified, this is return as empty value.
687 selectNumber : function(path, root, defaultValue){
688 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
689 return parseFloat(v);
693 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
694 * @param {String/HTMLElement/HTMLElement[]} el An element id, element or array of elements
695 * @param {String} selector The simple selector to test
698 is : function(el, ss){
699 if(typeof el == "string"){
700 el = document.getElementById(el);
702 var isArray = Ext.isArray(el),
703 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
704 return isArray ? (result.length == el.length) : (result.length > 0);
708 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
709 * @param {HTMLElement[]} el An array of elements to filter
710 * @param {String} selector The simple selector to test
711 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
712 * the selector instead of the ones that match
713 * @return {HTMLElement[]} An Array of DOM elements which match the selector. If there are
714 * no matches, and empty Array is returned.
716 filter : function(els, ss, nonMatches){
717 ss = ss.replace(trimRe, "");
718 if(!simpleCache[ss]){
719 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
721 var result = simpleCache[ss](els);
722 return nonMatches ? quickDiff(result, els) : result;
726 * Collection of matching regular expressions and code snippets.
727 * Each capture group within () will be replace the {} in the select
728 * statement as specified by their index.
732 select: 'n = byClassName(n, " {1} ");'
734 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
735 select: 'n = byPseudo(n, "{1}", "{2}");'
737 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
738 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
741 select: 'n = byId(n, "{1}");'
744 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
749 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
750 * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, > <.
753 "=" : function(a, v){
756 "!=" : function(a, v){
759 "^=" : function(a, v){
760 return a && a.substr(0, v.length) == v;
762 "$=" : function(a, v){
763 return a && a.substr(a.length-v.length) == v;
765 "*=" : function(a, v){
766 return a && a.indexOf(v) !== -1;
768 "%=" : function(a, v){
771 "|=" : function(a, v){
772 return a && (a == v || a.substr(0, v.length+1) == v+'-');
774 "~=" : function(a, v){
775 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
780 Object hash of "pseudo class" filter functions which are used when filtering selections.
781 Each function is passed two parameters:
784 An Array of DOM elements to filter.
787 The argument (if any) supplied in the selector.
789 A filter function returns an Array of DOM elements which conform to the pseudo class.
790 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
791 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
793 For example, to filter `a` elements to only return links to __external__ resources:
795 Ext.DomQuery.pseudos.external = function(c, v){
797 for(var i = 0, ci; ci = c[i]; i++){
798 // Include in result set only if it's a link to an external resource
799 if(ci.hostname != location.hostname){
806 Then external links could be gathered with the following statement:
808 var externalLinks = Ext.select("a:external");
813 "first-child" : function(c){
814 var r = [], ri = -1, n;
815 for(var i = 0, ci; ci = n = c[i]; i++){
816 while((n = n.previousSibling) && n.nodeType != 1);
824 "last-child" : function(c){
825 var r = [], ri = -1, n;
826 for(var i = 0, ci; ci = n = c[i]; i++){
827 while((n = n.nextSibling) && n.nodeType != 1);
835 "nth-child" : function(c, a) {
837 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
838 f = (m[1] || 1) - 0, l = m[2] - 0;
839 for(var i = 0, n; n = c[i]; i++){
840 var pn = n.parentNode;
841 if (batch != pn._batch) {
843 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
844 if(cn.nodeType == 1){
851 if (l == 0 || n.nodeIndex == l){
854 } else if ((n.nodeIndex + l) % f == 0){
862 "only-child" : function(c){
863 var r = [], ri = -1;;
864 for(var i = 0, ci; ci = c[i]; i++){
865 if(!prev(ci) && !next(ci)){
872 "empty" : function(c){
874 for(var i = 0, ci; ci = c[i]; i++){
875 var cns = ci.childNodes, j = 0, cn, empty = true;
878 if(cn.nodeType == 1 || cn.nodeType == 3){
890 "contains" : function(c, v){
892 for(var i = 0, ci; ci = c[i]; i++){
893 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
900 "nodeValue" : function(c, v){
902 for(var i = 0, ci; ci = c[i]; i++){
903 if(ci.firstChild && ci.firstChild.nodeValue == v){
910 "checked" : function(c){
912 for(var i = 0, ci; ci = c[i]; i++){
913 if(ci.checked == true){
920 "not" : function(c, ss){
921 return Ext.DomQuery.filter(c, ss, true);
924 "any" : function(c, selectors){
925 var ss = selectors.split('|'),
927 for(var i = 0, ci; ci = c[i]; i++){
928 for(var j = 0; s = ss[j]; j++){
929 if(Ext.DomQuery.is(ci, s)){
939 return this["nth-child"](c, "odd");
942 "even" : function(c){
943 return this["nth-child"](c, "even");
946 "nth" : function(c, a){
950 "first" : function(c){
954 "last" : function(c){
955 return c[c.length-1] || [];
958 "has" : function(c, ss){
959 var s = Ext.DomQuery.select,
961 for(var i = 0, ci; ci = c[i]; i++){
962 if(s(ss, ci).length > 0){
969 "next" : function(c, ss){
970 var is = Ext.DomQuery.is,
972 for(var i = 0, ci; ci = c[i]; i++){
981 "prev" : function(c, ss){
982 var is = Ext.DomQuery.is,
984 for(var i = 0, ci; ci = c[i]; i++){
997 * Shorthand of {@link Ext.DomQuery#select}
1000 * @alias Ext.DomQuery#select
1002 Ext.query = Ext.DomQuery.select;