[转帖]Mootools源码分析-22 -- Selectors-1

原帖地址:http://space.flash8.net/space/?uid-18713-action-viewspace-itemid-403967

原作者:我佛山人

 

//跟Selector有关的对Document和Element的扩展实现
Native.implement([Document, Element], {
    
//根据查询表达式获取子节点
    getElements: function(expression, nocash)    {
        
//拆分查询表达式
        expression = expression.split(',');
        
var items, local = {};
        
for (var i = 0, l = expression.length; i < l; i++)    {
            
//根据查询条件搜索匹配的节点
            var selector = expression[i], elements = Selectors.Utils.search(this, selector, local);
            
//如果找到节点集合,转为数组类型
            if (i != 0 && elements.item)    elements = $A(elements);
                
//加到items数组中
                items = (i == 0? elements : (items.item) ? $A(items).concat(elements) : items.concat(elements);
        }
        
//返回Elements集合
        return new Elements(items, {ddup: (expression.length > 1), cash: !nocash});
    }
});

//跟Selector有关的对Element的扩展实现
Element.implement({
    
//判断当前节点是否匹配指定模式
    match: function(selector)    {
        
if (!selector)    return true;
        
//根据选择符解释出标签名和id
        var tagid = Selectors.Utils.parseTagAndID(selector);
        
var tag = tagid[0], id = tagid[1];
        
//当前节点不匹配ID或标签中的任一个都返回false
        if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag))    return false;
        
//根据选择符匹配
        var parsed = Selectors.Utils.parseSelector(selector);
        
return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
    }
});

//声明为简单对象,搭个框架等待后续的填充处理
var Selectors = {Cache: {nth: {}, parsed: {}}};

