[转帖]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;
}
};
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;
}
};