From df3e2daffd0d68ab11cf1431f80f77174caed615 Mon Sep 17 00:00:00 2001
From: vitalets <vitalets@yandex-team.ru>
Date: Thu, 10 Oct 2013 20:36:36 +0400
Subject: [PATCH] allow `validate` to change submitted value, fixes #400

---
 CHANGELOG.txt                      |  1 +
 src/editable-form/editable-form.js | 15 +++++-
 test/unit/api.js                   | 75 ++++++++++++++++++++++++++++--
 3 files changed, 86 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index b9bd53e..3cb54c2 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -4,6 +4,7 @@ X-editable changelog
 
 Version 1.5.1 wip
 ----------------------------
+[enh #400] allow `validate` to change submitted value (vitalets)
 [enh #396] bs3 popover: placement `auto` (vitalets)
 [bug #357] select2: tags mode with space separator (vitalets)
 [bug #374] dateui: clear button does not submit (vitalets)
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index a59d679..e391ef1 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -199,9 +199,18 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
             //get new value from input
             var newValue = this.input.input2value(); 
 
-            // validation: if validate returns truthy value - means error
+            // validation: if validate returns string or truthy value - means error
+            // if returns object like {newValue: '...'} => submitted value is reassigned to it
             var error = this.validate(newValue);
-            if (error) {
+            if ($.type(error) === 'object' && error.newValue !== undefined) {
+                newValue = error.newValue;
+                this.input.value2input(newValue);
+                if(typeof error.msg === 'string') {
+                    this.error(error.msg);
+                    this.showForm();
+                    return;
+                }
+            } else if (error) {
                 this.error(error);
                 this.showForm();
                 return;
@@ -500,6 +509,8 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
         send: 'auto', 
         /**
         Function for client-side validation. If returns string - means validation not passed and string showed as error.
+        Since 1.5.1 you can modify submitted value by returning object from `validate`: 
+        `{newValue: '...'}` or `{newValue: '...', msg: '...'}`
 
         @property validate 
         @type function
diff --git a/test/unit/api.js b/test/unit/api.js
index 4958f1e..21298e3 100644
--- a/test/unit/api.js
+++ b/test/unit/api.js
@@ -446,11 +446,80 @@ $(function () {
         ok(!e.hasClass('editable-click'), 'editable-click class removed');
         
         equal(e.text(), '', 'emptytext removed');
-        
-        
+             
         
         e.click();
                 
-     });                                 
+     });
+
+
+    asyncTest("'validate' that change value", function () {
+        expect(3);
+
+        var e = $('<a href="#" data-type="text" data-pk="1" data-url="validate-change-ok" data-name="text">abc</a>').appendTo(fx).editable({
+                validate: function(value) {
+                    return {newValue: 'newval'};
+                }
+            });
+
+
+        $.mockjax({
+            url: 'validate-change-ok',
+            response: function(settings) {
+                equal(settings.data.value, 'newval', 'validate-change-ok');
+                this.responseText = '';
+            }
+        });
+       
+        //change value to pass client side validation
+        e.click();
+        var p = tip(e);
+        p.find('input[type=text]').val('cde');
+        p.find('button[type=submit]').click(); 
+       
+        setTimeout(function() {
+             equal(e.data('editable').value, 'newval', 'new value saved');
+             ok(!p.is(':visible'), 'popover closed');
+
+             e.remove();    
+             start();  
+        }, timeout);                 
+       
+     });                                         
   
+      asyncTest("'validate' that change value and shows message", function () {
+        expect(3);
+
+        var e = $('<a href="#" data-type="text" data-url="validate-change-error" data-name="text">abc</a>').appendTo(fx).editable({
+                validate: function(value) {
+                    return {newValue: 'newval', msg: 'error!'};
+                }
+            });
+
+
+        $.mockjax({
+            url: 'validate-change-error',
+            response: function(settings) {
+                ok(true, 'should not call');
+                this.responseText = '';
+            }
+        });
+       
+        //change value to pass client side validation
+        e.click();
+        var p = tip(e);
+        p.find('input[type=text]').val('cde');
+        p.find('button[type=submit]').click(); 
+       
+        setTimeout(function() {
+            ok(p.is(':visible'), 'popover visible');
+            equal(p.find('input[type=text]').val(), 'newval', 'new value shown in input');
+            equal(p.find('.editable-error-block').text(), 'error!', 'error msg shown');  
+
+            e.remove();    
+            start();  
+        }, timeout);                 
+       
+     }); 
+
 });