DomQuery.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:31k
源码类别:

JavaScript

开发平台:

JavaScript

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