<commit_before>var immutable = require('immutable')
var Model = require('./model')

var VNode = require('virtual-dom/vnode/vnode')
var VText = require('virtual-dom/vnode/vtext')
var diff  = require('virtual-dom/diff')
var patch = require('virtual-dom/patch')
var createElement = require('virtual-dom/create-element')

var updateHandler = []

module.exports = function bind(target, template, locals) {
  if(!target || target.nodeType !== Node.ELEMENT_NODE) {
    throw new TypeError('Target must be an element node')
  }

  if (!(template instanceof HTMLTemplateElement)) {
    locals = template
    template = undefined
  }

  if (!locals) {
    locals = new immutable.Stack
  }

  if (!immutable.Stack.isStack(locals)) {
    locals = new immutable.Stack(Array.isArray(locals) ? locals : [locals])
  }

  locals = locals.map(function(val) {
    return immutable.fromJS(val)
  })

  var update = prepare(target, template && template.content || target)
  var vdom   = new VNode(target.tagName, {}, [])
  var root   = target //createElement(vdom)
  target.innerHTML = '' // clear
  // target.parentNode.replaceChild(root, target)

  var updateFn = function() {
    var updated = update(locals)
    root = patch(root, diff(vdom, updated))
    vdom = updated
  }

  updateFn()

  var handlers = locals.filter(function(local) {
    return Model.isModel(local)
  }).map(function(local) {
    return {
      before: local.state(),
      model:  local
    }
  })

  if (handlers.size) {
    requestAnimationFrame(function animate() {
      if (!root.parentNode) { // removed from DOM
        return
      }

      var changed = handlers.filter(function(handler) {
        var hasChanged = !handler.model.eql(handler.before)
        if (hasChanged) {
          handler.before = handler.model.state()
          return true
        } else {
          return false
        }
      })

      if (changed.size > 0) {
        updateFn()
      }

      requestAnimationFrame(animate)
    })
  }

  return updateFn
}

function prepare(target, root) {
  var ctx = { nextId: 1 }
  var children = prepareChildren(ctx, root)
  var properties = prepareProperties(ctx, target)
  return function(locals) {
    return new VNode(target.tagName, properties(locals), children(locals))
  }
}

function prepareChildren(ctx, node) {
  var children = []
  for (var child = node.firstChild; child; child = child.nextSibling) {
    switch (child.nodeType) {
      case Node.ELEMENT_NODE:
        children.push.apply(children, visitElementNode(ctx, child))
        break
      case Node.TEXT_NODE:
        children.push.apply(children, visitTextNode(ctx, child))
        break
      default:
        continue
    }
  }

  return function(locals, keySuffix) {
    keySuffix = keySuffix || ''

    var childs = []
    children.forEach(function(child) {
      var vnodes = child(locals, keySuffix)
      if (Array.isArray(vnodes)) {
        childs.push.apply(childs, vnodes)
      } else {
        childs.push(vnodes)
      }
    })
    return childs
  }
}

function prepareProperties(ctx, node) {
  var attributes = {}
  var properties = {}

  for (var i = 0, len = node.attributes.length; i < len; ++i) {
    var attr = node.attributes[i]
    var prop = visitAttributeNode(ctx, node, attr)
    if (typeof prop === 'function') {
      properties[attr.name] = prop
    } else {
      attributes[attr.name] = prop
    }
  }

  return function(locals) {
    var props = { attributes: attributes }
    for (var key in properties) {
      props[key] = properties[key](locals)
    }
    return props
  }
}

var parser = require('./parser')
var ast    = require('./parser/ast')

var TemplateWidget = require('./widget/template')
var RepeatMixin = require('./mixin/repeat')
var IfMixin = require('./mixin/if')
var UnlessMixin = require('./mixin/unless')
var InputHook = require('./hook/input')

