;(function($) {
    var editLive = function(action, attribute, value) {
        var data = {};
        data[attribute] = value;

        $.post(action, data, function(data) {
            if (typeof(data) == 'string') {
                alert(data);
            } else {
                $.each(data, function(key, value) {
                    if (key == '!redirect') {
                        window.location.href = value;
                        return false;
                    } else if (key[0] == '!') {
                        // unknown command, skip.
                        return;
                    }
                    initForms($('#' + key).html(value).flash());
                });
            }
        });
    }

    // XXX handle the return key too in inputs?
    $(document.body).on('focusout',
            'input[type=text].editlive, textarea.editlive', function(event) {
        var $this = $(this),
            original = $this.data('original'),
            attribute = $this.data('attribute');

        if (this.value == original)
            return;

        editLive($this.data('action'), $this.data('attribute'), this.value);
    });

    $(document.body).on('click',
            'input[type=checkbox].editlive', function(event) {
        var $this = $(this),
            attribute = $this.data('attribute');

        editLive($this.data('action'), $this.data('attribute'),
            $this.attr('checked') ? true : false);
    });

    $(document.body).on('click', 'a.editlive', function(event) {
        event.stopPropagation();
        event.preventDefault();

        var $this = $(this),
            value = $this.data('value'),
            original = $this.data('original');

        if (value == original)
            return;

        editLive($this.data('action'), $this.data('attribute'), value);
    });

    $('form.editlive').each(function() {
        var $form = $(this),
            action = $form.attr('action');

        $form.on('submit', false);
        $form.on('change', 'input[type=text], textarea, select',
            function(event) {
                // TODO what about form prefixes?
                // TODO handle original value
                editLive(action, this.name, this.value);
            });

        $form.on('change', 'input[type=checkbox]', function(event) {
            editLive(action, this.name, this.checked);
        });
    });
})(jQuery);

@@ -17,20 +17,22 @@
     var 
-edit
+update
 Live = f
@@ -43,82 +43,134 @@
 ion(
-action, attribute, value) %7B%0A        var data = %7B%7D;%0A        data%5Battribute%5D
+data) %7B%0A        $.each(data, function(key, value) %7B%0A            if (key == '!redirect') %7B%0A                window.location.href
  = v
@@ -175,25 +175,24 @@
  value;%0A
-%0A
         
 $.post(a
@@ -187,290 +187,446 @@
     
-$.post(action, data, function(data) %7B%0A            if (typeof(data) == 'string') %7B%0A                alert(data);%0A            %7D else %7B%0A                $.each(data, function(key, value) %7B%0A                    if (key == '!redirect') %7B%0A                        window.location.href = 
+        return false;%0A            %7D else if (key%5B0%5D == '!') %7B%0A                // unknown command, skip.%0A                return;%0A            %7D%0A%0A            var elem = $('#' + key),%0A                update = elem.data('update') %7C%7C 'replace';%0A%0A            switch(update) %7B%0A                case 'append':%0A                    elem.append(value);%0A                    break;%0A                case 'prepend':%0A                    elem.prepend(
 value
+)
 ;%0A  
@@ -629,32 +629,45 @@
 ;%0A              
+      break;%0A
           return
@@ -656,36 +656,75 @@
 ;%0A          
-return false
+      default:%0A                    elem.html(value)
 ;%0A          
@@ -716,32 +716,36 @@
 ue);%0A           
+ %7D%0A%0A
          %7D else 
@@ -741,222 +741,550 @@
     
-%7D else if (key%5B0%5D == '!') %7B%0A                        // unknown command, skip.%0A                        return;%0A                    %7D%0A                    initForms($('#' + key).html(value).flash());%0A                %7D
+   elem.flash();%0A%0A            if (window.initForms) %7B%0A                // towel-bootstrap (TODO That's ugly, tight coupling)%0A                initForms(elem);%0A            %7D%0A        %7D);%0A    %7D;%0A    if (!window.updateLive) window.updateLive = updateLive;%0A%0A    var editLive = function(action, attribute, value) %7B%0A        var data = %7B%7D;%0A        data%5Battribute%5D = value;%0A%0A        $.post(action, data, function(data) %7B%0A            if (typeof(data) == 'string') %7B%0A                alert(data);%0A            %7D else %7B%0A                return updateLive(data
 );%0A 

Move actual editLive updater out of handler