Selectors.RegExps 
= {
    
//查询表达式中对id的匹配模式
    id: (/#([\w-]+)/),
    
//查询表达式中对标签名的匹配模式
    tag: (/^(\w+|\*)/),
    
//查询表达式中对快速模式的匹配模式
    quick: (/^(\w+|\*)$/),
    
//查询表达式中对分隔符的匹配模式
    splitter: (/\s*([+>~\s])\s*([a-zA-Z#.*:\[])/g),
    
//组合模式 
    combined: (/\.([\w-]+)|\[(\w+)(?:([!*^$~|]?=)["']?(.*?)["']?)?\]|:([\w-]+)(?:\(["']?(.*?)?["']?\)|$)/g)
};

//一些辅助的工具方法
Selectors.Utils = {
    
//检查item是否重复
    chk: function(item, uniques)    {
        
if (!uniques)    return true;
        
//总是返回当前元素的唯一id
        var uid = $uid(item);
        
//如果不存在,说明集合中还没有此项,置为true返回
        if (!uniques[uid])    return uniques[uid] = true;
        
return false;
    },

    
/*
    解释nth查询时的参数
    nth表示对第n个的查询,见http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#nth-child-pseudo
    
*/
    parseNthArgument: 
function(argument)    {
        
//如果曾经按相同参数查询过,返回缓存的结果,不错的策略,避免每次重新计算
        if (Selectors.Cache.nth[argument])    return Selectors.Cache.nth[argument];
        
//解释nth查询的参数
        var parsed = argument.match(/^([+-]?\d*)?([a-z]+)?([+-]?\d*)?$/);
        
if (!parsed)    return false;
        
//第一个数值
        var inta = parseInt(parsed[1]);
        
//inta没值时a默认为1
        var a = (inta || inta === 0? inta : 1;
        
//命名部分
        var special = parsed[2|| false;
        
//第二个数值
        var b = parseInt(parsed[3]) || 0;
        
if (a != 0)    {
            b
--;
            
while (b < 1) b += a;
            
while (b >= a) b -= a;
        }    
else    {
            a 
= b;
            special 
= 'index';
        }
        
switch (special)    {
        
case 'n': parsed = {a: a, b: b, special: 'n'}; break;
        
case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
        
case 'even': parsed =  {a: 2, b: 1, special: 'n'}; break;
        
case 'first': parsed = {a: 0, special: 'index'}; break;
        
case 'last': parsed = {special: 'last-child'}; break;
        
case 'only': parsed = {special: 'only-child'}; break;
        
default: parsed = {a: (a - 1), special: 'index'};
        }
        
return Selectors.Cache.nth[argument] = parsed;
    },

    
//解释选择符
    parseSelector: function(selector)    {
        
//同样的缓存策略
        if (Selectors.Cache.parsed[selector])    return Selectors.Cache.parsed[selector];
        
var m, parsed = {classes: [], pseudos: [], attributes: []};
        
//注意正则的exec和match的区别
        while ((m = Selectors.RegExps.combined.exec(selector)))    {
            
//匹配结果依次为:CSS类名,属性名,属性操作符,属性值,伪类名,伪类属性
            var cn = m[1], an = m[2], ao = m[3], av = m[4], pn = m[5], pa = m[6];
            
if (cn)    {
                
//加到CSS类名集合
                parsed.classes.push(cn);
            }    
else if (pn)    {
                
var parser = Selectors.Pseudo.get(pn);
                
//如果存在伪类名,pa作为伪类参数
                if (parser)    parsed.pseudos.push({parser: parser, argument: pa});
                
//否则作为属性比较的右值
                else    parsed.attributes.push({name: pn, operator: '=', value: pa});
            } 
else if (an)    {
            
//属性比较
            parsed.attributes.push({name: an, operator: ao, value: av});
            }
        }
        
//如果选择符中不存在CSS类名的查询,删除classes属性
        if (!parsed.classes.length)    delete parsed.classes;
        
//如果选择符中不存在Element属性的查询,删除attributes属性
        if (!parsed.attributes.length)    delete parsed.attributes;
        
//如果选择符中不存在伪类的查询,删除pseudos属性
        if (!parsed.pseudos.length)    delete parsed.pseudos;
        
//如果三者都不存在,置null
        if (!parsed.classes && !parsed.attributes && !parsed.pseudos)    parsed = null;
        
//缓存结果并返回
        return Selectors.Cache.parsed[selector] = parsed;
    },

    
//解释查询表达式中的标签名和id
    parseTagAndID: function(selector)    {
        
//匹配标签名
        var tag = selector.match(Selectors.RegExps.tag);
        
//匹配id
        var id = selector.match(Selectors.RegExps.id);
        
//没有匹配标签时用星号'*'代替,没有匹配id时返回false
        return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
    },

    filter: 
function(item, parsed, local)    {
        
var i;
        
//根据CSS类名匹配
        if (parsed.classes)    {
            
for (i = parsed.classes.length; i--; i)    {
                
var cn = parsed.classes[i];
                
if (!Selectors.Filters.byClass(item, cn))    return false;
            }
        }
        
//根据属性匹配
        if (parsed.attributes)    {
            
for (i = parsed.attributes.length; i--; i)    {
                
var att = parsed.attributes[i];
                
if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value))    return false;
            }
        }
        
//根据伪类匹配
        if (parsed.pseudos)    {
            
for (i = parsed.pseudos.length; i--; i)    {
                
var psd = parsed.pseudos[i];
                
if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local))    return false;
            }
        }
        
return true;
    },

    
//根据标签名和ID查找Element
    getByTagAndID: function(ctx, tag, id)    {
        
//如果提供id
        if (id)    {
            
//先根据id查找到Element
            var item = ctx.getElementById(id, true);
            
//再根据标签名过滤
            return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
        }    
else    {
            
//不提供id时直接查找满足标签名的Element
            return ctx.getElementsByTagName(tag);
        }
    },

    
//查找
    search: function(self, expression, local)    {
        
//查询表达式的按分隔符分割出来的操作符
        var splitters = [];
        
/*
        根据分隔符切割查询表达式,看来expression中不能包含':)'
        替换函数中m0是整个匹配的字符串,m1为+>~和空白之类的字符,m2为子表达式内容
        
*/
        
var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2)    {
            splitters.push(m1);
            
return ':)' + m2;
        }).split(
':)');
        
var items, match, filtered, item;
        
for (var i = 0, l = selectors.length; i < l; i++)    {
            
var selector = selectors[i];
            
//匹配quick模式意味着可以使用标签名匹配查找
            if (i == 0 && Selectors.RegExps.quick.test(selector))    {
                items 
= self.getElementsByTagName(selector);
                
continue;
            }
            
var splitter = splitters[i - 1];
            
//解释出标签名和id
            var tagid = Selectors.Utils.parseTagAndID(selector);
            
var tag = tagid[0], id = tagid[1];
            
if (i == 0)    {
                
//在首位时的处理
                items = Selectors.Utils.getByTagAndID(self, tag, id);
            }    
else    {
                
//先按标签名,id和操作符查找所有匹配项
                var uniques = {}, found = [];
                
for (var j = 0, k = items.length; j < k; j++)    found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
                items 
= found;
            }
            
//根据其它选择符过滤
            var parsed = Selectors.Utils.parseSelector(selector);
            
if (parsed)    {
                filtered 
= [];
                
for (var m = 0, n = items.length; m < n; m++)    {
                    item 
= items[m];
                    
//过滤匹配
                    if (Selectors.Utils.filter(item, parsed, local))    filtered.push(item);
                }
                items 
= filtered;
            }
        }
        
return items;
    }
};

 

posted @ 2009-11-06 21:56  webgis松鼠  阅读(207)  评论(0编辑  收藏  举报