Skip to content
leeluolee edited this page Jan 7, 2013 · 3 revisions

nes中parser扩展部分

首先通过var parser = nes.parser来获得我们的parser部分

我们会通过这个parser来完成:

  1. 新增一个与Id、className等等价的Simple Selectorparser.on
  2. 基于这个parser的改造完成一个dom版的zenCoding!!

1. parser.on(String name, Object def)

你可以理解成当碰到某种规则时,你需要parser做什么处理

Arguments:

  • name: 规则名(如classList)
  • def 包含三个部分 :
    1. (String || RegExp) reg : 你规则的正则定义 (必须输入)
    2. Void Function action: (可忽略)
      与parse相关的函数,参数即你正则里匹配到的子匹配,你要做的是把所有的匹配塞到应该去的地方
    3. 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 }
      })
    }
  }
})

2. 全面改造parser—— Zen Coding Dom版

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) 你可以看到这个塞的满满的的节点了

3. 其他的乱起八糟

  1. parser.cache.length 控制缓存大小 默认200
  2. parser.off 删除对某些规则的监听