From 3b7a7ec4ebe9ad3f6e0b7afa4a2d6afc53b9f78b Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 12:27:33 +0400
Subject: [PATCH 01/25] success returns object

---
 CHANGELOG.txt                      |  5 +++++
 src/editable-form/editable-form.js | 27 ++++++++++++++++--------
 test/loader.js                     |  3 +--
 test/unit/text.js                  | 33 +++++++++++++++++++++++++++++-
 4 files changed, 57 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 8257188..ee0d897 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,5 +1,10 @@
 X-editable changelog
 =============================
+
+
+Version 1.1.1 wip
+----------------------------   
+[enh] success callback can return object to overwrite submitted value (vitalets)  
        
        
 Version 1.1.0 Nov 27, 2012
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index b6f8018..90e96f9 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -177,13 +177,20 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             //sending data to server
             $.when(this.save(newValueStr))
             .done($.proxy(function(response) {
-                var error;
-                //call success callback. if it returns string --> show error
-                if(error = this.options.success.call(this, response, newValue)) {
-                    this.error(error);
+                //run success callback
+                var res = typeof this.options.success === 'function' ? this.options.success.call(this, response, newValue) : null;
+                
+                //if it returns string --> show error
+                if(res && typeof res === 'string') {
+                    this.error(res);
                     this.showForm();
                     return;
-                }                
+                }     
+                
+                //if it returns object like {newValue: <something>} --> use that value
+                if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
+                    newValue = res.newValue;
+                }                            
 
                 //clear error message
                 this.error(false);   
@@ -398,9 +405,13 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         **/         
         validate: null,
         /**
-        Success callback. Called when value successfully sent on server and response status = 200.
-        Can be used to process json response. If this function returns string - means error occured and string is shown as error message.
-
+        Success callback. Called when value successfully sent on server and **response status = 200**.  
+        Usefull to work with json response. For example, if your backend response can be <code>{success: true}</code>
+        or <code>{success: false, msg: "server error"}</code> you can check it inside this callback.  
+        If it returns **string** - means error occured and string is shown as error message.  
+        If it returns **object like** <code>{newValue: &lt;something&gt;}</code> - it overwrites value, submitted by user.  
+        Otherwise newValue simply rendered into element.
+        
         @property success 
         @type function
         @default null
diff --git a/test/loader.js b/test/loader.js
index ba91063..b1b4a6b 100644
--- a/test/loader.js
+++ b/test/loader.js
@@ -80,8 +80,7 @@ function getAssets(f, c, src, libs) {
         //core
         js.unshift(bootstrap+'js/bootstrap.js')
         css.unshift(bootstrap+'css/bootstrap.css');
-//        css.push(bootstrap+'css/bootstrap.css');
-        //css.unshift(bootstrap+'css/bootstrap-responsive.css');
+        css.unshift(bootstrap+'css/bootstrap-responsive.css');
 
         //editable
         js.push(forms+'editable-form-bootstrap.js');
diff --git a/test/unit/text.js b/test/unit/text.js
index 79f1b90..538e786 100644
--- a/test/unit/text.js
+++ b/test/unit/text.js
@@ -211,7 +211,38 @@ $(function () {
            start();  
         }, timeout);             
         
-      });   
+      });  
+      
+     asyncTest("should show new value if success callback returns object", function () {
+        var newText = 'cd<e>;"',
+            e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo(fx).editable({
+             success: function(response, newValue) {
+                 equal(newValue, newText, 'value in success passed correctly');
+                 return {newValue: 'xyz'};
+             } 
+          });  
+
+        e.click()
+        var p = tip(e);
+
+        ok(p.find('input[type=text]').length, 'input exists')
+        p.find('input').val(newText);
+        p.find('form').submit(); 
+        
+        setTimeout(function() {
+           ok(!p.is(':visible'), 'popover closed');  
+           equal(p.find('.editable-error-block').text(), '', 'no error msg');   
+           equal(e.data('editable').value, 'xyz', 'value ok');   
+           equal(e.text(), 'xyz', 'text ok');   
+           
+           p.find('button[type=button]').click(); 
+           ok(!p.is(':visible'), 'popover was removed');
+           e.remove();    
+           start();  
+        }, timeout);             
+        
+      });      
+       
    
       asyncTest("should submit all required params", function () {
         var e = $('<a href="#" data-pk="1" data-url="post-resp.php">abc</a>').appendTo(fx).editable({

From 36e7f2827fce30a660063c9da2c6071df995075f Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 15:06:04 +0400
Subject: [PATCH 02/25] ajaxOptions parameter

---
 src/editable-form/editable-form.js | 19 +++++++++++----
 test/unit/text.js                  | 37 ++++++++++++++++++++++++++++--
 2 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 90e96f9..225f1de 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -219,7 +219,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         save: function(value) {
             var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this) : this.options.pk,
             send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))),
-            params;
+            params, ajaxOptions;
 
             if (send) { //send to server
                 this.showLoading();
@@ -243,12 +243,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 if(typeof this.options.url === 'function') { //user's function
                     return this.options.url.call(this, params);
                 } else {  //send ajax to server and return deferred object
-                    return $.ajax({
+                    ajaxOptions = $.extend({
                         url     : this.options.url,
                         data    : params,
                         type    : 'post',
                         dataType: 'json'
-                    });
+                    }, this.options.ajaxOptions);
+
+                    return $.ajax(ajaxOptions);
                 }
             }
         }, 
@@ -420,7 +422,16 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             if(!response.success) return response.msg;
         }
         **/          
-        success: function(response, newValue) {}         
+        success: function(response, newValue) {},
+        /**
+        Additional options for ajax request.
+        List of values: http://api.jquery.com/jQuery.ajax
+
+        @property ajaxOptions 
+        @type object
+        @default null
+        **/        
+        ajaxOptions: null         
     };   
 
     /*
diff --git a/test/unit/text.js b/test/unit/text.js
index 538e786..8e56652 100644
--- a/test/unit/text.js
+++ b/test/unit/text.js
@@ -287,6 +287,9 @@ $(function () {
                  equal(resp.data.name, 'username', 'name ok');
                  equal(resp.data.value, newText, 'value ok');
                  equal(resp.data.q, 2, 'additional params ok');
+             },
+             ajaxOptions: {
+                 headers: {"myHeader": "123"}
              } 
           }),  
           newText = 'cd<e>;"'
@@ -303,8 +306,38 @@ $(function () {
            start();  
         }, timeout);             
         
-      });      
-      
+      });    
+              
+     asyncTest("ajaxOptions", function () {
+        var e = $('<a href="#" data-pk="1" data-url="post-options.php">abc</a>').appendTo(fx).editable({
+             name: 'username',
+             ajaxOptions: {
+                 dataType: 'html'
+             } 
+          }),  
+          newText = 'cd<e>;"'
+
+          $.mockjax({
+              url: 'post-options.php',
+              response: function(settings) {
+                 equal(settings.dataType, 'html', 'dataType key ok');
+              }
+          });          
+          
+        e.click()
+        var p = tip(e);
+
+        ok(p.find('input[type=text]').length, 'input exists')
+        p.find('input').val(newText);
+        p.find('form').submit(); 
+        
+        setTimeout(function() {
+           e.remove();    
+           start();  
+        }, timeout);             
+        
+      });            
+                   
       
      asyncTest("submit to url defined as function", function () {
         expect(3);

From 37d10a0db0fafb0a9ce27abd473a9dbf712ba43d Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 15:18:45 +0400
Subject: [PATCH 03/25] update base fiddle: add mockjax

---
 CHANGELOG.txt | 1 +
 README.md     | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index ee0d897..b149c02 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,6 +4,7 @@ X-editable changelog
 
 Version 1.1.1 wip
 ----------------------------   
+[enh] 'ajaxOptions' parameter for advanced ajax configuration (vitalets)  
 [enh] success callback can return object to overwrite submitted value (vitalets)  
        
        
diff --git a/README.md b/README.md
index ca3354d..b8417ad 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ It is a new life of [bootstrap-editable plugin](http://github.com/vitalets/boots
 See **http://vitalets.github.com/x-editable**
 
 ## Reporting issues
-When creating issues please provide jsFiddle example. You can just fork [this fiddle](http://jsfiddle.net/xBB5x/1/) as starting point.  
+When creating issues please provide jsFiddle example. You can just fork [this fiddle](http://jsfiddle.net/xBB5x/5/) as starting point.  
 Your feedback is very appreciated!
 
 ## Contribution

From da44cfd715fc3507f1f0d3ce6b1a5021f82c3448 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 16:20:37 +0400
Subject: [PATCH 04/25] display: inline for inline container

---
 src/containers/editable-container.css | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/containers/editable-container.css b/src/containers/editable-container.css
index 9098275..c0a5c48 100644
--- a/src/containers/editable-container.css
+++ b/src/containers/editable-container.css
@@ -5,4 +5,9 @@
 .editable-container.popover {
 /*   width: 300px;*/  /* debug */
     width: auto; /* without this rule popover does not stretch */
-}
\ No newline at end of file
+}
+
+.editable-container.editable-inline {
+    display: inline; 
+    vertical-align: middle;
+} 
\ No newline at end of file

From ba4a2055f78334234e90f42cee68d2ae754fd6bc Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 16:48:58 +0400
Subject: [PATCH 05/25] inile fixes for loading div

---
 src/containers/editable-container.css | 2 +-
 src/editable-form/editable-form.css   | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/containers/editable-container.css b/src/containers/editable-container.css
