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] 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();
-            }                
+            }   
         },
         
         /**