Ruby's Louvre

每天学习一点点算法

导航

我的第四代选择器Rage

Rage其实已经完成了半年多了,一直在内部使用。它的实现原理与Sizzle一致,都是通过最右近的表达式得到一个种子集,然后不断往左边切割,过滤种子集中不符合的元素,将它们置为flase,最后去掉这些false元素,就得到最后结果了。

支持CSS选择器类型,除jQuery自定义的位置伪类外,一切CSS选择器类型都支持。

由于Sizzle已经在这领域开发很久了,因此很难赶上其速度。

即使如此,我还是很有收获,如更深入了解Sizzle的运作,一些去重排序的算法(主要来自JK的帮助),新的子元素过滤伪类的算法,querySelectorAll的用法改进等等。

不过,第四代选择器最大的特色是其权重体系,它把所有选择器类型分为五类:

           index : {//构建权重体系
                "#":12,
                ".":23,
                "[":34,
                ":":55
                //tagName:41
            },
            apis: {
                12 : "getElementById",
                23 : "getElementsByClassName",
                34 : "getElementsByName",
                41 : "getElementsByTagName" ,
                55 : ""
            },

查找时用这些数字的十位数,过滤时用这些数字的个位数,之所以速度慢下来,是从右到左的实现太复杂了,暂时还无法掌控它。但这东西迟早会应用我新一代选择器中。

使用方法,dom.query(expr,context),不过它是依赖于lang模块的