index c0a5c48..1a91e98 100644
--- a/src/containers/editable-container.css
+++ b/src/containers/editable-container.css
@@ -10,4 +10,4 @@
 .editable-container.editable-inline {
     display: inline; 
     vertical-align: middle;
-} 
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 1ff18fe..874705a 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -38,7 +38,8 @@
 .editableform-loading {
     background: url('img/loading.gif') center center no-repeat;  
     height: 25px;
-    width: auto;  
+    width: auto; 
+    min-width: 25px; 
 }
 
 .editable-inline .editableform-loading {

From ee13a2b1fbfe627b86a23a2bd82dfde1f0f1175a Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 17:41:50 +0400
Subject: [PATCH 06/25] activate method in checklist

---
 src/inputs/checklist.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 1a5b149..376e80b 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -99,7 +99,11 @@ $(function(){
                html = this.options.limitText.replace('{checked}', $.isArray(value) ? value.length : 0).replace('{count}', this.sourceData.length); 
            }
            $(element).html(html);
-        }
+        },
+        
+       activate: function() {
+           this.$input.find('input[type="checkbox"]').first().focus();
+       }        
     });      
 
     Checklist.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {

From 79847d1d9295caf4abf2061b96a27f867ae4192f Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Wed, 28 Nov 2012 21:24:51 +0400
Subject: [PATCH 07/25] onblur option ready, need test

---
 src/containers/editable-container.js | 133 +++++++++++++++++++++++++--
 src/containers/editable-inline.js    |  10 +-
 src/containers/editable-poshytip.js  |   5 +-
 src/containers/editable-tooltip.js   |   8 +-
 src/element/editable-element.js      |  32 ++-----
 5 files changed, 140 insertions(+), 48 deletions(-)

diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 8cc64c2..0228d55 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -27,7 +27,73 @@ Applied as jQuery method.
             //bind 'destroyed' listener to destroy container when element is removed from dom
             this.$element.on('destroyed', $.proxy(function(){
                 this.destroy();
-            }, this));             
+            }, this)); 
+            
+            //attach document handlers (once)
+            if(!$(document).data('editable-handlers-attached')) {
+                //close all on escape
+                $(document).on('keyup.editable', function (e) {
+                    if (e.which === 27) {
+                        $('.editable-open').editableContainer('hide');
+                        //todo: return focus on element 
+                    }
+                });
+
+                //close containers when click outside
+                $(document).on('click.editable', function(e) {
+                    var element = e.target, 
+                        $target = $(e.target),
+                        $clickedContainer = $target.is('.editable-container') ? $target : $target.closest('.editable-container');
+                    
+                    //if click inside some editableContainer --> find corresponding element                  
+                    if($clickedContainer.length) {
+                         $('.editable-open').each(function(i, el){
+                            if($(el).data('editableContainer').tip()[0] === $clickedContainer[0]) {
+                                element = el;
+                                return false; 
+                            }
+                         }); 
+                    }
+                    
+                    //close all open containers (except one)
+                    EditableContainer.prototype.closeOthers(element);
+                    
+                   /* $('.editable-open').each(function(){
+                        //if click target is editable element --> do nothing with el
+                        if(this === e.target) {
+                            return;
+                        }
+                        
+                        var $el = $(this),
+                            ec = $el.data('editableContainer');
+                        
+                        //if click in some editableContainer and current el is it's owner --> do nothing with el
+                        if($clickedContainer.length && ec.tip()[0] === $clickedContainer[0]) {
+                            return;
+                        }
+                        
+                        //otherwise cancel or submit el's container  
+                        if(ec.options.onblur === 'cancel') {
+                            $el.data('editableContainer').hide();
+                        } else if(ec.options.onblur === 'submit') {
+                            $el.data('editableContainer').tip().find('form').submit();
+                        }
+                    });     */
+                    
+                    //if click inside container --> do nothing
+                   // if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {
+                   /*
+                    if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {
+                        return;
+                    }
+                    
+                    //close all other containers
+                    $('.editable-container').find('.editable-cancel').click();
+                    */
+                });
+                
+                $(document).data('editable-handlers-attached', true);
+            }                        
         },
 
         //split options on containerOptions and formOptions
@@ -93,11 +159,22 @@ Applied as jQuery method.
         /**
         Shows container with form
         @method show()
+        @param {boolean} multi if true - other editable containers will not be closed. Default false.
         **/          
