     catch e
       console.error('failed to compile', args, script, e)
 
+
+  class Node extends Module
+    @include Events
+
+    matchConstraint = /\${(.+)}/
+    constructor: (el, attributes = {}) ->
+      @subnodes = []
+      # Install types
+      @types = attributes.$types ? {}
+      delete attributes.$types
+
+      # Install methods
+      for methodname, method of attributes.$methods
+        # console.log 'installing method', methodname, method, @
+        installMethod(@, methodname, compileScript.apply(null, method))
+      delete attributes.$methods
+
+      for {name, script, args, type} in attributes.$handlers?
+        name = name.substr(2)
+        # console.log 'installing handler', name, args, type, script, @
+        @bind(name, @eventCallback(name, script, @, args, type))
+      delete attributes.$handlers
+
+      # Bind to event expressions and set attributes
+      for name, value of attributes
+        constraint = value.match?(matchConstraint)
+        if constraint
+          @applyConstraint(name, constraint[1])
+        else if name.indexOf('on') == 0
+          name = name.substr(2)
+          # console.log('binding to event expression', name, value, @)
+          @bind(name, @eventCallback(name, value, @))
+        else
+          @setAttribute(name, value)
+
+      @bindConstraints() if @constraints
+      # console.log 'new node', @, attributes
+      @trigger('init', @) if @events?['init']
+
+    applyConstraint: (name, expression) ->
+      @constraints ?= {}
+      @constraints[name] = compileScript('return ' + expression).bind(@)
+      # console.log 'adding constraint', name, expression, @
+      # console.log 'eval', @constraints[name]()
+
+      constraintBinding = @constraints[name]
+      bindings = constraintBinding.bindings ?= {}
+
+      scopes = propertyBindings.find(expression)
+      # console.log 'found scopes', scopes
+      for scope in scopes
+        bindexpression = scope.binding
+        scope.compiled = compileScript('return ' + bindexpression).bind(@)
+        bindings[bindexpression] = scope
+        # console.log 'applied', scope.property, bindexpression, 'for', @
+
+      # console.log 'matched constraint', name, @, expression
+      return
+
+    setAttribute: (name, value) ->
+      # TODO: add support for dynamic constraints
+      # constraint = value.match?(matchConstraint)
+      # if constraint
+      #   @applyConstraint(name, constraint[1])
+      #   return
+
+      # coerce value to type
+      type = @types[name]
+      if type
+        value = typemappings[type](value) if type of typemappings
+        # console.log 'type', name, type, value, @
+
+      if @[name] != value
+        # console.log 'setAttribute', name, value
+        setter = 'set_' + name
+        if setter of @
+          # console.log 'calling setter', setter, value, @[setter]
+          @[setter](value)
+        else
+          # console.log 'setting style', name, value
+          @[name] = value
+
+      # send event
+      @trigger(name, value, @, name) if @events?[name]
+      @
+
+    # generate a callback for an event expression in a way that preserves scope, e.g. on_x="console.log(value, this, ...)"
+    eventCallback: (name, script, scope, fnargs=['value'], type) ->
+      # console.log 'binding to event expression', name, script, scope
+      js = compileScript(script, fnargs, type)
+      () ->
+        if name of scope
+          args = [scope[name]]
+        else 
+          args = arguments
+        # console.log 'event callback', name, args, scope, js
+        js.apply(scope, args)
+
+    bindConstraints: () ->
+      # register constraints last
+      for name, value of @constraints
+        # console.log 'binding constraint', name, value, @
+        constraint = @constraintCallback(name, value)
+        for bindexpression, binding of @constraints[name].bindings
+          property = binding.property
+          boundref = binding.compiled()
+          boundref ?= boundref.$view
+          # console.log 'binding to', property, 'on', boundref
+          boundref.bind(property, constraint)
+          
+        @setAttribute(name, value())
+
+      return
+
+    # generate a callback for a constraint expression, e.g. x="${this.parent.baz.x + 10}"
+    constraintCallback: (name, fn) ->
+      # console.log('binding to constraint fn', name, fn, @)
+      () =>
+        # console.log 'setting', name, fn(), @
+        @setAttribute(name, fn())
+
+    set_parent: (parent) ->
+      # console.log 'set_parent', parent, @
+      # normalize to jQuery object
+      if parent instanceof Node
+        # store references to parent and children
+        @parent = parent
+        parent[@name] = @ if @name
+        parent.subnodes.push(@)
+        parent.trigger('subnodes', @) if parent.events?['subnodes']
+
+    set_name: (@name) ->
+      # console.log 'set_name', name, this
+      @parent?[name] = @
+
+
+  class Sprite
+#    guid = 0
+    stylemap= {x: 'left', y: 'top', bgcolor: 'background-color'}
+
+    constructor: (@jqel = $('<div/>'), view) ->
+      # console.log 'new sprite', @jqel, view
+      @jqel = $(@jqel) unless @jqel instanceof jQuery
+      @jqel[0].$view = view
+
+      # normalize to jQuery object
+#      guid++
+#      @jqel.attr('id', 'jqel-' + guid) if not @jqel.attr('id')
+      @jqel.addClass('sprite')
+    setStyle: (name, value) ->
+      value ?= ''
+      name = stylemap[name] if name of stylemap
+      # console.log('setStyle', name, value, @jqel[0])
+      @jqel.css(name, value)
+    set_parent: (parent) ->
+      if parent instanceof Sprite
+        parent = parent.jqel
+
+      parent = $(parent) unless parent instanceof jQuery
+      # console.log 'set_parent', parent
+      parent.append(@jqel)
+    set_id: (@id) ->
+      # console.log('setid', @id)
+      @jqel.attr('id', @id)
+    animate: =>
+      # console.log 'sprite animate', arguments, @jqel
+      @jqel.animate.apply(@jqel, arguments)
+
+
+  ignoredAttributes = {parent: true, id: true, name: true, extends: true, type: true}
+  class View extends Node
+    constructor: (el, attributes = {}) ->
+      @subviews = []
+      attributes.$types = {x: 'number', y: 'number', width: 'number', height: 'number'}
+      if (el instanceof HTMLElement and el.$view)
+        console.warn 'already bound view', el.$view, el
+        return
+
+      if (el instanceof View)
+        el = el.sprite
+
+      @sprite = new Sprite(el, @)
+
+      super(el, attributes)
+      # console.log 'new view', el, attributes, @
+
+    gotStyle: (name, value) ->
+      if not ignoredAttributes[name] 
+        # if view.events?[name])
+        _super::setAttribute.apply(@, arguments)
+
+    setAttribute: (name, value) ->
+      if not (ignoredAttributes[name] or @[name] == value)
+        # console.log 'setting style', name, @[name], value, @
+        @sprite.setStyle(name, value)
+      super(name, value)
+
+    set_parent: (parent) ->
+      # console.log 'view set_parent', parent, @
+      super(parent)
+
+      # store references subviews
+      if parent instanceof View
+        parent.subviews.push(@)
+        parent.trigger('subviews', @) if parent.events?['subviews']
+        parent = parent.sprite
+      @sprite.set_parent parent
+
+    set_id: (@id) ->
+      @sprite.set_id(id)
+
+    animate: ->
+      # console.log 'animate', arguments, @sprite.animate
+      @sprite.animate.apply(this, arguments)
+
+  # flatten element.attributes to a hash
+  flattenattributes = (namednodemap)  ->
+    attributes = {}
+    for i in namednodemap
+      attributes[i.name] = i.value
+    attributes
+
+  specialtags = ['handler', 'method', 'attribute', 'setter']
+  # init classes based on an existing element
+  initFromElement = (el, parent) ->
+    tagname = el.localName
+    if not (tagname of lz)
+      console.warn 'could not find class for tag', tagname, el
+      return
+
+    attributes = flattenattributes(el.attributes)
+
+    # swallow builtin mouse attributes to prevent events from the tag
+    for event in mouseEvents
+      el.removeAttribute('on' + event)
+
+    parent ?= el.parentNode
+    attributes.parent = parent if parent?
+    # console.log 'parent', tagname, attributes, parent
+
+    children = (child for child in el.childNodes when child.nodeType == 1)
+
+    if tagname is 'class'
+      for child in children
+        # console.log 'creating child', tagname, child.localName, child, parent
+        # console.dir(child)
+        # console.log 'defer', child
+        child.$defer = true
+        
+    processSpecialTags(el, attributes, attributes.type)
+
+    parent = new lz[tagname](el, attributes)
+
+    unless tagname is 'class'
+      for child in children
+        initFromElement(child, parent) unless child.localName in specialtags
+
+    return
+
+
+  # write default CSS to the DOM 
+  writeDefaultStyle = () ->
+    style = document.createElement('style')
+    style.type = 'text/css'
+    style.innerHTML = '.sprite{position:absolute;} .hidden{display:none;}'
+    document.getElementsByTagName('head')[0].appendChild(style)
+
+  # init all views in the DOM recursively
+  init = (selector = $('view')) ->
+    for el, i in selector
+      initFromElement(el) unless el.$defer or el.$view
+    # listen for jQuery style changes
+    hackstyle(true)
+    # console.log 'caches', compileCache
+
   # http://stackoverflow.com/questions/1248849/converting-sanitised-html-back-to-displayable-html
   htmlDecode = (input) ->
     # return if not input

        args = (attributes.args ? '').split()
        script = htmlDecode(child.innerHTML)
        type = attributes.type or defaulttype
        classattributes.$methods[attributes.name] = compileScript(script, args, type)
        # console.log 'added method', attributes.name, script, classattributes
      else if childname == 'setter'
        args = (attributes.args ? '').split()
        script = htmlDecode(child.innerHTML)
        type = attributes.type or defaulttype
        classattributes.$methods['set_' + attributes.name] = compileScript(script, args, type)
        # console.log 'added setter', 'set_' + attributes.name, args, classattributes.$methods
      else if childname == 'attribute'
        classattributes[attributes.name] = attributes.value

      return true
  # propertyBindings.find = _.memoize(propertyBindings.find)

  compileCache.script ?= {'coffee': {}}
  coffeeCache = compileCache.script.coffee
  compilermappings = 