;
(function(global,DOC){
    var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')];
    dom.log("已加载query模块")
    dom.define("query", "lang",function(){
        dom.mix(dom,{
            //http://www.cnblogs.com/rubylouvre/archive/2010/03/14/1685360.
            isXML : function(el){
                var doc = el.ownerDocument || el
                return doc.createElement("p").nodeName !== doc.createElement("P").nodeName;
            },
            //获取某个节点的文本,如果此节点为元素节点,则取其childNodes的所有文本,
            //为了让结果在所有浏览器下一致,忽略所有空白节点,因此它非元素的innerText或textContent
            getText : function( nodes ) {
                var ret = "", elem;
                for ( var i = 0; nodes[i]; i++ ) {
                    elem = nodes[i];
                    // 对得文本节点与CDATA的内容
                    if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
                        ret += elem.nodeValue;
                    //取得元素节点的内容
                    } else if ( elem.nodeType !== 8 ) {
                        ret += this.getText( elem.childNodes );
                    }
                }
                return ret;
            }

        });

        var reg_split = /^(?:[-\w\*]|[^\x00-\xa0]|\\.)+|[#:](?:[-\w]|[^\x00-\xa0]|\\.)+(?:\([^\)]*\))?|\.(?:[-\w*\.]|[^\x00-\xa0]|\\.)+|\[[^\]]*\]|(?:\s*)([>+~,\s])(?:\s*)(?=\S)/
        var reg_attr  = /\[\s*((?:[-\w]|[^\x00-\xa0]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/;
        var reg_pseudo = /^:(\w[-\w]*)(?:\((['"]*)(.*)\2\))?$/;
        var reg_name  = /\[name=['"]*((?:[-\w]|[^\x00-\xa0]|\\.)+)['"]*\]/;
        var reg_escape =/([\/\[\]\:])/g
        var reg_class = /\.([^.]+)/g;
        var reg_ltrim = /^\s+/;
        var reg_rtrim = /\s+$/;
        var reg_input = /input|select|textarea|button/i;
        var reg_slash = /\\/g
        // CSS3 user action pseudo-classes
        var one_action = dom.oneObject('target,active,hover,focus', 2);
        var one_url = dom.oneObject('action,cite,codebase,data,href,longdesc,lowsrc,src,usemap', 2);
        //According to the W3C spec, document.querySelectorAll('[selected=selected]')
        //should return the same NodeList as document.querySelectorAll('[selected]'),
        var one_bool = dom.oneObject('checked,disabled,defer,ismap,multiple,readonly,selected');
        var one_relative = dom.oneObject([" ", ">", "+", "~"]);
        var child_pseudo = "nth-child,nth-last-child";
        var one_child = dom.oneObject((child_pseudo+","+child_pseudo.replace(/child/g,"of-type")));
        var sort_type = function(a,b){
            return a.charAt(1) - b.charAt(1);
        }
        var one_link = dom.oneObject(["a","A","area","AREA","link","LINK"])
        var flag_repeat = false;
        var defaults = {
            value: 'defaultValue',
            checked: 'defaultChecked',
            selected: 'defaultSelected'
        }
        var props = dom.attrMap = {};
        var to_s = {}.toString;
        var str = "accessKey|allowTransparency|bgColor|cellPadding|cellSpacing|codeBase|codeType|colSpan|dateTime|defaultChecked|defaultSelected|defaultValue|frameBorder|"+
        "isMap|longDesc|maxLength|marginWidth|marginHeight|noHref|noResize|noShade|readOnly|rowSpan|tabIndex|useMap|vSpace|valueType|vAlign"
        str.replace(/\w+/g, function(name){
            props[name.toLowerCase()] = name;
        });
        dom.mix(props, {
            "accept-charset": "acceptCharset",
            "char": "ch",
            charoff: "chOff",
            "class": "className",
            "for": "htmlFor",
            "http-equiv": "httpEquiv"
        });
        var Query = dom.query = function(token, context, result, seed){
            result = result || [];
            context = context || DOC;
            if (context.nodeType !== 1 && context.nodeType !== 9) {
                return result;
            }
            Query.queryTime = new Date-0;//.replace(reg_rltrim,'')
            var right = token.replace(reg_ltrim,'').replace(reg_rtrim,''), loop = [], types = [],relative = one_relative,nodes, match, part;
            //将CSS表达式转换成二重数组,并根据'种子选择器取得候选集
            do{
                match = right.match(reg_split);
                right = RegExp.rightContext;
                part = match[1] || match[0];
                if(part == ","){
                    break
                }else if(relative[part]){
                    loop.push(types,part);
                    types = [];
                }else if(part){
                    (types[types.length] = new String(Query.index[part.charAt(0)] || 41))._ = part;
                }
            }while(right);
            //   dom.log(types)
            match =  seed ? {
                types: types,
                nodes: dom.slice(seed)//nte
            } :Query.getSeed(types, context);//取得种子集

            types = match.types;
            nodes = match.nodes;

            var flag_xml = dom.isXML(context);
            if ( types.length) {//缩窄种子集的范围
                nodes = to_s.call(nodes) === "[object Array]" ? nodes : dom.slice(nodes);
                //  dom.log(types+" "+nodes);
                nodes = Query.sameLevelSift(types, nodes, flag_xml);
            }
            var i = 0, n = nodes.length;
            if(loop.length ){//处理左边选择器
                var set = dom.slice(nodes);//复制一份构造映射集
                while ( loop.length ) {
                    part = types = loop.pop();
                    if ( relative[ part ] ) {
                        types = loop.pop();
                    } else {
                        part = " ";//默认为后代选择器
                    }
                    Query.iterator[ part ]( types, set, flag_xml );
                }
                for(; i < n; i++){
                    if(set[i]){
                        result[result.length] = nodes[i];//合并两个结果集
                    }
                }
            }
            else{
                for(; i < n; i++){
                    result[result.length] = nodes[i]
                }
            }
            if ( right ) {
                Query( right, context, result,seed );
                return result.length < 2 ? [] : dom.unique( result );
            }
            return result;
        }
        dom.mix(Query, {
            index : {//构建权重体系
                "#":12,
                ".":23,
                "[":34,
                ":":55
                //tagName:41
            },
            apis: {
                12 : "getElementById",
                23 : "getElementsByClassName",
                34 : "getElementsByName",
                41 : "getElementsByTagName" ,
                55 : ""
            },
            getAttribute : function(node, attribute) {
                return node.getAttribute(attribute) || '';
            } ,
            hasAttribute : function(node, attr) {
                return node.hasAttribute(attr);
            },
            getElementById:function(expr){
                var el =  this.getElementById(expr.slice(1).replace(reg_slash,''));
                return el && [el] || [];
            },
            getElementsByClassName:function(expr){
                return this.getElementsByClassName(expr.slice(1).replace(reg_slash,'').replace(/\./g, ' '));
            },
            getElementsByName:function(expr){
                var match = expr.match(reg_name);
                return match && this.getElementsByName(match[1].replace(reg_slash,''));
            },
            getElementsByTagName:function(expr){
                return this.getElementsByTagName(expr.replace(reg_slash,''));
            },
            isLink:function(node){
                return Query.hasAttribute(node,'href') && one_link[node.nodeName];
            },
            cacheNTH:{},
            parseNTH:function(expr){
                var a =  (expr === "even" && "2n0" || expr === "odd" && "2n1" || !/n/.test(expr) && ("0n"+expr) || expr.replace(/(^|\D+)n/g,"$11n") ).split(/n/);
                return (Query.cacheNTH[expr] = [a[0]|0,a[1]|0]);
            },
            /*************************获取候选集***************************/
            getSeed: function (types, context) {
                types.sort();
                var i =0, type, api, nodes;
                for(; type = types[i]; i++){
                    api = Query.apis[type];
                    nodes = context[api] && Query[api].call(context,type._);
                    if(nodes){
                        types.splice(i, 1);
                        break;
                    }
                }
                if (!nodes) {
                    nodes = context.getElementsByTagName("*");
                }
                return {
                    nodes: nodes,
                    types: types
                }
            },
            //flag_false,是否将不符合条件的元素置换为false,默认直接去掉此元素
            sameLevelSift:function(types, nodes, flag_xml, flag_false, flag_not){//平级过滤
                types.sort(sort_type);
                var i = 0, type, n, ii , node, match, filter, t = 0
                while ((type = types.shift())) {
                    n = nodes.length;
                    match = Query.preSift[ type ]( type._, flag_xml, flag_false, flag_not, nodes, n  );//加工参数
                    if(!match)
                        continue;
                    filter = Query.filters[type];
                    for (i = ii = 0; i < n; i++) {
                        if ((node = nodes[i])) {
                            if (filter.call( node, match ) ^ flag_not) {//这里只处理ID CLASS TAG ATTR
                                if(!flag_false){
                                    nodes[ii++] = node;
                                }
                            } else if (flag_false) {
                                nodes[i] = false;//过滤种子集
                            }
                        }
                    }
                    if (!flag_false) {
                        nodes.length = n = ii;
                    }
                }
                return nodes;
            },

            //表达式的类型 待检测的节点集合 是否为XML 测试值 目标元素的属性
            crossLevelSift:function(type, nodes, flag_xml, check, prop){//越级过滤
                var i = 0, n = nodes.length, time = Query.queryTime ++,STAR = "*",NIL, node, val;
                for (; i < n; i++) {
                    if ((node = nodes[i])) {
                        val = false;
                        while ((node = node[prop])) {
                            if (node.nodeType === 1) {
                                if (node.queryTime === time) {
                                    val = nodes[node.queryIndex];
                                    break;
                                }
                                if (check === node.nodeName || (check === NIL && Query.sameLevelSift([type], [node], flag_xml, false).length) || check === STAR) {
                                    node.queryTime = time;
                                    node.queryIndex = i;
                                    val = node;
                                    break;
                                }
                            }
                        }
                        nodes[i] = val;
                    }
                }
            },
            preSift:{
                12: function( expr ) {//ID
                    return expr.slice(1).replace(reg_slash,'');
                },
                23: function( expr, flag_xml, flag_false, flag_not, nodes, n ) {//CLASS
                    var  i = 0, ii = 0, node, src = expr.replace(reg_escape, '\\$1').replace(reg_class, '(?=[ \\s\\S]*(?:^|\\s)$1(?: \\s|$))'),
                    reg = new RegExp(src);
                    if ( flag_xml ) {
                        return reg;
                    }
                    for ( ; i < n ; i++ ) {
                        if((node = nodes[i])){
                            if(node.className && reg.test(node.className) ^ flag_not){
                                if(!flag_false){
                                    nodes[ii++] = node;
                                }
                            } else if ( flag_false ) { //不符合的置换为false
                                nodes[i] = false;
                            }
                        }
                    }
                    if ( !flag_false )
                        nodes.length = ii;
                    return false;
                },
                34:function(expr){//ATTR
                    var match = expr.match(reg_attr), name = match[1].replace(reg_slash,'');
                    var value = (match[4]||"").replace(reg_slash,'');
                    if ( match[2] === "~=" ) {
                        value = " " + value + " ";
                    }
                    return [name,match[2],value];
                },
                41:function(expr, flag_xml){//TAG
                    expr = expr.replace(reg_slash,'');
                    if (expr === "*")
                        return false;
                    return flag_xml ? expr : expr.toUpperCase()
                },
                55:function(value, flag_xml, flag_false, flag_not, nodes, n){//PSEUDO
                    var match = value.match(reg_pseudo),expr = (match[3] || "").replace(reg_ltrim,'').replace(reg_rtrim,''),
                    type = match[1] ,i = 0, ii = 0, node, args = [];
                    if((type === "nth-child" || type === "nth-last-child") && expr === "n")
                        return false;
                    if(one_child[type]){//拥有表达式的子元素过滤伪类
                        expr = expr.replace(/\s/g,'');
                        args = Query.cacheNTH[expr] || Query.parseNTH(expr);
                    }else if(one_action[type]){//目标伪类
                        args = [!flag_xml];
                    }else if(type === "lang"){//语言伪类
                        args = [new RegExp("^(" + expr + "$|" + expr + "-)", "i")];
                    }else if(type == "contains"){//内容伪类
                        args = [expr, flag_xml];
                    }else if(type === "not"){
                        var check = 0, whiterat = expr+"";
                        while(whiterat){
                            whiterat.match(reg_split);
                            whiterat = RegExp.rightContext;
                            if(check==2)
                                break;
                            check++;
                        }
                        //  if(check === 1 && expr.charAt(0) !== ":" && expr.slice(-1) !== ")")
                        if(check > 1){//复杂
                            var els = Query(expr, null, null, nodes), hash = {},  j = 0, uuid;
                            while((node = els[j++])){
                                uuid = node.uniqueNumber || (node.uniqueNumber = dom["@uuid"]++);
                                hash[uuid] = 1;
                            }
                            args = [hash];
                        }else {
                            (args[0] = new String(Query.index[expr.charAt(0)] || 41))._ = expr;
                            Query.sameLevelSift(args, nodes, flag_xml, flag_false, true ^ flag_not);
                            return false;
                        }
                    }
                    var filter = Query.filters[type];
                    for ( ; i < n ; i++ ) {
                        if((node = nodes[i])){
                            if(filter.apply(node, args) ^ flag_not){
                                if(!flag_false){
                                    nodes[ii++] = node;
                                }
                            } else if ( flag_false ) { //不符合的置换为false
                                nodes[i] = false;
                            }
                        }
                    }
                    if ( !flag_false )
                        nodes.length = ii;
                    return false;
                }
            },
            iterator:{
                " ":function (types, nodes, flag_xml) {
                    types.sort(sort_type);
                    var prop = "parentNode", type = types.shift(), expr = type._, check ;
                    if(type == "41"){
                        check = flag_xml ? expr : expr.toUpperCase();
                    }
                    Query.crossLevelSift(type, nodes, flag_xml, check, prop);//越级过滤
                    types.length && Query.sameLevelSift(types, nodes,flag_xml, true);//平级过滤
                },
                "~":function (types, nodes, flag_xml) {
                    types.sort(sort_type);
                    var prop = "previousSibling", type = types.shift(), expr = type._, check;
                    if(type == "41"){
                        check = flag_xml ? expr : expr.toUpperCase();
                    }
                    Query.crossLevelSift(type, nodes, flag_xml, check, prop);//越级过滤
                    types.length && Query.sameLevelSift(types, nodes, flag_xml, true);//平级过滤
                },
                ">" : function(types, nodes, flag_xml) {
                    types.sort(sort_type);
                    var type = types[0], expr = type._, check , node;
                    if(type == "41" && types.shift()){
                        check = flag_xml ? expr : expr.toUpperCase();
                    }
                    for (var i = 0, n = nodes.length; i < n; i++) {
                        if ((node = nodes[i])) {
                            node = node.parentNode;
                            nodes[i] = (check && node) ? node.nodeName === check && node : node;
                        }
                    }
                    types.length && Query.sameLevelSift(types, nodes, flag_xml, true);//平级过滤
                },
                "+" : function(types, nodes, flag_xml) {
                    types.sort(sort_type);
                    var type = types[0], expr = type._, check , node;
                    if(type == "41" && types.shift()){
                        check = flag_xml ? expr : expr.toUpperCase();
                    }
                    for (var i = 0, n = nodes.length; i < n; i++) {
                        if ((node = nodes[i])) {
                            while ((node = node.previousSibling) && node.nodeType !== 1) {};
                            nodes[i] = (check && node) ? node.nodeName === check && node : node;
                        }
                    }
                    types.length && Query.sameLevelSift(types, nodes, flag_xml, true);//平级过滤
                }
            },
            filters:{
                12:function(id){//这里用于XML
                    return id ===  this.getAttribute("id");
                },
                41: function (nodeName ) {
                    return  this.nodeName === nodeName;
                },
                23: function( reg ) {//这里用于XML
                    return reg.test(this.getAttribute("class"));
                },
                34:function(match){
                    var name = match[0], operator = match[1], value = match[2];
                    if(!operator){
                        return   Query.hasAttribute(this,name);
                    }
                    var attrib = Query.getAttribute(this, name, true);
                    if( match[2] === "" ){//目标值不能为空字串
                        return false;
                    }
                    switch (operator) {
                        case "=":
                            return attrib === value;
                        case "!=":
                            return attrib !== value;
                        case "~=":
                            return attrib  && (" " + attrib + " ").indexOf(value) !== -1;
                        case "^=":
                            return attrib  && attrib.indexOf(value) === 0;
                        case "$=":
                            return attrib  && attrib.lastIndexOf(value) + value.length === attrib.length;
                        case "*=":
                            return attrib  && attrib.indexOf(value) !== -1;
                        case "|=":
                            return attrib === value || attrib.substring(0, value.length + 1) === value + "-";
                    }
                },
                //CSS3属性伪类
                enabled: function(){
                    return this.disabled === false && this.type !== "hidden";
                },
                //CSS3属性伪类
                disabled: function(){
                    return this.disabled === true;
                },
                //CSS3属性伪类
                checked: function(){
                    return this.checked = true;
                },
                //CSS3属性伪类
                indeterminate:function(){
                    return this.indeterminate === true && this.type === "checkbox" ;
                },
                //自定义属性伪类
                selected: function () {
                    this.parentNode.selectedIndex; //处理safari的bug
                    return this.selected === true;
                },
                //CSS3结构伪类(子元素过滤伪类)
                empty: function(){
                    var child = this.firstChild;
                    return !(child && child.nodeType == 1) && !(this.innerText || this.textContent || '').length;
                },
                //自定义可见性伪类
                target: function (flag_html) { //CSS2.1目标标准
                    var id = location.hash.slice(1),
                    check = this.id || flag_html && this.name;
                    return check === id;
                },
                link: function(){
                    return Query.isLink(this) && !this.visited
                },
                visited: function(){
                    return Query.isLink(this) && this.visited
                },
                active: function(flag_html){
                    return flag_html && this === this.ownerDocument.activeElement;
                },
                hover: function(flag_html){
                    return flag_html && this === this.ownerDocument.hoverElement;
                },
                focus:function(flag_html){
                    return flag_html && (this.type|| this.href) && this === this.ownerDocument.activeElement;
                },
                //CSS2链接伪类
                lang: function (reg) { //CSS3语言伪类
                    var node = this;
                    while (!node.getAttribute("lang") && (node = node.parentNode)) {};
                    return reg.test(node.getAttribute("lang"));
                },
                header: function(){
                    return /h\d/i.test( this.nodeName )
                },
                //自定义属性伪类
                button: function(){
                    return this.type === "button" || this.nodeName === "BUTTON"
                },
                //自定义属性伪类
                input: function(){
                    return reg_input.test(this.nodeName)
                },

                parent: function(){
                    return !!this.firstChild;
                },
                //自定义内容伪类
                contains:function(text, flag_xml){
                    return (this.textContent || this.innerText || (flag_xml ? dom.getText([ this ]) : "")).indexOf(text) >= 0;
                },
                //自定义结构伪类
                has: function (exp, flag_xml) { //自定义结构伪类(子元素过滤伪类,根据子节点的选择器情况进行筛选)
                    return !!dom.query(exp, this, flag_xml).length;
                },
                not: function (hash) { //CSS3反选伪类
                    return !hash[this.uniqueNumber];
                },
                hidden:function(  ) {
                    var width = this.offsetWidth,
                    height = this.offsetHeight;
                    return width === 0 || height === 0 || (!dom.support.reliableHiddenOffsets && (this.style.display || dom.css( this, "display" )) === "none");
                }
            }
        });
        
        dom.query.filters.visible = function(  ) {
            return !dom.query.filters.hidden.call(this)
        };
        var queryPseudoNoExp = function (name, isLast, isOnly) {
            var head = "var node = this;{0} while((node=node.{1}))  if(node.{2} === {3}) return false;";
            var body = "var prev = this;while ((prev = prev.previousSibling)) if (prev.{2} === {3}) return false;";
            var foot = "return true;"
            var start = isLast ? "nextSibling" : "previousSibling";
            var fills = {
                type: ["var tagName = this.nodeName;", start, "nodeName", "tagName"],
                child: ["", start, "nodeType", "1"]
            }
            [name];
            var fn = isOnly ? head + body + foot : head + foot;
            return new Function(fn.replace(/{(\d)}/g, function ($, $1) {
                return fills[$1];
            }));
        }
        var queryPseudoHasExp = function(child, sibling, ofType,cache){
            return function(a,b){
                var uid = this.uniqueNumber || (this.uniqueNumber = dom["@uuid"]++), uuid
                if (!cache[uid]){
                    var parent = this.parentNode;
                    if (!parent) return false;
                    var node = parent[child], count = 1,
                    checkName = ofType ? "nodeName" : "nodeType",
                    checkValue = ofType ? this.nodeName : 1;
                    do {
                        if (node[checkName] != checkValue) continue;
                        uuid = node.uniqueNumber || (node.uniqueNumber = dom["@uuid"]++);
                        cache[uuid] = count++;
                    } while ((node = node[sibling]));
                }
                var pos = cache[uid];
                if (a == 0) return b == pos;
                if (a > 0){
                    if (pos < b) return false;
                } else {
                    if (b < pos) return false;
                }
                return ((pos - b) % a) == 0;
            };
        };
        dom.mix(Query.filters, {
            "first-child"     : queryPseudoNoExp("child", false, false),
            "last-child"      : queryPseudoNoExp("child", true,  false),
            "only-child"      : queryPseudoNoExp("child", true,  true),
            "first-of-type"   : queryPseudoNoExp("type",  false, false),
            "last-of-type"    : queryPseudoNoExp("type",  true,  false),
            "only-of-type"    : queryPseudoNoExp("type",  true,  true), //CSS3子元素过滤伪类
            "nth-child"       : queryPseudoHasExp("firstChild", "nextSibling", false, {}),
            "nth-last-child"  : queryPseudoHasExp("lastChild", "previousSibling", false, {}),
            "nth-of-type"     : queryPseudoHasExp("firstChild", "nextSibling", true, {}),
            "nth-last-of-type": queryPseudoHasExp("lastChild", "previousSibling", true, {})
        });
        "text|radio|checkbox|file|password|submit|image|reset".replace(/\w+/g, function(name){
            Query.filters[name] = function(){//自定义属性伪类
                return "form" in this && this.type === name;
            }
        });

        dom.unique = function(nodes){
            var self = dom.unique;
            var n = nodes.length;
            var lists = self.getLists(nodes);
            lists.sort(function(a,b){
                return a.length - b.length;
            });//按长度排序
            var depth = lists[0].length, length = lists.length, parent, cut, ii = 0;
            for(var i =0; i < depth; i++){
                parent = lists[0][i];//假设这个为LCA
                cut = true;
                for(var j = 1;j <  length; j++){//与其他祖先树相比
                    if(parent !== lists[j][i]){
                        cut = false;
                        break;
                    }
                }
                if(cut){
                    ii++
                }else{
                    break;
                }
            }
            var LCA = lists[0][ii-1];
            lists = self.sliceLists(lists,ii);
            lists.sort(self.sortLists);
            var list, i = 0, result = [];
            while((list = lists[i++])){
                result[result.length] = list.pop();
            }
            if(result.length !== n){
                result.unshift(LCA);
                if(result.length != n){
                    flag_repeat = true;
                }
                if ( flag_repeat ) {
                    for ( i = 1; i < result.length; i++ ) {
                        if ( result[i] === result[ i - 1 ] ) {
                            result.splice( i--, 1 );
                        }
                    }
                }
            }
            return result;
        }
        dom.mix(dom.unique,{
            getList : function(node){//取得所有父元素
                var list = [];
                while(node){
                    if(node.nodeType === 9){
                        break;
                    }
                    list.unshift(node);
                    node = node.parentNode;
                }
                return list;
            },
            getLists : function(nodes){//取得所有祖先的树
                var lists = [], getList = this.getList, i=0, node;
                while((node = nodes[i++])){
                    lists[ lists.length ] = getList(node);
                }
                return lists;
            },
            sliceLists : function(lists,num){
                var result = [], i = 0, list;
                while((list = lists[i++])){
                    list = list.slice(num);
                    if(list.length){
                        result[ result.length ] = list;
                    }
                }
                return result;
            },
            sortLists : function(a,b){
                var n = Math.min(a.length,b.length),ap,bp;
                for(var i=0; i < n; i++){
                    ap = a[i],bp = b[i]
                    if(ap !== bp){
                        while(ap = ap.nextSibling){
                            if(ap === bp){
                                return -1;
                            }
                        }
                        return 1;
                    }
                }
                return a.length-b.length;
            }
        },false);

        /************************根据浏览器特征重写部分函数**************************/
        if ( reg_ltrim.test( "\xA0" ) ) {
            reg_ltrim = /^[\s\xA0]+/;
            reg_rtrim = /[\s\xA0]+$/;
        }
        var HTML = dom.html,
        whiterat = DOC.createElement("div"),id = "WHITERAT";
        whiterat.innerHTML = '<a name="'+id+'"></a><b id="'+id+'"></b>';
        HTML.insertBefore(whiterat, HTML.firstChild);
        //IE的getElementsById无法区分name与id
        if( DOC.getElementById(id) === whiterat.firstChild){
            Query.filters["12"] = function(id){
                return this.getAttributeNode("id").nodeValue === id;
            }
        }
        if(!whiterat.hasAttribute){
            Query.hasAttribute = function(node, attribute) {
                attribute = attribute in props ?//处理HTML非对称的属性
                props[attribute] : attribute;
                if (defaults[attribute] in node) {//处理default前缀的属性
                    return !!node[defaults[attribute]];
                }
                node = node.getAttributeNode(attribute);
                return !!(node && (node.specified || node.nodeValue));
            }
        }
        var input;
        //IE6/7/8(Q)中元素的attribute与property内部实现是不分的,IE8部分分离,IE9实现彻底分离了
        //http://www.javaeye.com/topic/757522
        (input = DOC.createElement('input')).setAttribute('value', '5');
        if( input.defaultValue != 5){
            Query.getAttribute = function(node, attr, use_in_query) {
                if (defaults[attr] in node) {
                    return node[defaults[attr]] || '';
                }
                var result = ""
                if(one_url[attr]){
                    result = node.getAttribute(attr, 2)
                }else if(use_in_query && one_bool[attr]){
                    result = node.getAttribute(attr) ? attr : ''
                }else{
                    result = (node = node.getAttributeNode(attr)) && node.value
                }
                return result || "";
            }
        }
        //IE6/7/8的getElementsByName有严重bug,只支持搜索表单元素(6-7),并无法区分id与name
        if(DOC.getElementsByName(id).length === 0){//IE9返回正确的长度:2
            delete Query.apis["34"];
        }
        //如果支持sourceIndex我们将使用更为高效的节点排序
        //http://www.cnblogs.com/jkisjk/archive/2011/01/28/array_quickly_sortby.html
        if(whiterat.sourceIndex){
            dom.unique = function(nodes){
                var result = [], array = [], uniq = {}, node ,index, ri = 0;
                for(var i = 0 , n = nodes.length; i< n; i++){
                    node = nodes[i];
                    index = node.sourceIndex+1e8;
                    if(!uniq[index]){
                        (array[ri++] = new String(index))._ = node;
                        uniq[index] = 1
                    }
                }
                array.sort();
                while( ri )
                    result[--ri] = array[ri]._;
                return result;
            }
        }
        //如果支持querySelectorAll
        var one_qsa = {
            1:1,//只使用第一个分支
            2:0,
            9:1
        }
        if ( whiterat.querySelectorAll) {
            if (DOC.documentMode === 8){
                one_qsa = {
                    1:0,
                    2:1,
                    9:1
                }
            };
            (function(){
                var oldQuery = Query;
                // Safari can't handle uppercase or unicode characters when in quirks mode.
                if (  whiterat.querySelectorAll("#"+id).length === 0 ) {
                    return;
                }
                Query = dom.query = function( expr, context, result, seed ) {
                    context = context || DOC;
                    //http://bugs.jquery.com/ticket/7949
                    if ( !seed && !dom.isXML(context) && !/\:lang\(/.test(expr) ) {
                        expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
                        if ( one_qsa[context.nodeType] ) {// 1 9
                            try {
                                return dom.slice( context.querySelectorAll(expr) );
                            } catch(e) {}
                        }
                        if ( one_qsa[context.nodeType+1] ) {//2 10
                            var id = context.getAttribute( "id" ),uuid = context.uniqueID
                            if ( !id ) {
                                context.setAttribute( "id", uuid );
                            }
                            try {
                                return context.querySelectorAll( "#" + uuid + " " + expr );
                            } catch(e) {
                            } finally {
                                if ( id == context.uniqueID ) {
                                    context.removeAttribute( "id" );
                                }
                            }
                        }
                    }
                    return oldQuery(expr, context, result, seed);
                };
                for ( var prop in oldQuery ) {
                    Query[ prop ] = oldQuery[ prop ];
                }
            })();
        }

        HTML.removeChild(whiterat);

    });

})(this,this.document);
//2011.9.27  uniqueID改为uniqueNumber 
//2011.10.20 添加visible与hidden伪类
//2011.10.21 Fix visible BUG

posted on 2011-11-09 22:14  司徒正美  阅读(2180)  评论(2编辑  收藏  举报