-        show: function () {
+        show: function (multi) {
+            this.$element.addClass('editable-open');
+            if(!multi) {
+                //close all open containers (except one)
+                this.closeOthers(this.$element[0]);  
+            }
+            
+            this.innerShow();
+        },
+        
+        /* internal show method. To be overwritten in child classes */
+        innerShow: function () {
             this.call('show');                
             this.tip().addClass('editable-container');
-
             this.initForm();
             this.tip().find(this.innerCss).empty().append(this.$form);     
             this.$form.editableform('render');            
@@ -108,10 +185,11 @@ Applied as jQuery method.
         @method hide()
         **/         
         hide: function() {  
-            if(!this.tip() || !this.tip().is(':visible')) {
+            if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
                 return;
             }
-            this.call('hide');
+            this.$element.removeClass('editable-open');   
+            this.innerHide();
             /**        
             Fired when container was hidden. It occurs on both save or cancel.
 
@@ -121,6 +199,11 @@ Applied as jQuery method.
             this.$element.triggerHandler('hidden');   
         },
         
+        /* internal hide method. To be overwritten in child classes */
+        innerHide: function () {
+            this.call('hide');       
+        },        
+        
         /**
         Toggles container visibility (show / hide)
         @method toggle()
@@ -210,6 +293,34 @@ Applied as jQuery method.
         **/        
         destroy: function() {
             this.call('destroy');
+        },
+        
+        /*
+        Closes other containers except one related to passed element. 
+        Other containers can be cancelled or submitted (depends on onblur option)
+        */
+        closeOthers: function(element) {
+            $('.editable-open').each(function(i, el){
+                //do nothing with passed element
+                if(el === element) {
+                    return;
+                }
+
+                //otherwise cancel or submit all open containers 
+                var $el = $(el),
+                ec = $el.data('editableContainer');
+
+                if(!ec) {
+                    return;  
+                }
+                
+                if(ec.options.onblur === 'cancel') {
+                    $el.data('editableContainer').hide();
+                } else if(ec.options.onblur === 'submit') {
+                    $el.data('editableContainer').tip().find('form').submit();
+                }
+            });
+
         } 
 
     };
@@ -248,7 +359,7 @@ Applied as jQuery method.
     //store constructor
     $.fn.editableContainer.Constructor = EditableContainer;
 
-    //defaults - must be redefined!
+    //defaults
     $.fn.editableContainer.defaults = {
         /**
         Initial value of form input
@@ -275,7 +386,15 @@ Applied as jQuery method.
         @default true
         @private 
         **/        
-        autohide: true
+        autohide: true,
+        /**
+        Action when click outside container. Can be <code>cancel|submit|ignore</code>
+
+        @property onblur 
+        @type string
+        @default cancel
+        **/        
+        onblur: 'cancel'
     };
 
     /* 
diff --git a/src/containers/editable-inline.js b/src/containers/editable-inline.js
index 92c5455..df585a6 100644
--- a/src/containers/editable-inline.js
+++ b/src/containers/editable-inline.js
@@ -26,7 +26,7 @@
            return this.$form; 
         },
         
-        show: function () {
+        innerShow: function () {
             this.$element.hide();
             
             if(this.$form) {
@@ -40,19 +40,13 @@
             this.$form.editableform('render');
         }, 
         
-        hide: function () {
-            if(!this.tip() || !this.tip().is(':visible')) {
-                return;
-            }            
+        innerHide: function () {
             this.$form.hide(this.options.anim, $.proxy(function() {
                 this.$element.show();
                 //return focus on element
                 if (this.options.enablefocus) {
                     this.$element.focus();
                 }  
-                
-                //trigger event
-                this.$element.triggerHandler('hidden');              
             }, this)); 
         },
         
diff --git a/src/containers/editable-poshytip.js b/src/containers/editable-poshytip.js
index 5cbd427..3488ec9 100644
--- a/src/containers/editable-poshytip.js
+++ b/src/containers/editable-poshytip.js
@@ -28,11 +28,10 @@
             this.call('update', $content);                         
         },        
         
-        show: function () {
+        innerShow: function () {
             this.$form.editableform('render');
-            this.tip().addClass('editable-container');
-
             this.call('show');
+            this.tip().addClass('editable-container');
             this.$form.data('editableform').input.activate();
         },        
          
diff --git a/src/containers/editable-tooltip.js b/src/containers/editable-tooltip.js
index dcfa261..4fbc842 100644
--- a/src/containers/editable-tooltip.js
+++ b/src/containers/editable-tooltip.js
@@ -50,7 +50,7 @@
             return this.container()._find(this.container().element);
         },
         
-        show: function() {
+        innerShow: function() {
             this.call('open');
             this.tip().addClass('editable-container');
             
@@ -62,12 +62,8 @@
             this.$form.editableform('render');             
         },  
         
-        hide: function() {
-            if(!this.tip() || !this.tip().is(':visible')) {
-                return;
-            }            
+        innerHide: function() {
             this.call('close'); 
-            this.$element.triggerHandler('hidden');     
         },
         
         setPosition: function() {
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index f51afec..1a3dd78 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -50,24 +50,6 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 this.value = this.input.str2value($.trim(this.options.value));
             }
             
-            //attach handler to close any container on escape
-            $(document).off('keyup.editable').on('keyup.editable', function (e) {
-                if (e.which === 27) {
-                    $('.editable-container').find('.editable-cancel').click();
-                }
-            }); 
-            
-            //attach handler to close container when click outside
-            $(document).off('click.editable').on('click.editable', function(e) {
-                var $target = $(e.target);
-                //if click inside container --> do nothing
-                if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {
-                    return;
-                }
-                //close all other containers
-                $('.editable-container').find('.editable-cancel').click();
-            });
-            
             //add 'editable' class
             this.$element.addClass('editable');
             
@@ -199,15 +181,16 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 return;
             }
             //stop propagation bacause document listen any click to hide all editableContainers
-            e.stopPropagation();
+            //e.stopPropagation();
             this.toggle();
         },
         
         /**
         Shows container with form
         @method show()
+        @param {boolean} multi if true - other editable containers will not be closed. Default false.
         **/  
-        show: function () {
+        show: function (multi) {
             if(this.options.disabled) {
                 return;
             }
@@ -216,7 +199,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             if(!this.container) {
                 var containerOptions = $.extend({}, this.options, {
                     value: this.value,
-                    autohide: false //element itsef will show/hide container
+                    autohide: false //element will take care to show/hide container
                 });
                 this.$element.editableContainer(containerOptions);
                 this.$element.on({
@@ -229,10 +212,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             }      
                                          
             //hide all other editable containers. Required to work correctly with toggle = manual
-            $('.editable-container').find('.editable-cancel').click();
+            //temp
+            //$('.editable-container').find('.editable-cancel').click();
             
             //show container
-            this.container.show();
+            this.container.show(multi);
         },
         
         /**
@@ -247,7 +231,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             //return focus on element
             if (this.options.enablefocus && this.options.toggle === 'click') {
                 this.$element.focus();
-            }                
+            }   
         },
         
         /**

From 7f306aacbed84c6f39a66f7c56bb9331a26b2bf2 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 12:46:10 +0400
Subject: [PATCH 08/25] onblur option test ready

---
 CHANGELOG.txt       |   1 +
 test/unit/common.js | 189 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 188 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index b149c02..a9260f3 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,6 +4,7 @@ X-editable changelog
 
 Version 1.1.1 wip
 ----------------------------   
+[enh #13] 'onblur' option: to cancel, submit or ignore when user clicks outside the form (vitalets)  
 [enh] 'ajaxOptions' parameter for advanced ajax configuration (vitalets)  
 [enh] success callback can return object to overwrite submitted value (vitalets)  
        
diff --git a/test/unit/common.js b/test/unit/common.js
index 468c48e..22e8da2 100644
--- a/test/unit/common.js
+++ b/test/unit/common.js
@@ -78,7 +78,7 @@
 
         e.click();
         var p = tip(e); 
-        ok(p.is(':visible'), 'popover shown');
+        ok(p.is(':visible'), 'popover shown');   
 
         //todo: for jqueryui phantomjs calcs wrong position. Need investigation
         if(!$.browser.webkit && fc.f !== 'jqueryui') {
@@ -89,7 +89,7 @@
         ok(p.find(':contains("'+title+'")').length, 'title ok');
         e.remove();
       });   
-      
+     /* 
       test("should close all other containers on click on editable", function () {
         var e1 = $('<a href="#" data-pk="1" data-url="post.php" id="a">abc</a>').appendTo('#qunit-fixture').editable(),  
             e2 = $('<a href="#" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();  
@@ -121,6 +121,191 @@
         e1.click();
         ok(!p.is(':visible'), 'popover closed');
      });        
+      */
+      
+      test("onblur: cancel", function () {
+        var oldValue = 'abc',
+            newValue = 'cde',
+            e = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="a">'+oldValue+'</a>').appendTo('#qunit-fixture').editable({
+               onblur: 'cancel',
+               url: function() {}
+            }),  
+            e2 = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();  
+       
+        //click inside                                                              
+        e.click();
+        var p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        p.click();
+        p.find('input').click();
+        ok(p.is(':visible'), 'popover1 still visible');
+                                      
+        //click outside                                                              
+        p.find('input').val(newValue);
+        $('#qunit-fixture').click();
+        ok(!p.is(':visible'), 'popover1 closed');
+        equal(e.data('editable').value, oldValue, 'old value exists');
+        
+        //click on another editable                                                              
+        e.click();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        e2.click();
+        var p2 = tip(e2);
+        ok(!p.is(':visible'), 'popover1 closed');
+        ok(p2.is(':visible'), 'popover2 visible');
+        equal(e.data('editable').value, oldValue, 'old value exists'); 
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed');    
+        
+        //call show method of another editable (multi = false)                                                              
+        e.click();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        e2.editable('show');
+        p2 = tip(e2);
+        ok(!p.is(':visible'), 'popover1 closed');
+        ok(p2.is(':visible'), 'popover2 visible');
+        equal(e.data('editable').value, oldValue, 'old value exists');  
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed');         
+        
+        //call show method of another editable (multi = true)                                                              
+        e.click();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        e2.editable('show', true);
+        p2 = tip(e2);
+        ok(p.is(':visible'), 'popover1 visible');
+        ok(p2.is(':visible'), 'popover2 visible');
+
+        e.editable('hide');
+        e2.editable('hide');
+        ok(!p.is(':visible'), 'popover1 closed');
+        ok(!p2.is(':visible'), 'popover2 closed');
+     });  
+     
+     test("onblur: submit", function () {
+        var oldValue = 'abc',
+            newValue = 'cde',
+            e = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="a">'+oldValue+'</a>').appendTo('#qunit-fixture').editable({
+               onblur: 'submit',
+               url: function() {}
+            }),  
+            e2 = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();  
+        
+        //click inside                                                              
+        e.click();
+        var p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        p.click();
+        p.find('input').click();
+        ok(p.is(':visible'), 'popover1 still visible');        
+        
+        //click outside                                                              
+        p.find('input').val(newValue);
+        $('#qunit-fixture').click();
+        ok(!p.is(':visible'), 'popover1 closed');
+        equal(e.data('editable').value, newValue, 'new value saved');
+        
+        //click on another editable                                                              
+        e.click();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(oldValue);
+        e2.click();
+        var p2 = tip(e2);
+        ok(!p.is(':visible'), 'popover1 closed');
+        ok(p2.is(':visible'), 'popover2 visible');
+        equal(e.data('editable').value, oldValue, 'old value re-saved'); 
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed');    
+        
+        //call show method of another editable (multi = false)                                                              
+        e.click();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        e2.editable('show');
+        p2 = tip(e2);
+        ok(!p.is(':visible'), 'popover1 closed');
+        ok(p2.is(':visible'), 'popover2 visible');
+        equal(e.data('editable').value, newValue, 'new value saved');  
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed');         
+        
+        //call show method of another editable (multi = true)                                                              
+        e.click();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(oldValue);
+        e2.editable('show', true);
+        p2 = tip(e2);
+        ok(p.is(':visible'), 'popover1 visible');
+        ok(p2.is(':visible'), 'popover2 visible');
+
+        e.editable('hide');
+        e2.editable('hide');
+        ok(!p.is(':visible'), 'popover1 closed');
+        ok(!p2.is(':visible'), 'popover2 closed');
+     });         
+      
+     test("onblur: ignore", function () {
+        var oldValue = 'abc',
+            newValue = 'cde',
+            e = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="a">'+oldValue+'</a>').appendTo('#qunit-fixture').editable({
+               onblur: 'ignore',
+               url: function() {}
+            }),  
+            e2 = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();  
+        
+        //click inside                                                              
+        e.click();
+        var p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible');
+        p.find('input').val(newValue);
+        p.click();
+        p.find('input').click();
+        ok(p.is(':visible'), 'popover1 still visible');        
+        
+        //click outside                                                              
+        p.find('input').val(newValue);
+        $('#qunit-fixture').click();
+        ok(p.is(':visible'), 'popover1 still visible'); 
+        
+        //click on another editable                                                              
+        e2.click();
+        var p2 = tip(e2);
+        ok(p.is(':visible'), 'popover1 still visible'); 
+        ok(p2.is(':visible'), 'popover2 visible');
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed');    
+        
+        //call show method of another editable (multi = false)                                                              
+        e2.editable('show');
+        p2 = tip(e2);
+        ok(p.is(':visible'), 'popover1 still visible'); 
+        ok(p2.is(':visible'), 'popover2 visible');
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed'); 
+        
+        //call show method of another editable (multi = true)                                                              
+        e2.editable('show', true);
+        p2 = tip(e2);
+        ok(p.is(':visible'), 'popover1 still visible'); 
+        ok(p2.is(':visible'), 'popover2 visible');
+        e2.editable('hide');
+        ok(!p2.is(':visible'), 'popover2 closed'); 
+
+        e.editable('hide');
+        ok(!p.is(':visible'), 'popover1 closed');
+     });           
+     
       
      test("should not wrap buttons when parent has position:absolute", function () {
         var  d = $('<div style="position: absolute; top: 200px">').appendTo(fx),

From e24322bc52288a342b3a1dd070411bfad8b43201 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 13:39:52 +0400
Subject: [PATCH 09/25] escape method in abstract input

---
 src/inputs/abstract.js  | 9 ++++++++-
 src/inputs/checklist.js | 5 ++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js
index a40d742..6fe6dfe 100644
--- a/src/inputs/abstract.js
+++ b/src/inputs/abstract.js
@@ -46,7 +46,7 @@ To create your own input you should inherit from this class.
         @param {DOMElement} element
        **/       
        value2html: function(value, element) {
-           var html = $('<div>').text(value).html();
+           var html = this.escape(value);
            $(element).html(html);
        },
         
@@ -120,6 +120,13 @@ To create your own input you should inherit from this class.
        **/        
        clear:  function() {
            this.$input.val(null);
+       },
+       
+       /**
+       method to escape html
+       **/
+       escape: function(str) {
+           return $('<div>').text(str).html();
        } 
     };
         
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 376e80b..73c70b7 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -1,5 +1,6 @@
 /**
-List of checkboxes. Internally value stored as javascript array of values.
+List of checkboxes. 
+Internally value stored as javascript array of values.
 
 @class checklist
 @extends list
@@ -49,6 +50,8 @@ $(function(){
        
        value2str: function(value) {
            return $.isArray(value) ? value.join($.trim(this.options.separator)) : '';
+           //it is also possible to sent as array
+           //return value;
        },        
        
        //parse separated string

From a7c71001f6510002ba4e714095c9c6e5dcf94464 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 15:02:00 +0400
Subject: [PATCH 10/25] trim value only if it is string

---
 src/element/editable-element.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index 1a3dd78..6ce4e2d 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -47,7 +47,10 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 this.value = this.input.html2value($.trim(this.$element.html()));
                 isValueByText = true;
             } else {
-                this.value = this.input.str2value($.trim(this.options.value));
+                if(typeof this.options.value === 'string') {
+                   this.options.value = $.trim(this.options.value);
+                }
+                this.value = this.input.str2value(this.options.value);
             }
             
             //add 'editable' class

From c9c8f5894a10eee9a740fcab6576a00cb273c72b Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 15:12:52 +0400
Subject: [PATCH 11/25] comments for docs

---
 src/containers/editable-container.js | 5 +++--
 src/inputs/abstract.js               | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 0228d55..56d7290 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -388,7 +388,8 @@ Applied as jQuery method.
         **/        
         autohide: true,
         /**
-        Action when click outside container. Can be <code>cancel|submit|ignore</code>
+        Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>
+        Setting <code>ignore</code> ignore allows to have several containers open. 
 
         @property onblur 
         @type string
@@ -409,4 +410,4 @@ Applied as jQuery method.
         }
     };    
 
-}(window.jQuery));
\ No newline at end of file
+}(window.jQuery));
diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js
index 6fe6dfe..fe7ed84 100644
--- a/src/inputs/abstract.js
+++ b/src/inputs/abstract.js
@@ -123,7 +123,7 @@ To create your own input you should inherit from this class.
        },
        
        /**
-       method to escape html
+        method to escape html.
        **/
        escape: function(str) {
            return $('<div>').text(str).html();

From c9444d0a5f5f667d75b567663bcb7864a5525f74 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 18:24:23 +0400
Subject: [PATCH 12/25] comments fix

---
 src/editable-form/editable-form.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 225f1de..d5a14ae 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -348,7 +348,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         /**
         Additional params for submit. Function can be used to calculate params dynamically
         @example
-        params: function() {
+        params: function(params) {
             return { a: 1 };
         }
 

From cc7b4ad0b69a35238b3811f00ad3e4412fa2f1b7 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 18:59:04 +0400
Subject: [PATCH 13/25] inputs-ext directory with sample address input

---
 grunt.js                           |  30 +++-----
 src/inputs-ext/address/address.css |   9 +++
 src/inputs-ext/address/address.js  | 107 +++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+), 19 deletions(-)
 create mode 100644 src/inputs-ext/address/address.css
 create mode 100644 src/inputs-ext/address/address.js

diff --git a/grunt.js b/grunt.js
index 3b66d5b..4282da2 100644
--- a/grunt.js
+++ b/grunt.js
@@ -151,7 +151,8 @@ module.exports = function(grunt) {
               'src/element/*.js', 
               'src/inputs/*.js', 
               'src/inputs/date/date.js',
-              'src/inputs/dateui/dateui.js'
+              'src/inputs/dateui/dateui.js',
+              'src/inputs-ext/**/*.js'
               ]
     },
     /*
@@ -192,29 +193,20 @@ module.exports = function(grunt) {
                flatten: true
             }
         },
+        inputs_ext: {
+            files: {
+                '<%= dist %>/inputs-ext/': 'src/inputs-ext/**'
+            },
+            options: {
+               basePath: 'inputs-ext'
+            }            
+        },
         ui_datepicker: {
          files: {
              //copy jquery ui datepicker
              '<%= dist %>/jquery-editable/jquery-ui-datepicker/' : 'src/inputs/dateui/jquery-ui-datepicker/**' 
          }
-        } 
-    },
-    yuidoc: {
-      compile: {
-        name: '<%= pkg.title || pkg.name %>',
-        description: '<%= pkg.description %>',
-        version: '<%= pkg.version %>',
-        url: "<%= pkg.homepage %>",
-      //  logo: 'src/editable-form/img/loading.gif',
-        options: {
-          paths: "src/",
-          ignorePaths: ['src/inputs/date/locales'],
-          outdir: "../docs/",
-//          theme: "simple",
-          themedir: "../yuidoc-theme"
-          //themedir: "../yuidoc-bootstrap-theme-master"
-        }
-      }
+       } 
     },
     
     //compress does not work properly for MAC OS (see https://github.com/vitalets/bootstrap-editable/issues/19)
diff --git a/src/inputs-ext/address/address.css b/src/inputs-ext/address/address.css
new file mode 100644
index 0000000..f37a43b
--- /dev/null
+++ b/src/inputs-ext/address/address.css
@@ -0,0 +1,9 @@
+.editable-address {
+    display: block;
+    margin-bottom: 5px;  
+}
+
+.editable-address span {
+    width: 70px;  
+    display: inline-block;
+}
\ No newline at end of file
diff --git a/src/inputs-ext/address/address.js b/src/inputs-ext/address/address.js
new file mode 100644
index 0000000..7cc562e
--- /dev/null
+++ b/src/inputs-ext/address/address.js
@@ -0,0 +1,107 @@
+/**
+Address editable input.
+Internally value stored as {city: "Moscow", street: "Lenina", building: "15"}
+
+@class address
+@extends abstract
+@final
+@example
+<a href="#" id="address" data-type="address" data-pk="1">awesome</a>
+<script>
+$(function(){
+    $('#address').editable({
+        url: '/post',
+        title: 'Enter city, street and building #',
+        value: {
+            city: "Moscow", 
+            street: "Lenina", 
+            building: "15"
+        }
+    });
+});
+</script>
+**/
+(function ($) {
+    var Address = function (options) {
+        this.init('address', options, Address.defaults);
+    };
+
+    $.fn.editableform.utils.inherit(Address, $.fn.editableform.types.abstract);
+
+    $.extend(Address.prototype, {
+         render: function() {
+             Address.superclass.render.call(this);
+             
+            // this.$input.
+         },
+        
+        
+        value2html: function(value, element) {
+            var html = value.city + ', st. ' + value.street + ', bld. ' + value.building;
+            $(element).text(html); 
+        },
+        
+        html2value: function(html) {        
+          /*
+            you may write parsing method to get value by element's html
+            e.g. "Moscow, st. Lenina, bld. 15" => {city: "Moscow", street: "Lenina", building: "15"}
+            but for complex structures I do not recommend do that. 
+            Better always set value directly via javascript, e.g. 
+            editable({
+                value: {
+                    city: "Moscow", 
+                    street: "Lenina", 
+                    building: "15"
+                }
+            });
+          */ 
+          return null;  
+        },
+      
+       /*
+        method for converting data before sent on server.
+        As jQuery correctly sends objects via ajax, you can just return value
+       */
+       value2str: function(value) {
+           return value;
+       }, 
+       
+       /*
+        this is mainly for parsing value defined in data-value attribute. 
+        If you will always set value by javascript, no need to overwrite it
+       */
+       str2value: function(str) {
+           return str;
+       },                
+       
+       value2input: function(value) {
+           this.$input.find('input[name="city"]').val(value.city);
+           this.$input.find('input[name="street"]').val(value.street);
+           this.$input.find('input[name="building"]').val(value.building);
+       },       
+       
+       input2value: function() { 
+           return {
+              city: this.$input.find('input[name="city"]').val(), 
+              street: this.$input.find('input[name="street"]').val(), 
+              building: this.$input.find('input[name="building"]').val()
+           };
+       },        
+        
+       activate: function() {
+            //set focus on city
+            this.$input.find('input[name="city"]').focus();
+       }  
+    });
+
+    Address.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
+        tpl: '<div><label><span>City: </span><input type="text" name="city" class="span2"></label></div>'+
+             '<div><label><span>Street: </span><input type="text" name="street" class="span2"></label></div>'+
+             '<div><label><span>Building: </span><input type="text" name="building" class="span1"></label></div>',
+             
+        inputclass: 'editable-address'     
+    });
+
+    $.fn.editableform.types.address = Address;
+
+}(window.jQuery));
\ No newline at end of file

