DomQuery.js
上传用户:shuoshiled
上传日期:2018-01-28
资源大小:10124k
文件大小:29k
源码类别:

中间件编程

开发平台:

JavaScript

  1. /*!  * Ext JS Library 3.0.0  * Copyright(c) 2006-2009 Ext JS, LLC  * licensing@extjs.com  * http://www.extjs.com/license  */ /*
  2.  * This is code is also distributed under MIT license for use
  3.  * with jQuery and prototype JavaScript libraries.
  4.  */
  5. /**
  6.  * @class Ext.DomQuery
  7. 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).
  8. <p>
  9. 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>
  10. <p>
  11. 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.
  12. </p>
  13. <h4>Element Selectors:</h4>
  14. <ul class="list">
  15.     <li> <b>*</b> any element</li>
  16.     <li> <b>E</b> an element with the tag E</li>
  17.     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
  18.     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
  19.     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
  20.     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
  21. </ul>
  22. <h4>Attribute Selectors:</h4>
  23. <p>The use of &#64; and quotes are optional. For example, div[&#64;foo='bar'] is also a valid attribute selector.</p>
  24. <ul class="list">
  25.     <li> <b>E[foo]</b> has an attribute "foo"</li>
  26.     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
  27.     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
  28.     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
  29.     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
  30.     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
  31.     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
  32. </ul>
  33. <h4>Pseudo Classes:</h4>
  34. <ul class="list">
  35.     <li> <b>E:first-child</b> E is the first child of its parent</li>
  36.     <li> <b>E:last-child</b> E is the last child of its parent</li>
  37.     <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>
  38.     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
  39.     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
  40.     <li> <b>E:only-child</b> E is the only child of its parent</li>
  41.     <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>
  42.     <li> <b>E:first</b> the first E in the resultset</li>
  43.     <li> <b>E:last</b> the last E in the resultset</li>
  44.     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
  45.     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
  46.     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
  47.     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
  48.     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
  49.     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
  50.     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
  51.     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
  52.     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
  53. </ul>
  54. <h4>CSS Value Selectors:</h4>
  55. <ul class="list">
  56.     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
  57.     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
  58.     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
  59.     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
  60.     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
  61.     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
  62. </ul>
  63.  * @singleton
  64.  */
  65. Ext.DomQuery = function(){
  66.     var cache = {}, 
  67.      simpleCache = {}, 
  68.      valueCache = {},
  69.      nonSpace = /S/,
  70.      trimRe = /^s+|s+$/g,
  71.      tplRe = /{(d+)}/g,
  72.      modeRe = /^(s?[/>+~]s?|s|$)/,
  73.      tagTokenRe = /^(#)?([w-*]+)/,
  74.      nthRe = /(d*)n+?(d*)/, 
  75.      nthRe2 = /D/,
  76.      // This is for IE MSXML which does not support expandos.
  77.     // IE runs the same speed using setAttribute, however FF slows way down
  78.     // and Safari completely fails so they need to continue to use expandos.
  79.     isIE = window.ActiveXObject ? true : false,
  80.         isOpera = Ext.isOpera,
  81.     key = 30803;
  82.     
  83.     // this eval is stop the compressor from
  84. // renaming the variable to something shorter
  85. eval("var batch = 30803;");    
  86.     function child(p, index){
  87.         var i = 0,
  88.          n = p.firstChild;
  89.         while(n){
  90.             if(n.nodeType == 1){
  91.                if(++i == index){
  92.                    return n;
  93.                }
  94.             }
  95.             n = n.nextSibling;
  96.         }
  97.         return null;
  98.     };
  99.     function next(n){
  100.         while((n = n.nextSibling) && n.nodeType != 1);
  101.         return n;
  102.     };
  103.     function prev(n){
  104.         while((n = n.previousSibling) && n.nodeType != 1);
  105.         return n;
  106.     };
  107.     function children(d){
  108.         var n = d.firstChild, ni = -1,
  109.          nx;
  110.       while(n){
  111.           nx = n.nextSibling;
  112.           if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
  113.               d.removeChild(n);
  114.           }else{
  115.               n.nodeIndex = ++ni;
  116.           }
  117.           n = nx;
  118.       }
  119.       return this;
  120.   };
  121.     function byClassName(c, a, v){
  122.         if(!v){
  123.             return c;
  124.         }
  125.         var r = [], ri = -1, cn;
  126.         for(var i = 0, ci; ci = c[i]; i++){
  127.             if((' '+ci.className+' ').indexOf(v) != -1){
  128.                 r[++ri] = ci;
  129.             }
  130.         }
  131.         return r;
  132.     };
  133.     function attrValue(n, attr){
  134.         if(!n.tagName && typeof n.length != "undefined"){
  135.             n = n[0];
  136.         }
  137.         if(!n){
  138.             return null;
  139.         }
  140.         if(attr == "for"){
  141.             return n.htmlFor;
  142.         }
  143.         if(attr == "class" || attr == "className"){
  144.             return n.className;
  145.         }
  146.         return n.getAttribute(attr) || n[attr];
  147.     };
  148.     function getNodes(ns, mode, tagName){
  149.         var result = [], ri = -1, cs;
  150.         if(!ns){
  151.             return result;
  152.         }
  153.         tagName = tagName || "*";
  154.         if(typeof ns.getElementsByTagName != "undefined"){
  155.             ns = [ns];
  156.         }
  157.         if(!mode){
  158.             for(var i = 0, ni; ni = ns[i]; i++){
  159.                 cs = ni.getElementsByTagName(tagName);
  160.                 for(var j = 0, ci; ci = cs[j]; j++){
  161.                     result[++ri] = ci;
  162.                 }
  163.             }
  164.         }else if(mode == "/" || mode == ">"){
  165.             var utag = tagName.toUpperCase();
  166.             for(var i = 0, ni, cn; ni = ns[i]; i++){
  167.                 cn = isOpera ? ni.childNodes : (ni.children || ni.childNodes);
  168.                 for(var j = 0, cj; cj = cn[j]; j++){
  169.                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
  170.                         result[++ri] = cj;
  171.                     }
  172.                 }
  173.             }
  174.         }else if(mode == "+"){
  175.             var utag = tagName.toUpperCase();
  176.             for(var i = 0, n; n = ns[i]; i++){
  177.                 while((n = n.nextSibling) && n.nodeType != 1);
  178.                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
  179.                     result[++ri] = n;
  180.                 }
  181.             }
  182.         }else if(mode == "~"){
  183.             var utag = tagName.toUpperCase();
  184.             for(var i = 0, n; n = ns[i]; i++){
  185.                 while((n = n.nextSibling)){
  186.                     if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
  187.                         result[++ri] = n;
  188.                     }
  189.                 }
  190.             }
  191.         }
  192.         return result;
  193.     };
  194.     function concat(a, b){
  195.         if(b.slice){
  196.             return a.concat(b);
  197.         }
  198.         for(var i = 0, l = b.length; i < l; i++){
  199.             a[a.length] = b[i];
  200.         }
  201.         return a;
  202.     }
  203.     function byTag(cs, tagName){
  204.         if(cs.tagName || cs == document){
  205.             cs = [cs];
  206.         }
  207.         if(!tagName){
  208.             return cs;
  209.         }
  210.         var r = [], ri = -1;
  211.         tagName = tagName.toLowerCase();
  212.         for(var i = 0, ci; ci = cs[i]; i++){
  213.             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
  214.                 r[++ri] = ci;
  215.             }
  216.         }
  217.         return r;
  218.     };
  219.     function byId(cs, attr, id){
  220.         if(cs.tagName || cs == document){
  221.             cs = [cs];
  222.         }
  223.         if(!id){
  224.             return cs;
  225.         }
  226.         var r = [], ri = -1;
  227.         for(var i = 0,ci; ci = cs[i]; i++){
  228.             if(ci && ci.id == id){
  229.                 r[++ri] = ci;
  230.                 return r;
  231.             }
  232.         }
  233.         return r;
  234.     };
  235.     function byAttribute(cs, attr, value, op, custom){
  236.         var r = [], 
  237.          ri = -1, 
  238.          st = custom=="{",
  239.          f = Ext.DomQuery.operators[op];
  240.         for(var i = 0, ci; ci = cs[i]; i++){
  241.             if(ci.nodeType != 1){
  242.                 continue;
  243.             }
  244.             var a;
  245.             if(st){
  246.                 a = Ext.DomQuery.getStyle(ci, attr);
  247.             }
  248.             else if(attr == "class" || attr == "className"){
  249.                 a = ci.className;
  250.             }else if(attr == "for"){
  251.                 a = ci.htmlFor;
  252.             }else if(attr == "href"){
  253.                 a = ci.getAttribute("href", 2);
  254.             }else{
  255.                 a = ci.getAttribute(attr);
  256.             }
  257.             if((f && f(a, value)) || (!f && a)){
  258.                 r[++ri] = ci;
  259.             }
  260.         }
  261.         return r;
  262.     };
  263.     function byPseudo(cs, name, value){
  264.         return Ext.DomQuery.pseudos[name](cs, value);
  265.     };
  266.     function nodupIEXml(cs){
  267.         var d = ++key, 
  268.          r;
  269.         cs[0].setAttribute("_nodup", d);
  270.         r = [cs[0]];
  271.         for(var i = 1, len = cs.length; i < len; i++){
  272.             var c = cs[i];
  273.             if(!c.getAttribute("_nodup") != d){
  274.                 c.setAttribute("_nodup", d);
  275.                 r[r.length] = c;
  276.             }
  277.         }
  278.         for(var i = 0, len = cs.length; i < len; i++){
  279.             cs[i].removeAttribute("_nodup");
  280.         }
  281.         return r;
  282.     }
  283.     function nodup(cs){
  284.         if(!cs){
  285.             return [];
  286.         }
  287.         var len = cs.length, c, i, r = cs, cj, ri = -1;
  288.         if(!len || typeof cs.nodeType != "undefined" || len == 1){
  289.             return cs;
  290.         }
  291.         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
  292.             return nodupIEXml(cs);
  293.         }
  294.         var d = ++key;
  295.         cs[0]._nodup = d;
  296.         for(i = 1; c = cs[i]; i++){
  297.             if(c._nodup != d){
  298.                 c._nodup = d;
  299.             }else{
  300.                 r = [];
  301.                 for(var j = 0; j < i; j++){
  302.                     r[++ri] = cs[j];
  303.                 }
  304.                 for(j = i+1; cj = cs[j]; j++){
  305.                     if(cj._nodup != d){
  306.                         cj._nodup = d;
  307.                         r[++ri] = cj;
  308.                     }
  309.                 }
  310.                 return r;
  311.             }
  312.         }
  313.         return r;
  314.     }
  315.     function quickDiffIEXml(c1, c2){
  316.         var d = ++key,
  317.          r = [];
  318.         for(var i = 0, len = c1.length; i < len; i++){
  319.             c1[i].setAttribute("_qdiff", d);
  320.         }        
  321.         for(var i = 0, len = c2.length; i < len; i++){
  322.             if(c2[i].getAttribute("_qdiff") != d){
  323.                 r[r.length] = c2[i];
  324.             }
  325.         }
  326.         for(var i = 0, len = c1.length; i < len; i++){
  327.            c1[i].removeAttribute("_qdiff");
  328.         }
  329.         return r;
  330.     }
  331.     function quickDiff(c1, c2){
  332.         var len1 = c1.length,
  333.          d = ++key,
  334.          r = [];
  335.         if(!len1){
  336.             return c2;
  337.         }
  338.         if(isIE && c1[0].selectSingleNode){
  339.             return quickDiffIEXml(c1, c2);
  340.         }        
  341.         for(var i = 0; i < len1; i++){
  342.             c1[i]._qdiff = d;
  343.         }        
  344.         for(var i = 0, len = c2.length; i < len; i++){
  345.             if(c2[i]._qdiff != d){
  346.                 r[r.length] = c2[i];
  347.             }
  348.         }
  349.         return r;
  350.     }
  351.     function quickId(ns, mode, root, id){
  352.         if(ns == root){
  353.            var d = root.ownerDocument || root;
  354.            return d.getElementById(id);
  355.         }
  356.         ns = getNodes(ns, mode, "*");
  357.         return byId(ns, null, id);
  358.     }
  359.     return {
  360.         getStyle : function(el, name){
  361.             return Ext.fly(el).getStyle(name);
  362.         },
  363.         /**
  364.          * Compiles a selector/xpath query into a reusable function. The returned function
  365.          * takes one parameter "root" (optional), which is the context node from where the query should start.
  366.          * @param {String} selector The selector/xpath query
  367.          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
  368.          * @return {Function}
  369.          */
  370.         compile : function(path, type){
  371.             type = type || "select";
  372.             var fn = ["var f = function(root){n var mode; ++batch; var n = root || document;n"],
  373.              q = path, mode, lq,
  374.              tk = Ext.DomQuery.matchers,
  375.              tklen = tk.length,
  376.              mm,
  377.              // accept leading mode switch
  378.              lmode = q.match(modeRe);
  379.             
  380.             if(lmode && lmode[1]){
  381.                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
  382.                 q = q.replace(lmode[1], "");
  383.             }
  384.             // strip leading slashes
  385.             while(path.substr(0, 1)=="/"){
  386.                 path = path.substr(1);
  387.             }
  388.             while(q && lq != q){
  389.                 lq = q;
  390.                 var tm = q.match(tagTokenRe);
  391.                 if(type == "select"){
  392.                     if(tm){
  393.                         if(tm[1] == "#"){
  394.                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
  395.                         }else{
  396.                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
  397.                         }
  398.                         q = q.replace(tm[0], "");
  399.                     }else if(q.substr(0, 1) != '@'){
  400.                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
  401.                     }
  402.                 }else{
  403.                     if(tm){
  404.                         if(tm[1] == "#"){
  405.                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
  406.                         }else{
  407.                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
  408.                         }
  409.                         q = q.replace(tm[0], "");
  410.                     }
  411.                 }
  412.                 while(!(mm = q.match(modeRe))){
  413.                     var matched = false;
  414.                     for(var j = 0; j < tklen; j++){
  415.                         var t = tk[j];
  416.                         var m = q.match(t.re);
  417.                         if(m){
  418.                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
  419.                                                     return m[i];
  420.                                                 });
  421.                             q = q.replace(m[0], "");
  422.                             matched = true;
  423.                             break;
  424.                         }
  425.                     }
  426.                     // prevent infinite loop on bad selector
  427.                     if(!matched){
  428.                         throw 'Error parsing selector, parsing failed at "' + q + '"';
  429.                     }
  430.                 }
  431.                 if(mm[1]){
  432.                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
  433.                     q = q.replace(mm[1], "");
  434.                 }
  435.             }
  436.             fn[fn.length] = "return nodup(n);n}";
  437.             eval(fn.join(""));
  438.             return f;
  439.         },
  440.         /**
  441.          * Selects a group of elements.
  442.          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
  443.          * @param {Node} root (optional) The start of the query (defaults to document).
  444.          * @return {Array} An Array of DOM elements which match the selector. If there are
  445.          * no matches, and empty Array is returned.
  446.          */
  447.         select : function(path, root, type){
  448.             if(!root || root == document){
  449.                 root = document;
  450.             }
  451.             if(typeof root == "string"){
  452.                 root = document.getElementById(root);
  453.             }
  454.             var paths = path.split(","),
  455.              results = [];
  456.             for(var i = 0, len = paths.length; i < len; i++){
  457.                 var p = paths[i].replace(trimRe, "");
  458.                 if(!cache[p]){
  459.                     cache[p] = Ext.DomQuery.compile(p);
  460.                     if(!cache[p]){
  461.                         throw p + " is not a valid selector";
  462.                     }
  463.                 }
  464.                 var result = cache[p](root);
  465.                 if(result && result != document){
  466.                     results = results.concat(result);
  467.                 }
  468.             }
  469.             if(paths.length > 1){
  470.                 return nodup(results);
  471.             }
  472.             return results;
  473.         },
  474.         /**
  475.          * Selects a single element.
  476.          * @param {String} selector The selector/xpath query
  477.          * @param {Node} root (optional) The start of the query (defaults to document).
  478.          * @return {Element} The DOM element which matched the selector.
  479.          */
  480.         selectNode : function(path, root){
  481.             return Ext.DomQuery.select(path, root)[0];
  482.         },
  483.         /**
  484.          * Selects the value of a node, optionally replacing null with the defaultValue.
  485.          * @param {String} selector The selector/xpath query
  486.          * @param {Node} root (optional) The start of the query (defaults to document).
  487.          * @param {String} defaultValue
  488.          * @return {String}
  489.          */
  490.         selectValue : function(path, root, defaultValue){
  491.             path = path.replace(trimRe, "");
  492.             if(!valueCache[path]){
  493.                 valueCache[path] = Ext.DomQuery.compile(path, "select");
  494.             }
  495.             var n = valueCache[path](root),
  496.              v;
  497.             n = n[0] ? n[0] : n;
  498.             v = (n && n.firstChild ? n.firstChild.nodeValue : null);
  499.             return ((v === null||v === undefined||v==='') ? defaultValue : v);
  500.         },
  501.         /**
  502.          * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
  503.          * @param {String} selector The selector/xpath query
  504.          * @param {Node} root (optional) The start of the query (defaults to document).
  505.          * @param {Number} defaultValue
  506.          * @return {Number}
  507.          */
  508.         selectNumber : function(path, root, defaultValue){
  509.             var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
  510.             return parseFloat(v);
  511.         },
  512.         /**
  513.          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
  514.          * @param {String/HTMLElement/Array} el An element id, element or array of elements
  515.          * @param {String} selector The simple selector to test
  516.          * @return {Boolean}
  517.          */
  518.         is : function(el, ss){
  519.             if(typeof el == "string"){
  520.                 el = document.getElementById(el);
  521.             }
  522.             var isArray = Ext.isArray(el),
  523.              result = Ext.DomQuery.filter(isArray ? el : [el], ss);
  524.             return isArray ? (result.length == el.length) : (result.length > 0);
  525.         },
  526.         /**
  527.          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
  528.          * @param {Array} el An array of elements to filter
  529.          * @param {String} selector The simple selector to test
  530.          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
  531.          * the selector instead of the ones that match
  532.          * @return {Array} An Array of DOM elements which match the selector. If there are
  533.          * no matches, and empty Array is returned.
  534.          */
  535.         filter : function(els, ss, nonMatches){
  536.             ss = ss.replace(trimRe, "");
  537.             if(!simpleCache[ss]){
  538.                 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
  539.             }
  540.             var result = simpleCache[ss](els);
  541.             return nonMatches ? quickDiff(result, els) : result;
  542.         },
  543.         /**
  544.          * Collection of matching regular expressions and code snippets.
  545.          */
  546.         matchers : [{
  547.                 re: /^.([w-]+)/,
  548.                 select: 'n = byClassName(n, null, " {1} ");'
  549.             }, {
  550.                 re: /^:([w-]+)(?:(((?:[^s>/]*|.*?))))?/,
  551.                 select: 'n = byPseudo(n, "{1}", "{2}");'
  552.             },{
  553.                 re: /^(?:([[{])(?:@)?([w-]+)s?(?:(=|.=)s?['"]?(.*?)["']?)?[]}])/,
  554.                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
  555.             }, {
  556.                 re: /^#([w-]+)/,
  557.                 select: 'n = byId(n, null, "{1}");'
  558.             },{
  559.                 re: /^@([w-]+)/,
  560.                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
  561.             }
  562.         ],
  563.         /**
  564.          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
  565.          * 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, &gt; &lt;.
  566.          */
  567.         operators : {
  568.             "=" : function(a, v){
  569.                 return a == v;
  570.             },
  571.             "!=" : function(a, v){
  572.                 return a != v;
  573.             },
  574.             "^=" : function(a, v){
  575.                 return a && a.substr(0, v.length) == v;
  576.             },
  577.             "$=" : function(a, v){
  578.                 return a && a.substr(a.length-v.length) == v;
  579.             },
  580.             "*=" : function(a, v){
  581.                 return a && a.indexOf(v) !== -1;
  582.             },
  583.             "%=" : function(a, v){
  584.                 return (a % v) == 0;
  585.             },
  586.             "|=" : function(a, v){
  587.                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
  588.             },
  589.             "~=" : function(a, v){
  590.                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
  591.             }
  592.         },
  593.         /**
  594.          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
  595.          * and the argument (if any) supplied in the selector.
  596.          */
  597.         pseudos : {
  598.             "first-child" : function(c){
  599.                 var r = [], ri = -1, n;
  600.                 for(var i = 0, ci; ci = n = c[i]; i++){
  601.                     while((n = n.previousSibling) && n.nodeType != 1);
  602.                     if(!n){
  603.                         r[++ri] = ci;
  604.                     }
  605.                 }
  606.                 return r;
  607.             },
  608.             "last-child" : function(c){
  609.                 var r = [], ri = -1, n;
  610.                 for(var i = 0, ci; ci = n = c[i]; i++){
  611.                     while((n = n.nextSibling) && n.nodeType != 1);
  612.                     if(!n){
  613.                         r[++ri] = ci;
  614.                     }
  615.                 }
  616.                 return r;
  617.             },
  618.             "nth-child" : function(c, a) {
  619.                 var r = [], ri = -1,
  620.                  m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
  621.                  f = (m[1] || 1) - 0, l = m[2] - 0;
  622.                 for(var i = 0, n; n = c[i]; i++){
  623.                     var pn = n.parentNode;
  624.                     if (batch != pn._batch) {
  625.                         var j = 0;
  626.                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
  627.                             if(cn.nodeType == 1){
  628.                                cn.nodeIndex = ++j;
  629.                             }
  630.                         }
  631.                         pn._batch = batch;
  632.                     }
  633.                     if (f == 1) {
  634.                         if (l == 0 || n.nodeIndex == l){
  635.                             r[++ri] = n;
  636.                         }
  637.                     } else if ((n.nodeIndex + l) % f == 0){
  638.                         r[++ri] = n;
  639.                     }
  640.                 }
  641.                 return r;
  642.             },
  643.             "only-child" : function(c){
  644.                 var r = [], ri = -1;;
  645.                 for(var i = 0, ci; ci = c[i]; i++){
  646.                     if(!prev(ci) && !next(ci)){
  647.                         r[++ri] = ci;
  648.                     }
  649.                 }
  650.                 return r;
  651.             },
  652.             "empty" : function(c){
  653.                 var r = [], ri = -1;
  654.                 for(var i = 0, ci; ci = c[i]; i++){
  655.                     var cns = ci.childNodes, j = 0, cn, empty = true;
  656.                     while(cn = cns[j]){
  657.                         ++j;
  658.                         if(cn.nodeType == 1 || cn.nodeType == 3){
  659.                             empty = false;
  660.                             break;
  661.                         }
  662.                     }
  663.                     if(empty){
  664.                         r[++ri] = ci;
  665.                     }
  666.                 }
  667.                 return r;
  668.             },
  669.             "contains" : function(c, v){
  670.                 var r = [], ri = -1;
  671.                 for(var i = 0, ci; ci = c[i]; i++){
  672.                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
  673.                         r[++ri] = ci;
  674.                     }
  675.                 }
  676.                 return r;
  677.             },
  678.             "nodeValue" : function(c, v){
  679.                 var r = [], ri = -1;
  680.                 for(var i = 0, ci; ci = c[i]; i++){
  681.                     if(ci.firstChild && ci.firstChild.nodeValue == v){
  682.                         r[++ri] = ci;
  683.                     }
  684.                 }
  685.                 return r;
  686.             },
  687.             "checked" : function(c){
  688.                 var r = [], ri = -1;
  689.                 for(var i = 0, ci; ci = c[i]; i++){
  690.                     if(ci.checked == true){
  691.                         r[++ri] = ci;
  692.                     }
  693.                 }
  694.                 return r;
  695.             },
  696.             "not" : function(c, ss){
  697.                 return Ext.DomQuery.filter(c, ss, true);
  698.             },
  699.             "any" : function(c, selectors){
  700.                 var ss = selectors.split('|'),
  701.                  r = [], ri = -1, s;
  702.                 for(var i = 0, ci; ci = c[i]; i++){
  703.                     for(var j = 0; s = ss[j]; j++){
  704.                         if(Ext.DomQuery.is(ci, s)){
  705.                             r[++ri] = ci;
  706.                             break;
  707.                         }
  708.                     }
  709.                 }
  710.                 return r;
  711.             },
  712.             "odd" : function(c){
  713.                 return this["nth-child"](c, "odd");
  714.             },
  715.             "even" : function(c){
  716.                 return this["nth-child"](c, "even");
  717.             },
  718.             "nth" : function(c, a){
  719.                 return c[a-1] || [];
  720.             },
  721.             "first" : function(c){
  722.                 return c[0] || [];
  723.             },
  724.             "last" : function(c){
  725.                 return c[c.length-1] || [];
  726.             },
  727.             "has" : function(c, ss){
  728.                 var s = Ext.DomQuery.select,
  729.                  r = [], ri = -1;
  730.                 for(var i = 0, ci; ci = c[i]; i++){
  731.                     if(s(ss, ci).length > 0){
  732.                         r[++ri] = ci;
  733.                     }
  734.                 }
  735.                 return r;
  736.             },
  737.             "next" : function(c, ss){
  738.                 var is = Ext.DomQuery.is,
  739.                  r = [], ri = -1;
  740.                 for(var i = 0, ci; ci = c[i]; i++){
  741.                     var n = next(ci);
  742.                     if(n && is(n, ss)){
  743.                         r[++ri] = ci;
  744.                     }
  745.                 }
  746.                 return r;
  747.             },
  748.             "prev" : function(c, ss){
  749.                 var is = Ext.DomQuery.is,
  750.                  r = [], ri = -1;
  751.                 for(var i = 0, ci; ci = c[i]; i++){
  752.                     var n = prev(ci);
  753.                     if(n && is(n, ss)){
  754.                         r[++ri] = ci;
  755.                     }
  756.                 }
  757.                 return r;
  758.             }
  759.         }
  760.     };
  761. }();
  762. /**
  763.  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
  764.  * @param {String} path The selector/xpath query
  765.  * @param {Node} root (optional) The start of the query (defaults to document).
  766.  * @return {Array}
  767.  * @member Ext
  768.  * @method query
  769.  */
  770. Ext.query = Ext.DomQuery.select;