Ext.DomQuery源码

/*
 * author:prk
 * date:2008-08-03
 * comment:analyse for Ext.DomQuery 
 * 
 */
/*
 * Ext JS Library 2.0.2
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */

/*
 * This is code is also distributed under MIT license for use
 * with jQuery and prototype JavaScript libraries.
 */
/**
 * @class Ext.DomQuery
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).
<p>
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>

<p>
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.
</p>
<h4>Element Selectors:</h4>
<ul class="list">
    <li> <b>*</b> any element</li>
    <li> <b>E</b> an element with the tag E</li>
    <li> <b>E F</b> All descendent elements of E that have the tag F</li>
    <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
    <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
    <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
</ul>
<h4>Attribute Selectors:</h4>
<p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
<ul class="list">
    <li> <b>E[foo]</b> has an attribute "foo"</li>
    <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
    <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
    <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
    <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
    <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
    <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
</ul>
<h4>Pseudo Classes:</h4>
<ul class="list">
    <li> <b>E:first-child</b> E is the first child of its parent</li>
    <li> <b>E:last-child</b> E is the last child of its parent</li>
    <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>
    <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
    <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
    <li> <b>E:only-child</b> E is the only child of its parent</li>
    <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>
    <li> <b>E:first</b> the first E in the resultset</li>
    <li> <b>E:last</b> the last E in the resultset</li>
    <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
    <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
    <li> <b>E:even</b> shortcut for :nth-child(even)</li>
    <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
    <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
    <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
    <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
    <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
    <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
</ul>
<h4>CSS Value Selectors:</h4>
<ul class="list">
    <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
    <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
    <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
    <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
    <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
    <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
</ul>
 * @singleton
 */
 
 /*
    Element Node.ELEMENT_NODE 1 
    Text Node.TEXT_NODE 3 
    Document Node.DOCUMENT_NODE 9 
    Comment Node.COMMENT_NODE 8 
    DocumentFragment Node.DOCUMENT_FRAGMENT_NODE 11 
    Attr Node.ATTRIBUTE_NODE 2
  */