From ad420118681ba7d3e7c7b584becb63b2a990a9f7 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 19:09:03 +0400
Subject: [PATCH 14/25] bump ver 1.1.1

---
 README.md    | 2 +-
 package.json | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index b8417ad..f7f61c2 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ Or use grunt's _qunit_ task <code>grunt test</code>. For that you also need to [
 
 5.To build lib + docs:
 * run <code>grunt build</code> in **lib** directory
-* run <code>build data-docs-dist</code> in **gh-pages** directory  
+* run <code>build data-docs-dist-zip</code> in **gh-pages** directory  
 You will get distributive in **lib/dist** and updated docs in **gh-pages/*.html**.  
 Do not edit **index.html** and **docs.html** directly! Instead look at [Handlebars](https://github.com/wycats/handlebars.js) templates in **generator/templates**.
 
diff --git a/package.json b/package.json
index f7cf0e1..e0700a2 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
   "name": "X-editable",
   "title": "X-editable",
   "description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery",
-  "version": "1.1.0",
+  "version": "1.1.1",
   "homepage": "http://github.com/vitalets/x-editable",
   "author": {
     "name": "Vitaliy Potapov",

From cb6cf702b666068fd38e3c376bd617e0ccf06471 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 19:54:57 +0400
Subject: [PATCH 15/25] html tpl fix in address

---
 src/inputs-ext/address/address.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/inputs-ext/address/address.js b/src/inputs-ext/address/address.js
index 7cc562e..ec144b8 100644
--- a/src/inputs-ext/address/address.js
+++ b/src/inputs-ext/address/address.js
@@ -37,7 +37,7 @@ $(function(){
         
         
         value2html: function(value, element) {
-            var html = value.city + ', st. ' + value.street + ', bld. ' + value.building;
+            var html = value.city + ', ' + value.street + ' st., bld. ' + value.building;
             $(element).text(html); 
         },
         
@@ -99,7 +99,7 @@ $(function(){
              '<div><label><span>Street: </span><input type="text" name="street" class="span2"></label></div>'+
              '<div><label><span>Building: </span><input type="text" name="building" class="span1"></label></div>',
              
-        inputclass: 'editable-address'     
+        inputclass: 'editable-address'
     });
 
     $.fn.editableform.types.address = Address;

From c3a3553b295bcc08481f100b7fab38126a57bf70 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 19:55:10 +0400
Subject: [PATCH 16/25] comments

---
 src/editable-form/editable-form.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index d5a14ae..a4b7514 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -11,7 +11,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
 
     var EditableForm = function (element, options) {
         this.options = $.extend({}, $.fn.editableform.defaults, options);
-        this.$element = $(element); //div (usually), containing form. not form tag!
+        this.$element = $(element); //div, containing form. Not form tag!
         this.initInput();
     };
 

From 518b2d88b135d91ef3134864596511d28bb5d5db Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 22:05:40 +0400
Subject: [PATCH 17/25] checklist: replace inArray on loop with non-strict
 comparison

---
 src/inputs/checklist.js | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 73c70b7..7f76b5b 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -71,11 +71,17 @@ $(function(){
             var $checks = this.$input.find('input[type="checkbox"]');
             $checks.removeAttr('checked');
             if($.isArray(value) && value.length) {
-                $checks.each(function(i, el) {
-                    if($.inArray($(el).val(), value) !== -1) {
-                        $(el).attr('checked', 'checked');
-                    }
-                }); 
+               $checks.each(function(i, el) {
+                   var $el = $(el);
+                   // cannot use $.inArray as it performs strict comparison
+                   $.each(value, function(j, val){
+                       /*jslint eqeq: true*/
+                       if($el.val() == val) {
+                       /*jslint eqeq: false*/                           
+                           $el.attr('checked', 'checked');
+                       }
+                   });
+               }); 
             }  
         },  
         

