text: /**
   * Compile a template, parsed by @see parse
   *
   * @param {documentFragment} template
   * @param {string|undefined} sourceURL - include sourceURL to aid debugging
   *
   * @returns {string} - Function body, accepting Freak instance parameter, suitable for eval()
   */
  function compile(template, sourceURL, depth) {
  
    var ri, rules, rlen;
    var match, block;
  
    // Generate dynamic function body
    var func = '(function(model) {\n' +
      'var frag = document.createDocumentFragment(), node;\n\n';
  
    if (!depth) {
      // Global bookkeeping
      func +=
        'var radioGroups = {};\n' +
        'var radioGroupsUpdating = {};\n' +
        'var selects = [];\n' +
        'var selectsUpdating = [];\n' +
        'var selectOptions = [];\n' +
        'var selectOptionsContexts = [];\n\n';
    }
  
    // Wrap model in a Freak instance, if necessary
    func += 'model = typeof model === "function" ?' +
      'model : ' +
      'typeof model === "object" ?' +
        'lite(model) :' +
        'lite({".": model});\n\n';
  
    // Iterate childNodes
    for (var i = 0, childNodes = template.childNodes, len = childNodes.length, node;
         i < len; i++) {
  
      node = childNodes[i];
  
      switch (node.nodeType) {
  
        // Element node
        case 1:
  
          // lite tag?
          if (node.nodeName === 'SCRIPT' && node.type === 'text/lite-tag') {
  
            for (ri = 0, rules = require('./compile-rules-node'), rlen = rules.length;
                ri < rlen; ri++) {
  
              match = rules[ri](node);
  
              // Rule found?
              if (match) {
  
                // Block tag?
                if (match.block) {
  
                  // Fetch block template
                  block = document.createDocumentFragment();
                  for (i++;
                      (i < len) && !matchEndBlock(match.block, childNodes[i].innerHTML || '');
                      i++) {
                    block.appendChild(childNodes[i].cloneNode(true));
                  }
  
                  if (i === len) {
                    throw 'lite: Unclosed ' + match.block;
                  }
                  else {
                    func += '(' + match.rule.toString() + ')' +
                      '(frag, model, ' +
                      JSON.stringify(match.block) + ', ' +   // prop
                      JSON.stringify(
                        // template
                        compile(
                          block,
                          sourceURL && (sourceURL + '-' + node.innerHTML + '[' + i + ']'),
                          (depth || 0) + 1
                        )
                      ) + ');';
                  }
  
                }
                // Inline tag
                else {
                  func += '(' + match.rule.toString() + ')' +
                    '(frag, model, ' + JSON.stringify(match.prop) + ');\n';
                }
  
                // Skip remaining rules
                break;
              }
            } // end iterating node rules
          }
  
          else {
            // Create element
            func += 'node = document.createElement("' + node.nodeName + '");\n';
  
            // Process attributes
            for (var ai = 0, attributes = node.attributes, alen = attributes.length;
                 ai < alen; ai++) {
  
              for (ri = 0, rules = require('./compile-rules-attr'), rlen = rules.length;
                  ri < rlen; ri++) {
  
                match = rules[ri](node, attributes[ai].name.toLowerCase());
  
                if (match) {
  
                  // Match found, append rule to func
                  func += '(' + match.rule.toString() + ')' +
                    '(node, ' +
                    JSON.stringify(attributes[ai].name) + // attr
                    ', model, ' +
                    JSON.stringify(match.prop) +          // prop
                    ');\n';
  
                  // Skip other attribute rules
                  break;
                }
              }
            }
  
            if (node.nodeName !== 'INPUT') {
              // Recursively compile
              func += 'node.appendChild(' +
                compile(
                  node,
              sourceURL && (sourceURL + '-' + node.nodeName + '[' + i + ']'),
              (depth || 0) + 1
              ) + '(model));\n';
            }
  
            // Append to fragment
            func += 'frag.appendChild(node);\n';
          }
  
          break;
  
  
        // Text node
        case 3:
          func += 'frag.appendChild(document.createTextNode(' +
            JSON.stringify(node.data) + '));\n';
          break;
  
  
        // Comment node
        case 8:
          func += 'frag.appendChild(document.createComment(' +
            JSON.stringify(node.data) + '));\n';
          break;
  
      } // end switch
    } // end iterate childNodes
  
    func += 'return frag; })';
    func += sourceURL ?
      '\n//@ sourceURL=' + sourceURL + '\n//# sourceURL=' + sourceURL + '\n' :
      '';
  
    return func;
  }
  
  
  
  
  function matchEndBlock(block, str) {
    var match = str.match(/^\/([\w\.\-]+)?$/);
    return match ?
      block === '' || !match[1] || match[1] === block :
      false;
  }
  
  
  
  
  module.exports = compile;
  