function visitElementNode(ctx, node) {
  var tagName    = node.tagName
  var properties = prepareProperties(ctx, node)
  var namespace  = getNamespace(node)

  var id = ctx.nextId++

  if (node instanceof HTMLTemplateElement) {
    var Mixin, prop
    switch (true) {
      case RepeatMixin.isRepeatTemplate(node):
        Mixin = RepeatMixin
        prop  = 'repeat'
        break
      case IfMixin.isIfTemplate(node):
        Mixin = IfMixin
        prop  = 'if'
        break
      case UnlessMixin.isUnlessTemplate(node):
        Mixin = UnlessMixin
        prop  = 'unless'
    }

    if (Mixin) {
      var template = parser.parse(node.getAttribute(prop) || '') || null

      if (!template.isSingleExpression) {
        throw new TypeError('Only one single expression allowd for repeat templates, '
                          + 'got: ' + template.source)
      }

      if (!template.isEmpty) {
        var content = prepareChildren(ctx, node.content)
        return [function(locals, keySuffix) {
          var mixin = new Mixin(id + keySuffix, locals, template)
          return mixin.execute(content) || []
        }]
      }
    }

    return [function(locals, keySuffix) {
      keySuffix = keySuffix || ''
      return new TemplateWidget(id + keySuffix, locals, node, properties)
    }]
  }

  var children = prepareChildren(ctx, node)

  if (node.tagName === 'TEXTAREA') {
    (function() {
      var id = ctx.nextId++
      properties['input-hook'] = function(locals, keySuffix) {
        return new InputHook(id + keySuffix, locals, parser.parse(node.value))
      }
    })()
  }

  return [function(locals, keySuffix) {
    return new VNode(tagName, properties(locals), children(locals), id + keySuffix, namespace)
  }]
}

var TextWidget = require('./widget/text')
var HTMLWidget = require('./widget/html')

