 # TODO mit canvas tag kommt man direkt auf die browser render ticks.
 
 
+# multiple changes of this context possible
+singlton_callback = (that, callback) ->
+    req = -> callback?.apply(that, arguments)
+    req.replace = (replacement) -> that = req.that = replacement
+    req.that = that
+#     req.callback = callback?.callback
+#     req.cancel = callback?.cancel
+    return req
+
+cancelable_callback = (callback) ->
+    canceled = no
+    cb = -> callback?() unless canceled
+    cb.cancel = -> canceled = yes
+    cb.cancel.reset = -> canceled = no
+#     cb.callback = callback?.callback
+#     cb.replace = callback?.replace
+    return cb
+
+deferred_callbacks = () ->
+    done = no
+    callbacks = []
+    res = (cb) ->
+        return cb?() if done
+        callbacks.push(cb)
+    allowed = null
+    res.callback = ->
+        return (->) if done
+        callback = ->
+            if callback is allowed
+                while (cb = callbacks.shift())?
+                    cb?(arguments...)
+                callbacks = null
+                allowed = null
+                done = yes
+        allowed = callback
+        return callback
+    return res
+
+cancelable_and_retrivable_callbacks = () ->
+    canceled = no
+    res = (cb) ->
+        return ->
+            if canceled
+                res.callbacks.push(cb)
+            else
+                cb?(arguments...)
+    res.cancel = -> canceled = yes
+    res.reset = -> canceled = no
+    res.callbacks = []
+    return res
+
+
 removed = (el) ->
     el.closed is "removed"
 
+
+# NOTE delay and release are only for manipulations where no parent is needed
 # delay or invoke job immediately
 delay = (job) ->
     return if removed this


    initialize: () ->
        do @listen
        @builder._jquery_tracker = el:@builder
        # override query
        old_query = @builder.query
        @builder.query = (type, tag, key) ->

    # eventlisteners

    onadd: (parent, el) ->
        that = this
        # `this` changes in onreplace
        el._jquery_insert ?= singlton_callback el, ->
            return if removed this
            if @parent is @parent.builder
                $root = @parent._jquery.parent()
                if $root.length is 0
                    jq = @parent._jquery
                    jq = jq.add(@_jquery)
                    if @parent is that.builder
                        that.template.jquery = jq
                        that.template._jquery = jq
                        @parent.jquery = jq
                    @parent._jquery = jq
                else
                    $root.append(@_jquery)
            else
                @parent._jquery?.append(@_jquery)
            @_jquery_ready?()
            @_jquery_ready = yes
            @_jquery_insert = yes
        el._jquery_insert.replace?(el)

        el._jquery_manip ?= cancelable_and_retrivable_callbacks()
        el._jquery_done ?= deferred_callbacks()
        el.ready(el._jquery_done.callback())


    onclose: (el) ->
        if el is el.builder
            el._jquery ?= $([], el.parent._jquery)
        else
            el._jquery ?= $(el.toString(), el.parent._jquery)
        release.call el


        insertion = el._jquery_insert
        insertion = el._jquery_replace if insertion is yes
        if insertion is undefined
            @onadd(el.parent, el)
            insertion = el._jquery_insert
        el.parent?._jquery_done? =>
            if el.parent is el.parent.builder
                insertion()
            else
                @animation.push(insertion)
            # if manip left from last time run them
            insertion?.that?._jquery_manip?.reset()
            while (cb = insertion?.that?._jquery_manip?.callbacks.shift())?
                @animation.push(cb)

    onreplace: (oldtag, newtag) ->
        newtag._jquery_insert ?= oldtag._jquery_insert
        oldtag._jquery_insert = null

        if newtag._jquery_insert is true
            newtag._jquery_replace ?= oldtag._jquery_replace
            noreplacerequest = newtag._jquery_replace?
            newtag._jquery_replace ?= singlton_callback newtag, ->
                return if removed this
                _jquery = @_jquery ? this
#                 return unless _jquery?.length
                oldtag._jquery.replaceWith(_jquery)
                # replaceWith isnt inplace
                @_jquery = _jquery
                if this is @builder
                    @jquery = _jquery
            oldtag._jquery_replace = null
            replace = newtag._jquery_replace
            replace.replace?(newtag)
        else
            newtag._jquery_insert.replace?(newtag)

        newtag._jquery_manip ?= cancelable_and_retrivable_callbacks()
        oldtag._jquery_manip?.cancel?()
        newtag._jquery_manip.reset()
        unless newtag.closed
            # if manip left from last time run them
            while (cb = newtag._jquery_manip.callbacks.shift())?
                @animation.push(cb)

        newtag._jquery_done ?= oldtag._jquery_done
        newtag._jquery_done ?= deferred_callbacks()
        oldtag._jquery_done = null
        cb = newtag._jquery_done.callback()
        if newtag is newtag.builder
            cb()
        else unless newtag.closed
            newtag.ready(cb)
        if (newtag._jquery_insert is true and newtag.closed) or noreplacerequest
            @onclose(newtag)
        # else just throw cb away, but reset allowed callback

    ontext: (el, text) ->
        that = this
        delay.call el, ->
            that.animation.push @_jquery_manip =>
                @_jquery.text(text)

    onraw: (el, html) ->
        that = this
        delay.call el, ->
            that.animation.push @_jquery_manip =>
                @_jquery?.html(html)

    onattr: (el, key, value) ->
        that = this
        delay.call el, ->
            that.animation.push @_jquery_manip =>
                if value is undefined
                    @_jquery.removeAttr(key)
                else
                    @_jquery.attr(key, value)

    onshow: (el) ->
        delay.call el, ->
            @_jquery.show()