Ext.DomQuery = function(){
    var cache = {}, simpleCache = {}, valueCache = {};
    var nonSpace = /\S/;
    var trimRe = /^\s+|\s+$/g;
    //{2}
    var tplRe = /\{(\d+)\}/g;
    //' [/>+~] ' | ' ' | ''
    var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
    // '#xxx'|'*'|p|a
    var tagTokenRe = /^(#)?([\w-\*]+)/;
    //3 n+ 3  not digit  
    var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
    
     //E:nth-child(n) 找到p的第index个孩子节点(element)
    function child(p, index){
        var i = 0;
        var n = p.firstChild;
        while(n){
        	//nodeType == 1:Element
            if(n.nodeType == 1){
               if(++i == index){
                   return n;
               }
            }
            n = n.nextSibling;
        }
        return null;
    };
    
    //E:next(S) 找到n的下一个节点(element)
    function next(n){
        while((n = n.nextSibling) && n.nodeType != 1);
        return n;
    };
    
   //E:prev(S)  找到n的前一个节点(element)
    function prev(n){
        while((n = n.previousSibling) && n.nodeType != 1);
        return n;
    };
    
     //为节点的子节点标上顺序号,同时删除空白字符的节点
    function children(d){
        var n = d.firstChild, ni = -1;
 	    while(n){
 	        var nx = n.nextSibling;
 	        //Text 类型同时文本为空
 	        if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
 	        	//删除空白字符的节点。FF,IE兼容
 	            d.removeChild(n);
 	        }else{
 	        	//为节点加上index
 	            n.nodeIndex = ++ni;
 	        }
 	        n = nx;
 	    }
 	    return this;
 	};
    //c:element, v:value=class   a:attr没用到   byClassName(n,null,"foo")
 	//通过class名字在C中找class相同或相容的所有元素
    function byClassName(c, a, v){
       //value is not exist
    	if(!v){
            return c;
        }
        var r = [], ri = -1, cn;
        for(var i = 0, ci; ci = c[i]; i++){
        	 //v在不在ci.className中?
            if((' '+ci.className+' ').indexOf(v) != -1){
                r[++ri] = ci;
            }
        }
        return r;
    };
   // n: [{tagName:div,htmlFor:xxx,className:xxx}]  node
    //从n[0]中元素找到attr的属性值
    function attrValue(n, attr){
        if(!n.tagName && typeof n.length != "undefined"){
            n = n[0];
        }
        if(!n){
            return null;
        }
        if(attr == "for"){
            return n.htmlFor;
        }
        if(attr == "class" || attr == "className"){
            return n.className;
        }
        //n可以是Element,也可以是{}
        return n.getAttribute(attr) || n[attr];

    };
     
    //ns ;nodes   Element Selectors
    //通过tagName在ns中找tagName与之相同且符合mode运算条件的所有元素
    function getNodes(ns, mode, tagName){
        var result = [], ri = -1, cs;
        if(!ns){
            return result;
        }
        tagName = tagName || "*";
        //支持单个元素,转换成统一的数组形式,便于操作
        if(typeof ns.getElementsByTagName != "undefined"){
            ns = [ns];//转换成数组
        }
        //mode不存在或为空等,返回其下所有的与tagName相同的元素
        if(!mode){
            for(var i = 0, ni; ni = ns[i]; i++){
            	//找到ni下面所有tagName的子标签
                cs = ni.getElementsByTagName(tagName);
                //把找到的所有cs的Element存在result
                for(var j = 0, ci; ci = cs[j]; j++){
                    result[++ri] = ci;
                }
            }
        }
        // E > F or E/F 返回e所有tagName相同的直接子节点或全部子节点      
        else if(mode == "/" || mode == ">"){
            var utag = tagName.toUpperCase();
            for(var i = 0, ni, cn; ni = ns[i]; i++){
            	//ni的所有直接子节点,IE/FF兼容,
                cn = ni.children || ni.childNodes;
                for(var j = 0, cj; cj = cn[j]; j++){
                	//tagName相等或tagName == '*'
                    if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
                        //把找到的所有cs的Element存在result
                        result[++ri] = cj;
                    }
                }
            }
        }
        // E + F 返回e的第一个后续兄弟节点(element)。其tagName必须等于F或*。        
        else if(mode == "+"){
            var utag = tagName.toUpperCase();
            for(var i = 0, n; n = ns[i]; i++){
            	//n的下一个兄弟不是element的,
                while((n = n.nextSibling) && n.nodeType != 1);
                //找到n的下一个兄弟是Element的。
                if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
                    result[++ri] = n;
                }
            }
        }
         //E ~ F 返回e的所有后续兄弟节点(element),其tagName必须等于F或*。  
        ///E节点后面的所有兄弟节点F
        else if(mode == "~"){
            for(var i = 0, n; n = ns[i]; i++){
            	//下一个兄弟存在,同时不是元素或tagName == '*'或tagName不相同
                while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
               
                //下一个兄弟存在,是元素同时(tagName != '*'或tagName相同)
                if(n){
                    result[++ri] = n;
                }
            }
        }
        return result;
    };
   
    function concat(a, b){
    	//有可能是节点集合,node Collection(getElementsByTagName),  arguments  is array-like  .    	          
        if(b.slice){
            return a.concat(b);
        }
        //把b中所有元素追加到a中。
        for(var i = 0, l = b.length; i < l; i++){
            a[a.length] = b[i];
        }
        return a;
    }
    //通过tagName在cs中找到与之相同tagName的所有元素
    function byTag(cs, tagName){
        if(cs.tagName || cs == document){
            cs = [cs];
        }
        if(!tagName){
            return cs;
        }
        var r = [], ri = -1;
        tagName = tagName.toLowerCase();
        for(var i = 0, ci; ci = cs[i]; i++){
            if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
                r[++ri] = ci;
            }
        }
        return r;
    };
    
 
  //通过id在cs中找到与之相同的id的所有元素
    function byId(cs, attr, id){
        if(cs.tagName || cs == document){
            cs = [cs];
        }
        if(!id){
            return cs;
        }
        var r = [], ri = -1;
        for(var i = 0,ci; ci = cs[i]; i++){
            if(ci && ci.id == id){
                r[++ri] = ci;
                return r;
            }
        }
        return r;
    };
    //CSS selector,and attribute Selector   
    //通过CSS属性或自身的属性找到value相同且符合op操作关系的所有CS中元素
    function byAttribute(cs, attr, value, op, custom){
        var r = [], ri = -1, st = custom=="{";//表示从style中取属性做比较,CSS Value Selectors
        var f = Ext.DomQuery.operators[op];//Dom的操作符
        for(var i = 0, ci; ci = cs[i]; i++){
            var a;
           //找到attr的属性值
            if(st){//CSS Value Selectors
                a = Ext.DomQuery.getStyle(ci, attr);
            }
            //纯的Attribute Selectors,
            else if(attr == "class" || attr == "className"){
                a = ci.className;
            }else if(attr == "for"){
                a = ci.htmlFor;
            }else if(attr == "href"){
                a = ci.getAttribute("href", 2);
            }else{
                a = ci.getAttribute(attr);
            }
            //如果能找到操作符,说明不是单一属性,不能彩采用找到attr的属性值来做比较
            //如果能找到操作符,运算看看是否满足条件,满足的话,就表明当前的ci是要寻找的。
            //如果不能找到操作符,说明采用attr的属性名来做比较
            if((f && f(a, value)) || (!f && a)){
                r[++ri] = ci;
            }
        }
        return r;
    };

    function byPseudo(cs, name, value){
        return Ext.DomQuery.pseudos[name](cs, value);
    };

    // This is for IE MSXML which does not support expandos.
    // IE runs the same speed using setAttribute, however FF slows way down
    // and Safari completely fails so they need to continue to use expandos.
    var isIE = window.ActiveXObject ? true : false;

    // this eval is stop the compressor from
    // renaming the variable to something shorter
    eval("var batch = 30803;");

    var key = 30803;
    
    //找到不重复的元素,重新组成数组返回,IE为元素加属性只能通过setAttribute
    function nodupIEXml(cs){
        var d = ++key;
        cs[0].setAttribute("_nodup", d);
        var r = [cs[0]];
        for(var i = 1, len = cs.length; i < len; i++){
            var c = cs[i];
            if(!c.getAttribute("_nodup") != d){
                c.setAttribute("_nodup", d);
                r[r.length] = c;
            }
        }
        //去掉已经加上的属性
        for(var i = 0, len = cs.length; i < len; i++){
            cs[i].removeAttribute("_nodup");
        }
        return r;
    }
    //在数组中取不重复的元素的集合
    function nodup(cs){
        if(!cs){
            return [];
        }
        var len = cs.length, c, i, r = cs, cj, ri = -1;
        //elements no length or length==1
        if(!len || typeof cs.nodeType != "undefined" || len == 1){
            return cs;
        }
        if(isIE && typeof cs[0].selectSingleNode != "undefined"){
            return nodupIEXml(cs);
        }
        var d = ++key;
        //cs[0]的元素有可能在Cs中多处出现,也就是说数组中不是唯一的元素。
        cs[0]._nodup = d;
        for(i = 1; c = cs[i]; i++){
            if(c._nodup != d){
                c._nodup = d;
            }else{
                r = [];
                //找到c._nodup = d的元素前面的所有元素,存起来
                for(var j = 0; j < i; j++){
                    r[++ri] = cs[j];
                }
                //跳过c._nodup==d的元素
                //找到c._nodup = d的元素后面的所有元素,如果其_nodup != d,设定为d,存起来
                for(j = i+1; cj = cs[j]; j++){
                    if(cj._nodup != d){
                        cj._nodup = d;
                        r[++ri] = cj;
                    }
                }
                return r;
            }
        }
        return r;
    }
    
     //针对IE的操作
    function quickDiffIEXml(c1, c2){
        var d = ++key;
        for(var i = 0, len = c1.length; i < len; i++){
            c1[i].setAttribute("_qdiff", d);
        }
        var r = [];
        for(var i = 0, len = c2.length; i < len; i++){
            if(c2[i].getAttribute("_qdiff") != d){
                r[r.length] = c2[i];
            }
        }
        for(var i = 0, len = c1.length; i < len; i++){
           c1[i].removeAttribute("_qdiff");
        }
        return r;
    }
    //返回C2中不在C1中出现过的元素
    function quickDiff(c1, c2){
        var len1 = c1.length;
        if(!len1){
            return c2;
        }
        if(isIE && c1[0].selectSingleNode){
            return quickDiffIEXml(c1, c2);
        }
        var d = ++key;
        for(var i = 0; i < len1; i++){
        	//因为c1[i]是元素,也就是为元素设值
            c1[i]._qdiff = d;
        }
        //如果c2中元素在c1中出现过,那么值就会相等
        var r = [];
        for(var i = 0, len = c2.length; i < len; i++){
            if(c2[i]._qdiff != d){
                r[r.length] = c2[i];
            }
        }
        return r;
    }
   
    //快速通过Id找到elements
    function quickId(ns, mode, root, id){
        if(ns == root){
           var d = root.ownerDocument || root;
           return d.getElementById(id);
        }
        //从ns找到所有符合mode关系的元素
        ns = getNodes(ns, mode, "*");
        return byId(ns, null, id);
    }

    return {
    	//获得el的style中为name的属性值
        getStyle : function(el, name){
            return Ext.fly(el).getStyle(name);
        },
        /**
         * Compiles a selector/xpath query into a reusable function. The returned function
         * takes one parameter "root" (optional), which is the context node from where the query should start.
         * @param {String} selector The selector/xpath query
         * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
         * @return {Function}
         */
        
        /**
         * div.foo:nth-child(odd)[@foo=bar]#aId{display=none} > a
         * 
         * fn's content:
         * var f = function(root){
         * var mode; ++batch;
         * var n = root || document;
         * n = getNodes(n, mode, "div");
         * n = byClassName(n, null, "foo");
         * n = byPseudo(n, "nth-child", "odd");
         * n = byAttribute(n, "foo", "bar", "=", "[");
         * n = quickId(n, mode, root, "aId");
         * n = byAttribute(n, "display", "none", "=", "{");
         * mode=">";
         * n = getNodes(n, mode, "a");
         * return nodup(n);
         * }
         */
        compile : function(path, type){
        	
        	//默认是select
            type = type || "select";

            var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
            var q = path, mode, lq;
            var tk = Ext.DomQuery.matchers;
            var tklen = tk.length;
            var mm;
                      
            // accept leading mode switch            
            //var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;  element selector           
            var lmode = q.match(modeRe);
            if(lmode && lmode[1]){            	
                fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';               
                q = q.replace(lmode[1], "");
            }            
            // strip leading slashes
            while(path.substr(0, 1)=="/"){
                path = path.substr(1);
            }
           
            //q存在,且q发生变化
            while(q && lq != q){
                lq = q;               
                // /^(#)?([\w-\*]+)/;
                var tm = q.match(tagTokenRe);
                if(type == "select"){
                    if(tm){
                        if(tm[1] == "#"){                        	
                            fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
                        }else{                        	
                            fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
                        }                       
                        q = q.replace(tm[0], "");
                    }                    
                    else if(q.substr(0, 1) != '@'){
                    	//会处理到分隔开来的特殊字符开头的,如. :一些不是字符或数字的。 .xxx,或其它的。
                        fn[fn.length] = 'n = getNodes(n, mode, "*");';
                    }
                }else{//simple
                    if(tm){
                        if(tm[1] == "#"){
                        	//n = byId(n, null, "userNameId");
                            fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
                        }else{
                        	//n = byTag(n, "p");
                            fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
                        }                        
                        q = q.replace(tm[0], "");
                    }
                }       
                       
                //不是结束,也不是空格,也不是mode的符号          与主标识连在一起的。如div.foo,div:first-child     
                while(!(mm = q.match(modeRe))){     
                    var matched = false;
                    /**
                     * 这里就是实现用regexp找到符合的子项去填充生成调用的函数的参数。如下例:
                     * 原正则表达式:  re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/
                     * 其对应的select为:'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
                                                                        
                     * 它的 四个子项  {"([\[\{])"  "([\w-]+)"  "(=|.=)"  "(.*?)"}
                     * 可以把  E[foo$=bar] 分成 {"[" "foo"  "$="  "bar"}  四部分
                     * var m = q.match(t.re)可以得出    m=['[','foo','$','bar'];                                
                                                                   
                     * 接下来t.select.replace可以把'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
                     * 分成四次调用function(x, i){},其参数为{ x: 1:{2},2:{4},3:{3},4:{1}  i: 1:2,2:4,3:3,4:1 }  
                                             
                     * 得出的结果:n = byAttribute(n, "foo", "bar", "$=", "[");                        
                     */
                   
                    for(var j = 0; j < tklen; j++){
                        var t = tk[j];                         
                        var m = q.match(t.re);
                        if(m){    
                            fn[fn.length] = t.select.replace(tplRe, function(x, i){
                                                    return m[i];
                                                });
                            q = q.replace(m[0], "");
                            matched = true;
                            break;
                        }
                    }
                    // prevent infinite loop on bad selector
                    if(!matched){
                        throw 'Error parsing selector, parsing failed at "' + q + '"';
                    }
                }
                //如果mode能找到值 加上:mode="~"同时remove q中找到的字符串。
                if(mm[1]){
                    fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
                    q = q.replace(mm[1], "");
                }
            }
            fn[fn.length] = "return nodup(n);\n}";
            eval(fn.join(""));
            return f;
        },

        /**
         * Selects a group of elements.
         * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
         * @param {Node} root (optional) The start of the query (defaults to document).
         * @return {Array}
         */
        select : function(path, root, type){
        	//默认把document当做root
            if(!root || root == document){
                root = document;
            }
            //支持root采用string id的形式
            if(typeof root == "string"){
                root = document.getElementById(root);
            }
            //支持多个path
            var paths = path.split(",");
            var results = [];
            for(var i = 0, len = paths.length; i < len; i++){
            	//去string前后的空格
                var p = paths[i].replace(trimRe, "");
                //cache 已经编译的select
                if(!cache[p]){
                	//编译
                    cache[p] = Ext.DomQuery.compile(p);
                    if(!cache[p]){
                        throw p + " is not a valid selector";
                    }
                }
                //运行编译的函数
                var result = cache[p](root);
                if(result && result != document){
                    results = results.concat(result);
                }
            }
            //去掉重复的元素
            if(paths.length > 1){
                return nodup(results);
            }
            return results;
        },

        /**
         * Selects a single element.
         * @param {String} selector The selector/xpath query
         * @param {Node} root (optional) The start of the query (defaults to document).
         * @return {Element}
         */
        //返回查询结果中的第一个。
        selectNode : function(path, root){
            return Ext.DomQuery.select(path, root)[0];
        },

        /**
         * Selects the value of a node, optionally replacing null with the defaultValue.
         * @param {String} selector The selector/xpath query
         * @param {Node} root (optional) The start of the query (defaults to document).
         * @param {String} defaultValue
         */
        selectValue : function(path, root, defaultValue){
        	//trim(path)
            path = path.replace(trimRe, "");
            //编译path
            if(!valueCache[path]){
                valueCache[path] = Ext.DomQuery.compile(path, "select");
            }
             //运行编译的函数
            var n = valueCache[path](root);
            n = n[0] ? n[0] : n;
            //返回节点第一个孩子的节点值。
            var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
            return ((v === null||v === undefined||v==='') ? defaultValue : v);
        },

        /**
         * Selects the value of a node, parsing integers and floats.
         * @param {String} selector The selector/xpath query
         * @param {Node} root (optional) The start of the query (defaults to document).
         * @param {Number} defaultValue
         * @return {Number}
         */
        selectNumber : function(path, root, defaultValue){
            var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
            return parseFloat(v);
        },

        /**
         * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
         * @param {String/HTMLElement/Array} el An element id, element or array of elements
         * @param {String} selector The simple selector to test
         * @return {Boolean}
         */
        is : function(el, ss){
            if(typeof el == "string"){
                el = document.getElementById(el);
            }
            //支持el参数为非数组的元素
            var isArray = Ext.isArray(el);
            var result = Ext.DomQuery.filter(isArray ? el : [el], ss);
            return isArray ? (result.length == el.length) : (result.length > 0);
        },

        /**
         * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
         * @param {Array} el An array of elements to filter
         * @param {String} selector The simple selector to test
         * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
         * the selector instead of the ones that match
         * @return {Array}
         */
        //找到els中与ss找到els中相配或不相配的元素集合
        filter : function(els, ss, nonMatches){
            ss = ss.replace(trimRe, "");
            if(!simpleCache[ss]){
                simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
            }
            var result = simpleCache[ss](els);
            return nonMatches ? quickDiff(result, els) : result;
        },

        /**
         * Collection of matching regular expressions and code snippets.
         */
        matchers : [{
                re: /^\.([\w-]+)/, //div.foo
                select: 'n = byClassName(n, null, " {1} ");'
            }, {
                re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
                select: 'n = byPseudo(n, "{1}", "{2}");'
            },{
            	//支持CSS,属性两种形式的selector            	
                re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
                select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
            }, {
                re: /^#([\w-]+)/,//#aid
                select: 'n = byId(n, null, "{1}");'
            },{
                re: /^@([\w-]+)/,//div@xxx
                select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
            }
        ],

        /**
         * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
         * 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, > <.
         */
        operators : {
            "=" : function(a, v){
                return a == v;
            },
            "!=" : function(a, v){
                return a != v;
            },
            "^=" : function(a, v){
                return a && a.substr(0, v.length) == v;
            },
            "$=" : function(a, v){
                return a && a.substr(a.length-v.length) == v;
            },
            "*=" : function(a, v){
                return a && a.indexOf(v) !== -1;
            },
            "%=" : function(a, v){
                return (a % v) == 0;
            },
            "|=" : function(a, v){
                return a && (a == v || a.substr(0, v.length+1) == v+'-');
            },
            "~=" : function(a, v){
                return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
            }
        },

        /**
         * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
         * and the argument (if any) supplied in the selector.
         */
        pseudos : {
            "first-child" : function(c){
                var r = [], ri = -1, n;
                for(var i = 0, ci; ci = n = c[i]; i++){
                    while((n = n.previousSibling) && n.nodeType != 1);
                    if(!n){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "last-child" : function(c){
                var r = [], ri = -1, n;
                for(var i = 0, ci; ci = n = c[i]; i++){
                    while((n = n.nextSibling) && n.nodeType != 1);
                    if(!n){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "nth-child" : function(c, a) {
                var r = [], ri = -1;
                var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
                var f = (m[1] || 1) - 0, l = m[2] - 0;
                for(var i = 0, n; n = c[i]; i++){
                    var pn = n.parentNode;
                    if (batch != pn._batch) {
                        var j = 0;
                        for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
                            if(cn.nodeType == 1){
                               cn.nodeIndex = ++j;
                            }
                        }
                        pn._batch = batch;
                    }
                    if (f == 1) {
                        if (l == 0 || n.nodeIndex == l){
                            r[++ri] = n;
                        }
                    } else if ((n.nodeIndex + l) % f == 0){
                        r[++ri] = n;
                    }
                }

                return r;
            },

            "only-child" : function(c){
                var r = [], ri = -1;;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(!prev(ci) && !next(ci)){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "empty" : function(c){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    var cns = ci.childNodes, j = 0, cn, empty = true;
                    while(cn = cns[j]){
                        ++j;
                        if(cn.nodeType == 1 || cn.nodeType == 3){
                            empty = false;
                            break;
                        }
                    }
                    if(empty){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "contains" : function(c, v){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "nodeValue" : function(c, v){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(ci.firstChild && ci.firstChild.nodeValue == v){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "checked" : function(c){
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(ci.checked == true){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "not" : function(c, ss){
                return Ext.DomQuery.filter(c, ss, true);
            },

            "any" : function(c, selectors){
                var ss = selectors.split('|');
                var r = [], ri = -1, s;
                for(var i = 0, ci; ci = c[i]; i++){
                    for(var j = 0; s = ss[j]; j++){
                        if(Ext.DomQuery.is(ci, s)){
                            r[++ri] = ci;
                            break;
                        }
                    }
                }
                return r;
            },

            "odd" : function(c){
                return this["nth-child"](c, "odd");
            },

            "even" : function(c){
                return this["nth-child"](c, "even");
            },

            "nth" : function(c, a){
                return c[a-1] || [];
            },

            "first" : function(c){
                return c[0] || [];
            },

            "last" : function(c){
                return c[c.length-1] || [];
            },

            "has" : function(c, ss){
                var s = Ext.DomQuery.select;
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    if(s(ss, ci).length > 0){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "next" : function(c, ss){
                var is = Ext.DomQuery.is;
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    var n = next(ci);
                    if(n && is(n, ss)){
                        r[++ri] = ci;
                    }
                }
                return r;
            },

            "prev" : function(c, ss){
                var is = Ext.DomQuery.is;
                var r = [], ri = -1;
                for(var i = 0, ci; ci = c[i]; i++){
                    var n = prev(ci);
                    if(n && is(n, ss)){
                        r[++ri] = ci;
                    }
                }
                return r;
            }
        }
    };
}();

/**
 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
 * @param {String} path The selector/xpath query
 * @param {Node} root (optional) The start of the query (defaults to document).
 * @return {Array}
 * @member Ext
 * @method query
 */
Ext.query = Ext.DomQuery.select;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值