-
Notifications
You must be signed in to change notification settings - Fork 22
parser
leeluolee edited this page Jan 7, 2013
·
3 revisions
首先通过var parser = nes.parser
来获得我们的parser部分
我们会通过这个parser来完成:
- 新增一个与Id、className等等价的Simple Selectorparser.on
- 基于这个parser的改造完成一个dom版的zenCoding!!
你可以理解成当碰到某种规则时,你需要parser做什么处理
Arguments:
- name: 规则名(如classList)
- def 包含三个部分 :
- (String || RegExp) reg : 你规则的正则定义 (必须输入)
- Void Function action: (可忽略)
与parse相关的函数,参数即你正则里匹配到的子匹配,你要做的是把所有的匹配塞到应该去的地方 - Bool Function filter: (可忽略)
与find相关的函数,针对单节点的过滤函数,返回Boolean
场景描述: 你需要获取ul.test1中所有在同级节点中所处位置大于1,但是小于9的li标签
原生方法: ul.test1 > li:nth-child(n+1):not(:nth-last-child(n+10))
理想做法: ul.test1 > li{1,9}
===> 此语法没有定义会报解析错误
你需要做的扩展是:
// 在进行语法扩展时reg是必须的,
// action与filter至少需要需要二选其一(根据场景不同)才能完成一个自定义规则的实施, 否则虽然不会报错
// 但是只是匹配了,让解析器不报错,但是这个选择器什么都不会做
nes.parser.on("range",{
reg:/\s*\{\s*(\d*),(\-?\d*)\s*\}\s*/,
// action中需要注意两个部分一个是this.error()打印出解析错误信息,
// 一个this.current()永远返回当前的Simple Selector对应的data部分这是个hash表,
// 你往里放key value对就行
action:function(all, a, b){ //注意这里的a,b是郑则匹配到的子匹配,all是完整匹配
var current = this.current(), //1. this.current返回当前匹配的simple selector
pesudos = current.pesudos || (current.pesudos = [])
if(!a && !b) this.error("range中的参数不能同时为空") //2. this.error
a = a && parseInt(a) || 1
b = b && parseInt(b) || 0
pesudos.push({ //a 如果不存在 视为
name:"nth-child",
param:{start:a, step:1 }
})
if(b>0){
pesudos.push({ //意思小于b
name:"not",
param:":nth-child(" + (b+1) + ")"
})
}else{
pesudos.push({
name:"nth-last-child",
param:{start:b?-b:1, step:1 }
})
}
}
})
Example:
// zen-coding dom版本 开始
// ==============================
// 这里由于使用到了很多nes中的规则定义,
// 所以我们直接clone nes的parser
//
// __为什么不直接添加新规则到nes.parser中?__
// 因为例如(li.class)*2 的group或者 repeat都是
// nes中不需要的 拖缓了速度 还增加了冲突的可能(冲突可通过order来规避,
// 这个parser可以用来生成 命令行参数解析等简单任务
// 但是复杂的parse还是不建议这样来实现,应该通过手写或者bison这种成熟解析生成)
var parser2 = nes.parser.clone()
// 其中pesudos,分隔符也是不需要
.off(["pesudos","split"])
// 添加zen-coding中需要的匹配
.on({
"group": {
reg:/\(([^\)]*)\)/,
action:function(all, capture){
this.current().group = capture
},
order:9
},
"repeat":{
reg:/\*([1-9]\d*)/,
action:function(all, num){
this.current().repeat = parseInt(num)
},
order:8
}
})
// 根据data生成节点
var createNode = function(option){
var tag = option.tag,
group = option.group,
creater
if(group){
node = create(group)
}
else{
node = document.createElement(tag == "*"? "div":option.tag)
}
for(var i in option){
if(creater = ExpandCreater[i]){
creater(node, option[i])
}
}
if(option.repeat){//如果重复数
var parent = document.createDocumentFragment()
for(var len = option.repeat;len--;){
parent.appendChild(len ==0?node: node.cloneNode(true))
}
return parent
}
return node
}
var ExpandCreater = {
//group与tag特殊
id:function(node, id){
node.id = id
},
classList: function(node, classList){
node.className = classList.join(" ")
},
attributes:function(node, attributes){
var len = attributes.length, attribute
for(;len--;){
attribute = attributes[len]
node.setAttribute(attribute.key, typeof attribute.value == "undefined"? true : attribute.value)
}
}
}
var comboFilter = function(prev, node, combo){
if(combo!==">" && combo!=="+"){
throw Error("节点创建不能传入非>或+连接符")
}else{
if(combo === ">"){
var parent = prev
parent.appendChild(node)
}else{
parent = prev.parentNode
if(!parent){
parent = document.createDocumentFragment()
parent.appendChild(prev)
}
parent.appendChild(node)
}
return parent
}
}
// 这里是暴露的api: __create__
// 传入选择器,生成dom节点(与一般的zen-coding生成字符串不同)
// 如果顶层节点不是一个,则返回一个documentFragment
// 如果是group 则进行二次create (group中有group以此类推)
var create = function(sl){
var data = parser2.parse(sl)[0],
len = data.length,
datum, parent, current, prev
for(var i = 0; i < len; i++){
datum = data[i]
prev = current
current = createNode(datum)
if(i!==0){
var node = comboFilter(prev, current, data[i-1].combo) //匹配出真正的parent节点
if(parent === prev) parent = node
}
if(!parent) parent = current
}
return parent
}
var node = create("p>div#cnt+header#id.m-hd.m-md>ul>(li#nm[rel=hah].name1+li.nm2*4)*5")
console.log(node) 你可以看到这个塞的满满的的节点了
- parser.cache.length 控制缓存大小 默认200
- parser.off 删除对某些规则的监听