From 5b8f00d01b62734f5a128d5990dd177b4fd10f19 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Thu, 29 Nov 2012 22:06:15 +0400
Subject: [PATCH 18/25] added toggle values dblclick and hover

---
 CHANGELOG.txt                        |   3 +-
 src/containers/editable-container.js |  13 +--
 src/element/editable-element.js      |  45 +++++++----
 test/unit/common.js                  | 114 +++++++++++++++++----------
 test/unit/text.js                    |  12 ---
 5 files changed, 108 insertions(+), 79 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index a9260f3..62f3d74 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,9 +4,10 @@ X-editable changelog
 
 Version 1.1.1 wip
 ----------------------------   
+[enh] added 'inputs-ext' directory with sample input 'address'. They will not be concatenated to main files (vitalets)  
 [enh #13] 'onblur' option: to cancel, submit or ignore when user clicks outside the form (vitalets)  
 [enh] 'ajaxOptions' parameter for advanced ajax configuration (vitalets)  
-[enh] success callback can return object to overwrite submitted value (vitalets)  
+[enh] 'success' callback can return object to overwrite submitted value (vitalets)  
        
        
 Version 1.1.0 Nov 27, 2012
diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 56d7290..9072aa0 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -159,12 +159,12 @@ Applied as jQuery method.
         /**
         Shows container with form
         @method show()
-        @param {boolean} multi if true - other editable containers will not be closed. Default false.
+        @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
         **/          
-        show: function (multi) {
+        show: function (closeAll) {
             this.$element.addClass('editable-open');
-            if(!multi) {
-                //close all open containers (except one)
+            if(closeAll !== false) {
+                //close all open containers (except this)
                 this.closeOthers(this.$element[0]);  
             }
             
@@ -207,12 +207,13 @@ Applied as jQuery method.
         /**
         Toggles container visibility (show / hide)
         @method toggle()
+        @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
         **/          
-        toggle: function() {
+        toggle: function(closeAll) {
             if(this.tip && this.tip().is(':visible')) {
                 this.hide();
             } else {
-                this.show();
+                this.show(closeAll);
             } 
         },
 
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index 6ce4e2d..8b07eb8 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -57,9 +57,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             this.$element.addClass('editable');
             
             //attach click handler. In disabled mode it just prevent default action (useful for links)
-            if(this.options.toggle === 'click') {
+            if(this.options.toggle !== 'manual') {
                 this.$element.addClass('editable-click');
-                this.$element.on('click.editable', $.proxy(this.click, this));
+                this.$element.on(this.options.toggle + '.editable', $.proxy(this.activate, this));
             } else {
                 this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
             }
@@ -100,7 +100,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             this.options.disabled = false;
             this.$element.removeClass('editable-disabled');
             this.handleEmpty();
-            if(this.options.toggle === 'click') {
+            if(this.options.toggle !== 'manual') {
                 if(this.$element.attr('tabindex') === '-1') {    
                     this.$element.removeAttr('tabindex');                                
                 }
@@ -178,22 +178,34 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             }
         },        
         
-        click: function (e) {
+        activate: function (e) {
             e.preventDefault();
             if(this.options.disabled) {
                 return;
             }
-            //stop propagation bacause document listen any click to hide all editableContainers
+            /*
+            stop propagation not required anymore because in document click handler it checks event target
+            */
             //e.stopPropagation();
-            this.toggle();
+            
+            if(this.options.toggle === 'mouseenter') {
+                //for hover only show container
+                this.show(); 
+            } else {
+                /*
+                if toggle = click we should not close all other containers as they will be closed automatically in document click listener
+                */
+                var closeAll = (this.options.toggle !== 'click');
+                this.toggle(closeAll);
+            }
         },
         
         /**
         Shows container with form
         @method show()
-        @param {boolean} multi if true - other editable containers will not be closed. Default false.
+        @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
         **/  
-        show: function (multi) {
+        show: function (closeAll) {
             if(this.options.disabled) {
                 return;
             }
@@ -213,13 +225,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             } else if(this.container.tip().is(':visible')) {
                 return;
             }      
-                                         
-            //hide all other editable containers. Required to work correctly with toggle = manual
-            //temp
-            //$('.editable-container').find('.editable-cancel').click();
             
             //show container
-            this.container.show(multi);
+            this.container.show(closeAll);
         },
         
         /**
@@ -240,12 +248,13 @@ Makes editable any HTML element on the page. Applied as jQuery method.
         /**
         Toggles container visibility (show / hide)
         @method toggle()
+        @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
         **/  
-        toggle: function() {
+        toggle: function(closeAll) {
             if(this.container && this.container.tip().is(':visible')) {
                 this.hide();
             } else {
-                this.show();
+                this.show(closeAll);
             }
         },
         
@@ -462,9 +471,10 @@ Makes editable any HTML element on the page. Applied as jQuery method.
         **/         
         disabled: false,
         /**
-        How to toggle editable. Can be <code>click|manual</code>. 
+        How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>. 
         When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable.  
-        Note: if you are calling <code>show</code> on **click** event you need to apply <code>e.stopPropagation()</code> because container has behavior to hide on any click outside.
+        **Note**: if you call <code>show</code> or <code>toggle</code> inside **click** handler of some DOM element, 
+        you need to apply <code>e.stopPropagation()</code> because containers are being closed on any click on document.
         
         @example
         $('#edit-button').click(function(e) {
@@ -477,6 +487,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
         @default 'click'
         **/          
         toggle: 'click',
+
         /**
         Text shown when element is empty.
 
diff --git a/test/unit/common.js b/test/unit/common.js
index 22e8da2..0edd2d8 100644
--- a/test/unit/common.js
+++ b/test/unit/common.js
@@ -89,39 +89,6 @@
         ok(p.find(':contains("'+title+'")').length, 'title ok');
         e.remove();
       });   
-     /* 
-      test("should close all other containers on click on editable", function () {
-        var e1 = $('<a href="#" data-pk="1" data-url="post.php" id="a">abc</a>').appendTo('#qunit-fixture').editable(),  
-            e2 = $('<a href="#" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();  
-                                                                      
-        e1.click()
-        var p1 = tip(e1);
-        ok(p1.is(':visible'), 'popover1 visible');
-        
-        e2.click()
-        var p2 = tip(e2);
-        ok(p2.is(':visible'), 'popover2 visible');
-        ok(!p1.is(':visible'), 'popover1 closed');
-        
-        p2.find('button[type=button]').click();
-        ok(!p2.is(':visible'), 'popover2 closed');
-      });
-      
-     test("click outside container should hide it", function () {
-        var e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo('#qunit-fixture').editable(),
-            e1 = $('<div>').appendTo('body');
-        
-        e.click();
-        var p = tip(e);
-        ok(p.is(':visible'), 'popover shown');
-        
-        p.click();
-        ok(p.is(':visible'), 'popover still shown');
-        
-        e1.click();
-        ok(!p.is(':visible'), 'popover closed');
-     });        
-      */
       
       test("onblur: cancel", function () {
         var oldValue = 'abc',
@@ -160,7 +127,7 @@
         e2.editable('hide');
         ok(!p2.is(':visible'), 'popover2 closed');    
         
-        //call show method of another editable (multi = false)                                                              
+        //call show method of another editable, closeAll = true (default)                                                              
         e.click();
         p = tip(e);
         ok(p.is(':visible'), 'popover1 visible');
@@ -173,12 +140,12 @@
         e2.editable('hide');
         ok(!p2.is(':visible'), 'popover2 closed');         
         
-        //call show method of another editable (multi = true)                                                              
+        //call show method of another editable, closeAll = false
         e.click();
         p = tip(e);
         ok(p.is(':visible'), 'popover1 visible');
         p.find('input').val(newValue);
-        e2.editable('show', true);
+        e2.editable('show', false);
         p2 = tip(e2);
         ok(p.is(':visible'), 'popover1 visible');
         ok(p2.is(':visible'), 'popover2 visible');
@@ -226,7 +193,7 @@
         e2.editable('hide');
         ok(!p2.is(':visible'), 'popover2 closed');    
         
-        //call show method of another editable (multi = false)                                                              
+        //call show method of another editable, closeAll = true (default)                                                              
         e.click();
         p = tip(e);
         ok(p.is(':visible'), 'popover1 visible');
@@ -239,12 +206,12 @@
         e2.editable('hide');
         ok(!p2.is(':visible'), 'popover2 closed');         
         
-        //call show method of another editable (multi = true)                                                              
+        //call show method of another editable, closeAll = false
         e.click();
         p = tip(e);
         ok(p.is(':visible'), 'popover1 visible');
         p.find('input').val(oldValue);
-        e2.editable('show', true);
+        e2.editable('show', false);
         p2 = tip(e2);
         ok(p.is(':visible'), 'popover1 visible');
         ok(p2.is(':visible'), 'popover2 visible');
@@ -286,7 +253,7 @@
         e2.editable('hide');
         ok(!p2.is(':visible'), 'popover2 closed');    
         
-        //call show method of another editable (multi = false)                                                              
+        //call show method of another editable, closeAll = true (default)
         e2.editable('show');
         p2 = tip(e2);
         ok(p.is(':visible'), 'popover1 still visible'); 
@@ -294,8 +261,8 @@
         e2.editable('hide');
         ok(!p2.is(':visible'), 'popover2 closed'); 
         
-        //call show method of another editable (multi = true)                                                              
-        e2.editable('show', true);
+        //call show method of another editable, closeAll = false                                                              
+        e2.editable('show', false);
         p2 = tip(e2);
         ok(p.is(':visible'), 'popover1 still visible'); 
         ok(p2.is(':visible'), 'popover2 visible');
@@ -317,7 +284,68 @@
         ok(p.find('button').offset().left > p.find('.editable-input').offset().left + p.find('.editable-input').width(), 'buttons left ok');
        
         d.remove();
-     });       
+     });   
+     
+      test("toggle: manual", function () {
+        var e = $('<a href="#" id="a"></a>').appendTo('#qunit-fixture').editable({
+            toggle: 'manual'
+        });
+        
+        e.click();                       
+        ok(!e.data('editableContainer'), 'popover not visible after click');
+        e.editable('show'); 
+        var p = tip(e);
+        ok(p.is(':visible'), 'shown manually');
+     });    
+     
+      test("toggle: dblclick", function () {
+        var e = $('<a href="#" id="a"></a>').appendTo('#qunit-fixture').editable({
+            toggle: 'dblclick'
+        }),
+        p, p2,
+        e2 = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();
+        
+        e.click();
+        ok(!e.data('editableContainer'), 'popover not visible after click');
+
+        e2.click();
+        p2 = tip(e2);                     
+        ok(p2.is(':visible'), 'popover2 visible');         
+        
+        e.dblclick();
+        p = tip(e);                     
+        ok(p.is(':visible'), 'popover1 visible');         
+        ok(!p2.is(':visible'), 'popover2 closed');         
+     });    
+     
+      test("toggle: mouseenter", function () {
+        var e = $('<a href="#" id="a"></a>').appendTo('#qunit-fixture').editable({
+            toggle: 'mouseenter'
+        }),
+        p, p2,
+        e2 = $('<a href="#" data-type="text" data-pk="1" data-url="post.php" id="b">abcd</a>').appendTo('#qunit-fixture').editable();
+        
+        e.click();
+        ok(!e.data('editableContainer'), 'popover not visible after click');
+        
+        e.dblclick();
+        ok(!e.data('editableContainer'), 'popover not visible after dblclick');
+
+        e2.click();   
+        p2 = tip(e2);                     
+        ok(p2.is(':visible'), 'popover2 visible');         
+        
+        e.mouseenter();
+        ok(e.data('editableContainer'), 'container defined');
+        p = tip(e);                     
+        ok(p.is(':visible'), 'popover1 visible');         
+        ok(!p2.is(':visible'), 'popover2 closed'); 
+        
+        //hover once again --> container should stay open
+        e.hover();
+        p = tip(e);
+        ok(p.is(':visible'), 'popover1 visible after second hover');                                      
+     });        
       
       //unfortunatly, testing this feature does not always work in browsers. Tested manually.
       /*
diff --git a/test/unit/text.js b/test/unit/text.js
index 8e56652..f7fc3e9 100644
--- a/test/unit/text.js
+++ b/test/unit/text.js
@@ -39,18 +39,6 @@ $(function () {
         p.find('button[type=button]').click(); 
         ok(!p.is(':visible'), 'popover was removed');
       });           
-      
-     test("option 'toggle' = manual", function () {
-        var e = $('<a href="#" id="a"></a>').appendTo('#qunit-fixture').editable({
-            toggle: 'manual'
-        });
-        
-        e.click();                       
-        ok(!e.data().editableContainer, 'popover not visible after click');
-        e.editable('show'); 
-        var p = tip(e);
-        ok(p.is(':visible'), 'shown manually');
-     });    
      
      asyncTest("should load correct value and save new entered text (and value)", function () {
         var  v = 'ab<b>"',

From 3dc331cf5bb26a4a13510bdda94ba8fdf10c39fc Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 14:20:18 +0400
Subject: [PATCH 19/25] pass object in option method

---
 CHANGELOG.txt                   |  2 ++
 src/element/editable-element.js | 31 +++++++++++++++++++++++++------
 test/unit/api.js                |  8 +++++++-
 3 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 62f3d74..7c17193 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,6 +4,8 @@ X-editable changelog
 
 Version 1.1.1 wip
 ----------------------------   
+[enh] object can be passed in 'option' method to set several options simultaneously (vitalets)  
+[enh #20] toggle editable by 'dblclick' and 'mouseenter' (vitalets)  
 [enh] added 'inputs-ext' directory with sample input 'address'. They will not be concatenated to main files (vitalets)  
 [enh #13] 'onblur' option: to cancel, submit or ignore when user clicks outside the form (vitalets)  
 [enh] 'ajaxOptions' parameter for advanced ajax configuration (vitalets)  
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index 8b07eb8..4d91d58 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -136,10 +136,24 @@ Makes editable any HTML element on the page. Applied as jQuery method.
         Sets new option
         
         @method option(key, value)
-        @param {string} key 
-        @param {mixed} value 
+        @param {string|object} key option name or object with several options
+        @param {mixed} value option new value
+        @example
+        $('.editable').editable('option', 'pk', 2);
         **/          
         option: function(key, value) {
+            //set option(s) by object
+            if(key && typeof key === 'object') {
+               $.each(key, $.proxy(function(k, v){
+                  this.option($.trim(k), v); 
+               }, this)); 
+               return;
+            }
+
+            //set option by string             
+            this.options[key] = value;                          
+            
+            //disabled
             if(key === 'disabled') {
                 if(value) {
                     this.disable();
@@ -148,12 +162,15 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 }
                 return;
             } 
-                       
-            this.options[key] = value;
+            
+            //value
+            if(key === 'value') {
+                this.setValue(value);
+            }
             
             //transfer new option to container! 
             if(this.container) {
-              this.container.option(key, value);  
+                this.container.option(key, value);  
             }
         },              
         
@@ -177,7 +194,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 }
             }
         },        
-        
+        /*
+         show / hide editable container when element triggers event defined by toggle option 
+        */
         activate: function (e) {
             e.preventDefault();
             if(this.options.disabled) {
diff --git a/test/unit/api.js b/test/unit/api.js
index 313e682..47f6ca2 100644
--- a/test/unit/api.js
+++ b/test/unit/api.js
@@ -287,7 +287,7 @@ $(function () {
      });          
     
     
-     test("option method", function () {
+     test("option method (string and object)", function () {
         var e = $('<a href="#" data-url="post.php" data-name="text">abc</a>').appendTo('#qunit-fixture').editable(),
             e1 = $('<a href="#" data-pk="1" data-name="text1">abc</a>').appendTo('#qunit-fixture').editable(),
             url = 'abc';
@@ -296,6 +296,12 @@ $(function () {
             
         equal(e.data('editable').options.pk, 2, 'pk set correctly');
         equal(e1.data('editable').options.pk, 2, 'pk2 set correctly');
+        
+        $('#qunit-fixture a').editable('option', {pk: 3, value: 'abcd'});
+            
+        equal(e.data('editable').options.pk, 3, 'pk set correctly (by object)');
+        equal(e.data('editable').value, 'abcd', 'value set correctly (by object)');        
+        equal(e.text(), 'abcd', 'text set correctly (by object)');        
      });    
      
       asyncTest("'submit' method: client and server validation", function () {

From de7907575ae26d250cb6bad5b4452e8d02b7975e Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 16:33:54 +0400
Subject: [PATCH 20/25] showbuttons option ready

---
 CHANGELOG.txt                                |  3 +-
 README.md                                    |  4 +--
 src/editable-form/editable-form-bootstrap.js |  7 ++--
 src/editable-form/editable-form-jqueryui.js  |  6 +---
 src/editable-form/editable-form.js           | 35 +++++++++++++++++---
 test/unit/common.js                          | 15 ++++++++-
 6 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 7c17193..035888d 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,7 +4,8 @@ X-editable changelog
 
 Version 1.1.1 wip
 ----------------------------   
-[enh] object can be passed in 'option' method to set several options simultaneously (vitalets)  
+[enh] 'showbuttons' option to hide buttons in form (vitalets)  
+[enh] object can be passed in 'option' method to set several options at once (vitalets)  
 [enh #20] toggle editable by 'dblclick' and 'mouseenter' (vitalets)  
 [enh] added 'inputs-ext' directory with sample input 'address'. They will not be concatenated to main files (vitalets)  
 [enh #13] 'onblur' option: to cancel, submit or ignore when user clicks outside the form (vitalets)  
diff --git a/README.md b/README.md
index f7f61c2..aa774c1 100644
--- a/README.md
+++ b/README.md
@@ -56,11 +56,11 @@ Or use grunt's _qunit_ task <code>grunt test</code>. For that you also need to [
 
 5.To build lib + docs:
 * run <code>grunt build</code> in **lib** directory
-* run <code>build data-docs-dist-zip</code> in **gh-pages** directory  
+* run <code>build data-docs-dist</code> in **gh-pages** directory  
 You will get distributive in **lib/dist** and updated docs in **gh-pages/*.html**.  
 Do not edit **index.html** and **docs.html** directly! Instead look at [Handlebars](https://github.com/wycats/handlebars.js) templates in **generator/templates**.
 
-6.Commit changes on <code>dev</code> branch and make pull request as usual.  
+6.Commit changes on <code>dev</code> / <code>gh-pages-dev</code> branch and make pull request as usual.  
 
 Thanks for your support!
 
diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js
index 50f72bb..3388544 100644
--- a/src/editable-form/editable-form-bootstrap.js
+++ b/src/editable-form/editable-form-bootstrap.js
@@ -5,11 +5,8 @@ Editableform based on Twitter Bootstrap
     
       $.extend($.fn.editableform.Constructor.prototype, {
          initTemplate: function() {
-              this.$form = $($.fn.editableform.template);
-              this.$form.find('.editable-error-block').addClass('help-block');
-              
-              //buttons
-              this.$form.find('div.editable-buttons').append($.fn.editableform.buttons);                
+            this.$form = $($.fn.editableform.template); 
+            this.$form.find('.editable-error-block').addClass('help-block');
          }
     });    
     
diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js
index 804b6d2..9f0ea9e 100644
--- a/src/editable-form/editable-form-jqueryui.js
+++ b/src/editable-form/editable-form-jqueryui.js
@@ -4,10 +4,7 @@ Editableform based on jQuery UI
 (function ($) {
     
     $.extend($.fn.editableform.Constructor.prototype, {
-        initTemplate: function() {
-            this.$form = $($.fn.editableform.template);
-
-            //buttons
+        initButtons: function() {
             this.$form.find('.editable-buttons').append($.fn.editableform.buttons);                
             this.$form.find('.editable-submit').button({
                 icons: { primary: "ui-icon-check" },
@@ -17,7 +14,6 @@ Editableform based on jQuery UI
                 icons: { primary: "ui-icon-closethick" },
                 text: false
             }).removeAttr('title');
-
         }
     });
     
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index a4b7514..7373ec7 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -34,9 +34,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         },
         initTemplate: function() {
             this.$form = $($.fn.editableform.template); 
-
-            //buttons
-            this.$form.find('div.editable-buttons').append($.fn.editableform.buttons);              
+        },
+        initButtons: function() {
+            this.$form.find('.editable-buttons').append($.fn.editableform.buttons);
         },
         /**
         Renders editableform
@@ -47,8 +47,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             this.$loading = $($.fn.editableform.loading);        
             this.$element.empty().append(this.$loading);
             this.showLoading();
-
+            
+            //init form template and buttons
             this.initTemplate(); 
+            if(this.options.showbuttons) {
+                this.initButtons();
+            } else {
+                this.$form.find('.editable-buttons').remove();
+            }
 
             /**        
             Fired when rendering starts
@@ -431,7 +437,26 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @type object
         @default null
         **/        
-        ajaxOptions: null         
+        ajaxOptions: null,
+        /**
+        Wether to show buttons or not.
+        Form without buttons can be submitted by Enter (depends on input), by onblur = 'submit' or manually (via submit api method)
+
+        @property showbuttons 
+        @type boolean
+        @default true
+        **/         
+        showbuttons: true,
+        /**
+        Submit strategy. Can be <code>normal|never</code>
+        <code>submit='never'</code> usefull for turning into classic form several inputs and submitting them together manually.
+        Works pretty with <code>showbuttons=false</code>
+
+        @property submit 
+        @type string
+        @default normal
+        **/         
+        submit: 'normal' 
     };   
 
     /*
diff --git a/test/unit/common.js b/test/unit/common.js
index 0edd2d8..61e6658 100644
--- a/test/unit/common.js
+++ b/test/unit/common.js
@@ -345,7 +345,20 @@
         e.hover();
         p = tip(e);
         ok(p.is(':visible'), 'popover1 visible after second hover');                                      
-     });        
+     }); 
+     
+      test("showbuttons: false", function () {
+        var e = $('<a href="#" id="a"></a>').appendTo('#qunit-fixture').editable({
+            showbuttons: false
+        });
+        
+        e.click();                       
+        var p = tip(e);                     
+        ok(p.is(':visible'), 'popover visible');   
+        ok(!p.find('.editable-submit').length, 'submit not rendered');   
+        ok(!p.find('.editable-cancel').length, 'cancel not rendered');   
+        ok(!p.find('.editable-buttons').length, '.editable-buttons block not rendered');   
+     });            
       
       //unfortunatly, testing this feature does not always work in browsers. Tested manually.
       /*

From 49d795bf0d1761f5d0a56904f6555279a691c424 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 18:48:20 +0400
Subject: [PATCH 21/25] activate method

---
 src/containers/editable-container.js | 10 ++++++
 src/element/editable-element.js      | 53 ++++++++++++++--------------
 2 files changed, 36 insertions(+), 27 deletions(-)

diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 9072aa0..864f800 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -322,6 +322,16 @@ Applied as jQuery method.
                 }
             });
 
+        },
+        
+        /**
+        Activates input of visible container (e.g. set focus)
+        @method activate()
+        **/         
+        activate: function() {
+            if(this.tip && this.tip().is(':visible') && this.$form) {
+               this.$form.data('editableform').input.activate(); 
+            }
         } 
 
     };
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index 4d91d58..0e2cfe5 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -56,10 +56,23 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             //add 'editable' class
             this.$element.addClass('editable');
             
-            //attach click handler. In disabled mode it just prevent default action (useful for links)
+            //attach handler activating editable. In disabled mode it just prevent default action (useful for links)
             if(this.options.toggle !== 'manual') {
                 this.$element.addClass('editable-click');
-                this.$element.on(this.options.toggle + '.editable', $.proxy(this.activate, this));
+                this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
+                    e.preventDefault();
+                    //stop propagation not required anymore because in document click handler it checks event target
+                    //e.stopPropagation();
+                    
+                    if(this.options.toggle === 'mouseenter') {
+                        //for hover only show container
+                        this.show(); 
+                    } else {
+                        //when toggle='click' we should not close all other containers as they will be closed automatically in document click listener
+                        var closeAll = (this.options.toggle !== 'click');
+                        this.toggle(closeAll);
+                    }                    
+                }, this));
             } else {
                 this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
             }
@@ -194,30 +207,6 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 }
             }
         },        
-        /*
-         show / hide editable container when element triggers event defined by toggle option 
-        */
-        activate: function (e) {
-            e.preventDefault();
-            if(this.options.disabled) {
-                return;
-            }
-            /*
-            stop propagation not required anymore because in document click handler it checks event target
-            */
-            //e.stopPropagation();
-            
-            if(this.options.toggle === 'mouseenter') {
-                //for hover only show container
-                this.show(); 
-            } else {
-                /*
-                if toggle = click we should not close all other containers as they will be closed automatically in document click listener
-                */
-                var closeAll = (this.options.toggle !== 'click');
-                this.toggle(closeAll);
-            }
-        },
         
         /**
         Shows container with form
@@ -339,7 +328,17 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 this.handleEmpty();
                 this.$element.triggerHandler('render', this);                        
             }, this));
-        }        
+        },
+        
+        /**
+        Activates input of visible container (e.g. set focus)
+        @method activate()
+        **/         
+        activate: function() {
+            if(this.container) {
+               this.container.activate(); 
+            }
+        }                 
     };
 
     /* EDITABLE PLUGIN DEFINITION

From ed836c22711209a57c2bbd660d06bd40aeb5e48b Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 18:48:42 +0400
Subject: [PATCH 22/25] autosubmit method

---
 src/editable-form/editable-form.js | 35 +++++++++++++++++-------------
 src/inputs/abstract.js             |  9 +++++++-
 src/inputs/checklist.js            | 10 ++++++++-
 src/inputs/date/date.js            | 11 +++++++++-
 src/inputs/dateui/dateui.js        | 11 +++++++++-
 src/inputs/select.js               |  8 ++++++-
 6 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 7373ec7..12ae191 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -11,7 +11,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
 
     var EditableForm = function (element, options) {
         this.options = $.extend({}, $.fn.editableform.defaults, options);
-        this.$element = $(element); //div, containing form. Not form tag!
+        this.$element = $(element); //div, containing form. Not form tag! Not editable-element.
         this.initInput();
     };
 
@@ -69,6 +69,11 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 //input
                 this.$form.find('div.editable-input').append(this.input.$input);
 
+                //automatically submit inputs when no buttons shown
+                if(!this.options.showbuttons) {
+                    this.input.autosubmit(); 
+                }
+                
                 //"clear" link
                 if(this.input.$clear) {
                     this.$form.find('div.editable-input').append($('<div class="editable-clear">').append(this.input.$clear));  
@@ -156,11 +161,10 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         submit: function(e) {
             e.stopPropagation();
             e.preventDefault();
-
+            
             var error,
-            //get value from input
-            newValue = this.input.input2value(),
-            newValueStr;
+                newValue = this.input.input2value(), //get new value from input
+                newValueStr;
 
             //validation
             if (error = this.validate(newValue)) {
@@ -168,14 +172,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 this.showForm();
                 return;
             } 
-
+            
             //value as string
             newValueStr = this.input.value2str(newValue);
 
             //if value not changed --> cancel
             /*jslint eqeq: true*/
             if (newValueStr == this.input.value2str(this.value)) {
-                /*jslint eqeq: false*/                
+            /*jslint eqeq: false*/                
                 this.cancel();
                 return;
             } 
@@ -186,14 +190,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 //run success callback
                 var res = typeof this.options.success === 'function' ? this.options.success.call(this, response, newValue) : null;
                 
-                //if it returns string --> show error
+                //if success callback returns string --> show error
                 if(res && typeof res === 'string') {
                     this.error(res);
                     this.showForm();
                     return;
                 }     
                 
-                //if it returns object like {newValue: <something>} --> use that value
+                //if success callback returns object like {newValue: <something>} --> use that value instead of submitted
                 if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
                     newValue = res.newValue;
                 }                            
@@ -446,17 +450,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @type boolean
         @default true
         **/         
-        showbuttons: true,
-        /**
+        showbuttons: false
+        
+        /*todo: 
         Submit strategy. Can be <code>normal|never</code>
-        <code>submit='never'</code> usefull for turning into classic form several inputs and submitting them together manually.
+        <code>submitmode='never'</code> usefull for turning into classic form several inputs and submitting them together manually.
         Works pretty with <code>showbuttons=false</code>
 
-        @property submit 
+        @property submitmode 
         @type string
         @default normal
-        **/         
-        submit: 'normal' 
+        */         
+//        submitmode: 'normal' 
     };   
 
     /*
diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js
index fe7ed84..0da5ddb 100644
--- a/src/inputs/abstract.js
+++ b/src/inputs/abstract.js
@@ -127,7 +127,14 @@ To create your own input you should inherit from this class.
        **/
        escape: function(str) {
            return $('<div>').text(str).html();
-       } 
+       },
+       
+       /**
+        attach handler to automatically submit form when value changed (usefull when buttons not shown)
+       **/       
+       autosubmit: function() {
+        
+       }
     };
         
     Abstract.defaults = {  
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 7f76b5b..60af4ce 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -112,7 +112,15 @@ $(function(){
         
        activate: function() {
            this.$input.find('input[type="checkbox"]').first().focus();
-       }        
+       },
+       
+       autosubmit: function() {
+           this.$input.find('input[type="checkbox"]').on('keydown', function(e){
+               if (e.which === 13) {
+                   $(this).closest('form').submit();
+               }
+           });
+       }
     });      
 
     Checklist.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {
diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js
index 6490021..2fb8eab 100644
--- a/src/inputs/date/date.js
+++ b/src/inputs/date/date.js
@@ -95,7 +95,16 @@ $(function(){
        clear:  function() {
           this.$input.data('datepicker').date = null;
           this.$input.find('.active').removeClass('active');
-       }                
+       },
+       
+       autosubmit: function() {
+           this.$input.on('changeDate', function(e){
+               var $form = $(this).closest('form');
+               setTimeout(function() {
+                   $form.submit();
+               }, 200);
+           });
+       }
 
     });
     
diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js
index 5b39f27..a8978dc 100644
--- a/src/inputs/dateui/dateui.js
+++ b/src/inputs/dateui/dateui.js
@@ -112,7 +112,16 @@ $(function(){
        
        clear:  function() {
            this.$input.datepicker('setDate', null);
-       }   
+       },
+       
+       autosubmit: function() {
+           this.$input.on('mouseup', 'table.ui-datepicker-calendar a.ui-state-default', function(e){
+               var $form = $(this).closest('form');
+               setTimeout(function() {
+                   $form.submit();
+               }, 200);
+           });
+       }
 
     });
     
diff --git a/src/inputs/select.js b/src/inputs/select.js
index 9497ae1..42ffc77 100644
--- a/src/inputs/select.js
+++ b/src/inputs/select.js
@@ -45,7 +45,13 @@ $(function(){
                 text = item.text;
             }
             Select.superclass.constructor.superclass.value2html(text, element);   
-        }        
+        },
+        
+        autosubmit: function() {
+            this.$input.on('change', function(){
+                $(this).closest('form').submit();
+            });
+        }
     });      
 
     Select.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {

From a62afc9180d596014a34f6005a420f38ba6c7a7e Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 19:09:44 +0400
Subject: [PATCH 23/25] autosubmit select test + document click tiny refactor

---
 src/containers/editable-container.js | 52 ++++------------------------
 src/editable-form/editable-form.js   |  2 +-
 test/unit/common.js                  |  8 +++--
 test/unit/select.js                  | 25 +++++++++++++
 4 files changed, 37 insertions(+), 50 deletions(-)

diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 864f800..651eb22 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -41,55 +41,15 @@ Applied as jQuery method.
 
                 //close containers when click outside
                 $(document).on('click.editable', function(e) {
-                    var element = e.target, 
-                        $target = $(e.target),
-                        $clickedContainer = $target.is('.editable-container') ? $target : $target.closest('.editable-container');
+                    var $target = $(e.target);
                     
-                    //if click inside some editableContainer --> find corresponding element                  
-                    if($clickedContainer.length) {
-                         $('.editable-open').each(function(i, el){
-                            if($(el).data('editableContainer').tip()[0] === $clickedContainer[0]) {
-                                element = el;
-                                return false; 
-                            }
-                         }); 
-                    }
-                    
-                    //close all open containers (except one)
-                    EditableContainer.prototype.closeOthers(element);
-                    
-                   /* $('.editable-open').each(function(){
-                        //if click target is editable element --> do nothing with el
-                        if(this === e.target) {
-                            return;
-                        }
-                        
-                        var $el = $(this),
-                            ec = $el.data('editableContainer');
-                        
-                        //if click in some editableContainer and current el is it's owner --> do nothing with el
-                        if($clickedContainer.length && ec.tip()[0] === $clickedContainer[0]) {
-                            return;
-                        }
-                        
-                        //otherwise cancel or submit el's container  
-                        if(ec.options.onblur === 'cancel') {
-                            $el.data('editableContainer').hide();
-                        } else if(ec.options.onblur === 'submit') {
-                            $el.data('editableContainer').tip().find('form').submit();
-                        }
-                    });     */
-                    
-                    //if click inside container --> do nothing
-                   // if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {
-                   /*
-                    if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {
+                    //if click inside some editableContainer --> no nothing  
+                    if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {                
                         return;
+                    } else {
+                        //close all open containers (except one)
+                        EditableContainer.prototype.closeOthers(e.target);
                     }
-                    
-                    //close all other containers
-                    $('.editable-container').find('.editable-cancel').click();
-                    */
                 });
                 
                 $(document).data('editable-handlers-attached', true);
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 12ae191..dcf661f 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -450,7 +450,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @type boolean
         @default true
         **/         
-        showbuttons: false
+        showbuttons: true
         
         /*todo: 
         Submit strategy. Can be <code>normal|never</code>
diff --git a/test/unit/common.js b/test/unit/common.js
index 61e6658..a5e6eb6 100644
--- a/test/unit/common.js
+++ b/test/unit/common.js
@@ -276,7 +276,9 @@
       
      test("should not wrap buttons when parent has position:absolute", function () {
         var  d = $('<div style="position: absolute; top: 200px">').appendTo(fx),
-             e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo(d).editable();
+             e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo(d).editable({
+                 showbuttons: true
+             });
             
         e.click();
         var p = tip(e);
@@ -348,7 +350,7 @@
      }); 
      
       test("showbuttons: false", function () {
-        var e = $('<a href="#" id="a"></a>').appendTo('#qunit-fixture').editable({
+        var e = $('<a href="#" id="a" data-type="text"></a>').appendTo('#qunit-fixture').editable({
             showbuttons: false
         });
         
@@ -357,7 +359,7 @@
         ok(p.is(':visible'), 'popover visible');   
         ok(!p.find('.editable-submit').length, 'submit not rendered');   
         ok(!p.find('.editable-cancel').length, 'cancel not rendered');   
-        ok(!p.find('.editable-buttons').length, '.editable-buttons block not rendered');   
+        ok(!p.find('.editable-buttons').length, '.editable-buttons block not rendered'); 
      });            
       
       //unfortunatly, testing this feature does not always work in browsers. Tested manually.
diff --git a/test/unit/select.js b/test/unit/select.js
index b4b969f..05d37b3 100644
--- a/test/unit/select.js
+++ b/test/unit/select.js
@@ -450,5 +450,30 @@ $(function () {
             start();   
          }, timeout);                              
     });                       
+    
+     asyncTest("autosubmit when showbuttons=false", function () {
+         expect(4);
+         var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo(fx).editable({
+             pk: 1,
+             source: groups,
+             showbuttons: false
+        }),
+        selected = 3;
+
+        e.click();
+        var p = tip(e);
+        equal(p.find('select').val(), e.data('editable').value, 'selected value correct'); 
+
+        p.find('select').val(selected);
+        p.find('select').trigger('change');
+         
+         setTimeout(function() {
+               ok(!p.is(':visible'), 'popover closed');
+               equal(e.data('editable').value, selected, 'new value saved')
+               equal(e.text(), groups[selected], 'text shown correctly') 
+               e.remove();    
+               start();  
+         }, timeout);   
+     });    
      
 });
\ No newline at end of file

From 8df731036a3be2589bc78e1d43ff36a00e47e952 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 19:27:23 +0400
Subject: [PATCH 24/25] comments for docs

---
 src/containers/editable-container.js | 6 +++---
 src/editable-form/editable-form.js   | 4 ++--
 src/element/editable-element.js      | 8 ++++----
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 651eb22..0029eba 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -359,12 +359,12 @@ Applied as jQuery method.
         **/        
         autohide: true,
         /**
-        Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>
-        Setting <code>ignore</code> ignore allows to have several containers open. 
+        Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>.  
+        Setting <code>ignore</code> allows to have several containers open. 
 
         @property onblur 
         @type string
-        @default cancel
+        @default 'cancel'
         **/        
         onblur: 'cancel'
     };
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index dcf661f..a2db423 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -443,8 +443,8 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         **/        
         ajaxOptions: null,
         /**
-        Wether to show buttons or not.
-        Form without buttons can be submitted by Enter (depends on input), by onblur = 'submit' or manually (via submit api method)
+        Wether to show buttons or not.  
+        Form without buttons can be auto-submitted by input or by onblur = 'submit'.
 
         @property showbuttons 
         @type boolean
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index 0e2cfe5..bd23548 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -473,7 +473,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
 
     $.fn.editable.defaults = {
         /**
-        Type of input. Can be <code>text|textarea|select|date</code>
+        Type of input. Can be <code>text|textarea|select|date|checklist</code> and more
 
         @property type 
         @type string
@@ -489,8 +489,8 @@ Makes editable any HTML element on the page. Applied as jQuery method.
         **/         
         disabled: false,
         /**
-        How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>. 
-        When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable.  
+        How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>.   
+        When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable.    
         **Note**: if you call <code>show</code> or <code>toggle</code> inside **click** handler of some DOM element, 
         you need to apply <code>e.stopPropagation()</code> because containers are being closed on any click on document.
         
@@ -535,7 +535,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
         **/          
         enablefocus: false,
         /**
-        Initial value of input
+        Initial value of input. Taken from <code>data-value</code> or element's text.
 
         @property value 
         @type mixed

From 0c5b09fcddadf6acfdb1691914129bb9156926ba Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Fri, 30 Nov 2012 19:28:50 +0400
Subject: [PATCH 25/25] changelog ready

---
 CHANGELOG.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 035888d..6498081 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -2,7 +2,7 @@ X-editable changelog
 =============================
 
 
-Version 1.1.1 wip
+Version 1.1.1 Nov 30, 2012
 ----------------------------   
 [enh] 'showbuttons' option to hide buttons in form (vitalets)  
 [enh] object can be passed in 'option' method to set several options at once (vitalets)