function visitTextNode(ctx, node, locals) {
  var template = parser.parse(node.nodeValue)

  if (!template.hasExpressions) {
    var vtext = new VText(node.nodeValue)
    return [function() {<commit_msg>ignore formatting text nodes when building the vdom<commit_after>var immutable = require('immutable')
var Model = require('./model')

var VNode = require('virtual-dom/vnode/vnode')
var VText = require('virtual-dom/vnode/vtext')
var diff  = require('virtual-dom/diff')
var patch = require('virtual-dom/patch')
var createElement = require('virtual-dom/create-element')

var updateHandler = []

module.exports = function bind(target, template, locals) {
  if(!target || target.nodeType !== Node.ELEMENT_NODE) {
    throw new TypeError('Target must be an element node')
  }

  if (!(template instanceof HTMLTemplateElement)) {
    locals = template
    template = undefined
  }

  if (!locals) {
    locals = new immutable.Stack
  }

  if (!immutable.Stack.isStack(locals)) {
    locals = new immutable.Stack(Array.isArray(locals) ? locals : [locals])
  }

  locals = locals.map(function(val) {
    return immutable.fromJS(val)
  })

  var update = prepare(target, template && template.content || target)
  var vdom   = new VNode(target.tagName, {}, [])
  var root   = target //createElement(vdom)
  target.innerHTML = '' // clear
  // target.parentNode.replaceChild(root, target)

  var updateFn = function() {
    var updated = update(locals)
    root = patch(root, diff(vdom, updated))
    vdom = updated
  }

  updateFn()

  var handlers = locals.filter(function(local) {
    return Model.isModel(local)
  }).map(function(local) {
    return {
      before: local.state(),
      model:  local
    }
  })

  if (handlers.size) {
    requestAnimationFrame(function animate() {
      if (!root.parentNode) { // removed from DOM
        return
      }

      var changed = handlers.filter(function(handler) {
        var hasChanged = !handler.model.eql(handler.before)
        if (hasChanged) {
          handler.before = handler.model.state()
          return true
        } else {
          return false
        }
      })

      if (changed.size > 0) {
        updateFn()
      }

      requestAnimationFrame(animate)
    })
  }

  return updateFn
}

function prepare(target, root) {
  var ctx = { nextId: 1 }
  var children = prepareChildren(ctx, root)
  var properties = prepareProperties(ctx, target)
  return function(locals) {
    return new VNode(target.tagName, properties(locals), children(locals))
  }
}

function prepareChildren(ctx, node) {
  var children = []
  for (var child = node.firstChild; child; child = child.nextSibling) {
    switch (child.nodeType) {
      case Node.ELEMENT_NODE:
        children.push.apply(children, visitElementNode(ctx, child))
        break
      case Node.TEXT_NODE:
        children.push.apply(children, visitTextNode(ctx, child))
        break
      default:
        continue
    }
  }

  return function(locals, keySuffix) {
    keySuffix = keySuffix || ''

    var childs = []
    children.forEach(function(child) {
      var vnodes = child(locals, keySuffix)
      if (Array.isArray(vnodes)) {
        childs.push.apply(childs, vnodes)
      } else {
        childs.push(vnodes)
      }
    })
    return childs
  }
}

function prepareProperties(ctx, node) {
  var attributes = {}
  var properties = {}

  for (var i = 0, len = node.attributes.length; i < len; ++i) {
    var attr = node.attributes[i]
    var prop = visitAttributeNode(ctx, node, attr)
    if (typeof prop === 'function') {
      properties[attr.name] = prop
    } else {
      attributes[attr.name] = prop
    }
  }

  return function(locals) {
    var props = { attributes: attributes }
    for (var key in properties) {
      props[key] = properties[key](locals)
    }
    return props
  }
}

var parser = require('./parser')
var ast    = require('./parser/ast')

var TemplateWidget = require('./widget/template')
var RepeatMixin = require('./mixin/repeat')
var IfMixin = require('./mixin/if')
var UnlessMixin = require('./mixin/unless')
var InputHook = require('./hook/input')

function visitElementNode(ctx, node) {
  var tagName    = node.tagName
  var properties = prepareProperties(ctx, node)
  var namespace  = getNamespace(node)

  var id = ctx.nextId++

  if (node instanceof HTMLTemplateElement) {
    var Mixin, prop
    switch (true) {
      case RepeatMixin.isRepeatTemplate(node):
        Mixin = RepeatMixin
        prop  = 'repeat'
        break
      case IfMixin.isIfTemplate(node):
        Mixin = IfMixin
        prop  = 'if'
        break
      case UnlessMixin.isUnlessTemplate(node):
        Mixin = UnlessMixin
        prop  = 'unless'
    }

    if (Mixin) {
      var template = parser.parse(node.getAttribute(prop) || '') || null

      if (!template.isSingleExpression) {
        throw new TypeError('Only one single expression allowd for repeat templates, '
                          + 'got: ' + template.source)
      }

      if (!template.isEmpty) {
        var content = prepareChildren(ctx, node.content)
        return [function(locals, keySuffix) {
          var mixin = new Mixin(id + keySuffix, locals, template)
          return mixin.execute(content) || []
        }]
      }
    }

    return [function(locals, keySuffix) {
      keySuffix = keySuffix || ''
      return new TemplateWidget(id + keySuffix, locals, node, properties)
    }]
  }

  var children = prepareChildren(ctx, node)

  if (node.tagName === 'TEXTAREA') {
    (function() {
      var id = ctx.nextId++
      properties['input-hook'] = function(locals, keySuffix) {
        return new InputHook(id + keySuffix, locals, parser.parse(node.value))
      }
    })()
  }

  return [function(locals, keySuffix) {
    return new VNode(tagName, properties(locals), children(locals), id + keySuffix, namespace)
  }]
}

var TextWidget = require('./widget/text')
var HTMLWidget = require('./widget/html')

function visitTextNode(ctx, node, locals) {
  // ignore formatting
  if (node.nodeValue.match(/^\s+$/)) {
    return []
  }

  var template = parser.parse(node.nodeValue)

  if (!template.hasExpressions) {
    var vtext = new VText(node.nodeValue)
    return [function() {
{'fraction_non_alphanumeric': 0.12667396614166596, 'fraction_numerical': 0.000505348269182178, 'mean_word_length': 2.7779191854915686, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3334532', 'n_tokens_mistral': 3684, 'n_tokens_neox': 3617, 'n_words': 858}