docs: function loopgen(dom, sel, loop, fns){
      var already = false, ls, sorter, filter, prop;
      for(prop in loop){
        if(loop.hasOwnProperty(prop)){
          if(prop === 'sort'){
            sorter = loop.sort;
            continue;
          }else if(prop === 'filter'){
            filter = loop.filter;
            continue;
          }
          if(already){
            error('cannot have more than one loop on a target');
          }
          ls = prop;
          already = true;
        }
      }
      if(!ls){
        error('Error in the selector: ' + sel + '\nA directive action must be a string, a function or a loop(<-)');
      }
      var dsel = loop[ls];
      // if it's a simple data selector then we default to contents, not replacement.
      if(typeof(dsel) === 'string' || typeof(dsel) === 'function'){
        loop = {};
        loop[ls] = {root: dsel};
        return loopgen(dom, sel, loop, fns);
      }
      var spec = parseloopspec(ls),
        itersel = dataselectfn(spec.sel),
        target = gettarget(dom, sel, true),
        nodes = target.nodes;
  
      for(i = 0; i < nodes.length; i++){
        var node = nodes[i],
          inner = compiler(node, dsel);
        fns[fns.length] = wrapquote(target.quotefn, loopfn(spec.name, itersel, inner, sorter, filter));
        target.nodes = [node];    // N.B. side effect on target.
        setsig(target, fns.length - 1);
      }
    }