From d2f988d545530f769c25d657b576f1e9938c1729 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 12:12:26 +0400
Subject: [PATCH 01/24] list input + checklist alpha

---
 CHANGELOG.txt                      |   6 +-
 src/editable-form/editable-form.js |  15 +-
 src/inputs/checklist.js            | 150 +++++++++++++++++
 src/inputs/date/date.js            |   1 +
 src/inputs/dateui/dateui.js        |   1 +
 src/inputs/list.js                 | 255 +++++++++++++++++++++++++++++
 src/inputs/select.js               | 236 ++------------------------
 src/inputs/text.js                 |   1 +
 src/inputs/textarea.js             |   1 +
 test/loader.js                     |   4 +-
 10 files changed, 444 insertions(+), 226 deletions(-)
 create mode 100644 src/inputs/checklist.js
 create mode 100644 src/inputs/list.js

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 90c417d..da494b4 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,6 +1,10 @@
 X-editable changelog
 =============================
-          
+       
+Version 1.0.2 wip
+----------------------------   
+[enh] updated docs: inputs dropdown menu, global templates section (vitalets)      
+                            
 
 Version 1.0.1 Nov 22, 2012
 ----------------------------          
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 8a975b0..d93042a 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -17,7 +17,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
 
     EditableForm.prototype = {
         constructor: EditableForm,
-        initInput: function() {
+        initInput: function() {  //called once
             var TypeConstructor, typeOptions;
             
             //create input of specified type
@@ -247,7 +247,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         
        option: function(key, value) {
           this.options[key] = value;
-       }        
+          if(key === 'value') {
+              this.setValue(value);
+          }
+       },
+       
+       setValue: function(value, convertStr) {
+          if(convertStr) {
+              this.value = this.input.str2value(value);
+          } else {
+              this.value = value;
+          }
+       }               
     };
 
     /*
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
new file mode 100644
index 0000000..158d509
--- /dev/null
+++ b/src/inputs/checklist.js
@@ -0,0 +1,150 @@
+/**
+Checklist input. Internally value stored as array.
+
+@class checklist
+@extends list
+@final
+@example
+<a href="#" id="status" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a>
+<script>
+$(function(){
+    $('#options').editable({
+        value: [2, 3],    
+        source: [
+              {value: 1, text: 'Active'},
+              {value: 2, text: 'Blocked'},
+              {value: 3, text: 'Deleted'}
+           ]
+        }
+    });
+});
+</script>
+**/
+(function ($) {
+
+    var Checklist = function (options) {
+        this.init('checklist', options, Checklist.defaults);
+    };
+
+    $.fn.editableform.utils.inherit(Checklist, $.fn.editableform.types.list);
+
+    $.extend(Checklist.prototype, {
+        renderList: function() {
+            var $label, $div;
+            if(!$.isArray(this.sourceData)) {
+                return;
+            }
+
+            for(var i=0; i<this.sourceData.length; i++) {
+                $label = $('<label>').text(' '+this.sourceData[i].text)
+                                     .prepend($('<input>', {
+                                           type: 'checkbox',
+                                           value: this.sourceData[i].value, 
+                                           name: this.options.name
+                                     }));
+                
+                $('<div>').append($label).appendTo(this.$input);
+            }
+        },
+       
+       value2str: function(value) {
+           return $.isArray(value) ? value.join($.trim(this.options.separator)) : '';
+       },        
+       
+       //parse separated string
+        str2value: function(str) {
+           var reg, value = null;
+           if(typeof str === 'string' && str.length) {
+               reg = new RegExp('\s*'+$.trim(this.options.separator)+'\s*');
+               value = str.split(reg);
+           } else if($.isArray(str)) {
+               value = str; 
+           }
+           return value;
+        },       
+       
+       //set checked on required checkboxes
+       value2input: function(value) {
+            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');
+                    }
+                }); 
+            }  
+        },  
+        
+       input2value: function() { 
+           var checked = [];
+           this.$input.find('input:checked').each(function(i, el) {
+               checked.push($(el).val());
+           });
+           return checked;
+       },            
+          
+       //collect text of checked boxes
+        value2htmlFinal: function(value, element) {
+           var selected = [], html = '';
+           if($.isArray(value) && value.length <= this.options.limit) {
+               for(var i=0; i<value.length; i++){
+                   item = this.itemByVal(value[i]);
+                   if(item) {
+                       selected.push($('<div>').text(item.text).html());
+                   }
+               }
+               html = selected.join(this.options.viewseparator);
+           } else {  
+               html = this.options.limitText.replace('{checked}', $.isArray(value) ? value.length : 0).replace('{count}', this.sourceData.length); 
+           }
+           $(element).html(html);
+        }
+    });      
+
+    Checklist.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {
+        /**
+        @property tpl 
+        @default <div></div>
+        **/         
+        tpl:'<div></div>',
+        
+        /**
+        Separator of values in string when sending to server
+
+        @property separator 
+        @type string
+        @default ', '
+        **/         
+        separator: ',',
+        /**
+        Separator of text when display as element content.
+
+        @property viewseparator 
+        @type string
+        @default ', '
+        **/         
+        viewseparator: '<br>',
+        /**
+        Maximum number of items shown as element content. 
+        If checked more items - <code>limitText</code> will be shown.
+
+        @property limit 
+        @type integer
+        @default 4
+        **/         
+        limit: 4,
+        /**
+        Text shown when count of checked items is greater than <code>limit</code> parameter.
+        You can use <code>{checked}</code> and <code>count</code> placeholders.
+
+        @property limitText 
+        @type string
+        @default 'Checked {checked} options of {count}'
+        **/         
+        limitText: 'Checked {checked} options of {count}'        
+    });
+
+    $.fn.editableform.types.checklist = Checklist;      
+
+}(window.jQuery));
\ No newline at end of file
diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js
index 38680e8..5081db2 100644
--- a/src/inputs/date/date.js
+++ b/src/inputs/date/date.js
@@ -5,6 +5,7 @@ For localization you can include js file from here: https://github.com/eternicod
 
 @class date
 @extends abstract
+@final
 @example
 <a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-original-title="Select date">15/05/1984</a>
 <script>
diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js
index 53ee858..38cc2d3 100644
--- a/src/inputs/dateui/dateui.js
+++ b/src/inputs/dateui/dateui.js
@@ -5,6 +5,7 @@ Do not use it together with bootstrap-datepicker.
 
 @class dateui
 @extends abstract
+@final
 @example
 <a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-original-title="Select date">15/05/1984</a>
 <script>
diff --git a/src/inputs/list.js b/src/inputs/list.js
new file mode 100644
index 0000000..8dba070
--- /dev/null
+++ b/src/inputs/list.js
@@ -0,0 +1,255 @@
+/**
+List - abstract class for inputs that have source option loaded from js array or via ajax
+
+@class list
+@extends abstract
+**/
+(function ($) {
+
+    var List = function (options) {
+       
+    };
+
+    $.fn.editableform.utils.inherit(List, $.fn.editableform.types.abstract);
+
+    $.extend(List.prototype, {
+        render: function () {
+            List.superclass.render.call(this);
+            var deferred = $.Deferred();
+            this.error = null;
+            this.sourceData = null;
+            this.prependData = null;
+            this.onSourceReady(function () {
+                this.renderList();
+                deferred.resolve();
+            }, function () {
+                this.error = this.options.sourceError;
+                deferred.resolve();
+            });
+
+            return deferred.promise();
+        },
+
+        html2value: function (html) {
+            return null; //can't set value by text
+        },
+        
+        value2html: function (value, element) {
+            var deferred = $.Deferred();
+            this.onSourceReady(function () {
+                this.value2htmlFinal(value, element);
+                deferred.resolve();
+            }, function () {
+                List.superclass.value2html(this.options.sourceError, element);
+                deferred.resolve();
+            });
+
+            return deferred.promise();
+        },  
+
+        // ------------- additional functions ------------
+
+        onSourceReady: function (success, error) {
+            //if allready loaded just call success
+            if($.isArray(this.sourceData)) {
+                success.call(this);
+                return; 
+            }
+
+            // try parse json in single quotes (for double quotes jquery does automatically)
+            try {
+                this.options.source = $.fn.editableform.utils.tryParseJson(this.options.source, false);
+            } catch (e) {
+                error.call(this);
+                return;
+            }
+
+            //loading from url
+            if (typeof this.options.source === 'string') {
+                var cacheID = this.options.source + (this.options.name ? '-' + this.options.name : ''),
+                cache;
+
+                if (!$(document).data(cacheID)) {
+                    $(document).data(cacheID, {});
+                }
+                cache = $(document).data(cacheID);
+
+                //check for cached data
+                if (cache.loading === false && cache.sourceData) { //take source from cache
+                    this.sourceData = cache.sourceData;
+                    success.call(this);
+                    return;
+                } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
+                    cache.callbacks.push($.proxy(function () {
+                        this.sourceData = cache.sourceData;
+                        success.call(this);
+                    }, this));
+
+                    //also collecting error callbacks
+                    cache.err_callbacks.push($.proxy(error, this));
+                    return;
+                } else { //no cache yet, activate it
+                    cache.loading = true;
+                    cache.callbacks = [];
+                    cache.err_callbacks = [];
+                }
+
+                //loading sourceData from server
+                $.ajax({
+                    url: this.options.source,
+                    type: 'get',
+                    cache: false,
+                    data: {name: this.options.name},
+                    dataType: 'json',
+                    success: $.proxy(function (data) {
+                        cache.loading = false;
+                        // this.options.source = data;
+                        this.sourceData = this.makeArray(data);
+                        if($.isArray(this.sourceData)) {
+                            this.doPrepend();
+                            //store result in cache
+                            cache.sourceData = this.sourceData;
+                            success.call(this);
+                            $.each(cache.callbacks, function () { this.call(); }); //run success callbacks for other fields
+                        } else {
+                            error.call(this);
+                            $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
+                        }
+                    }, this),
+                    error: $.proxy(function () {
+                        cache.loading = false;
+                        error.call(this);
+                        $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
+                    }, this)
+                });
+            } else { //options as json/array
+                this.sourceData = this.makeArray(this.options.source);
+                if($.isArray(this.sourceData)) {
+                    this.doPrepend();
+                    success.call(this);   
+                } else {
+                    error.call(this);
+                }
+            }
+        },
+
+        doPrepend: function () {
+            if(this.options.prepend === null || this.options.prepend === undefined) {
+                return;  
+            }
+            
+            if(!$.isArray(this.prependData)) {
+                //try parse json in single quotes
+                this.options.prepend = $.fn.editableform.utils.tryParseJson(this.options.prepend, true);
+                if (typeof this.options.prepend === 'string') {
+                    this.options.prepend = {'': this.options.prepend};
+                }              
+                this.prependData = this.makeArray(this.options.prepend);
+            }
+
+            if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
+                this.sourceData = this.prependData.concat(this.sourceData);
+            }
+        },
+
+        /*
+         renders input list
+        */
+        renderList: function() {
+            // this method should be overwritten in child class
+        },
+       
+         /*
+         set element's html by value
+        */
+        value2htmlFinal: function(value, element) {
+            // this method should be overwritten in child class
+        },        
+
+        /**
+        * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
+        */
+        makeArray: function(data) {
+            var count, obj, result = [], iterateEl;
+            if(!data || typeof data === 'string') {
+                return null; 
+            }
+
+            if($.isArray(data)) { //array
+                iterateEl = function (k, v) {
+                    obj = {value: k, text: v};
+                    if(count++ >= 2) {
+                        return false;// exit each if object has more than one value
+                    }
+                };
+            
+                for(var i = 0; i < data.length; i++) {
+                    if(typeof data[i] === 'object') {
+                        count = 0;
+                        $.each(data[i], iterateEl);
+                        if(count === 1) {
+                            result.push(obj); 
+                        } else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) {
+                            result.push(data[i]);
+                        } else {
+                            //data contains incorrect objects
+                        }
+                    } else {
+                        result.push({value: i, text: data[i]}); 
+                    }
+                }
+            } else {  //object
+                $.each(data, function (k, v) {
+                    result.push({value: k, text: v});
+                });  
+            }
+            return result;
+        },
+        
+        //search for item by particular value
+        itemByVal: function(val) {
+            if($.isArray(this.sourceData)) {
+                for(i=0; i<this.sourceData.length; i++){
+                    /*jshint eqeqeq: false*/
+                    if(this.sourceData[i].value == val) {
+                    /*jshint eqeqeq: true*/                            
+                        return this.sourceData[i];
+                    }
+                }
+            }
+        }        
+
+    });      
+
+    List.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
+        /**
+        Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array.
+        Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br>
+        For compability it also supports format <code>{value1: text1, value2: text2 ...}</code> but it does not guarantee elements order.      
+
+        @property source 
+        @type string|array|object
+        @default null
+        **/         
+        source:null, 
+        /**
+        Data automatically prepended to the begining of dropdown list.
+        
+        @property prepend 
+        @type string|array|object
+        @default false
+        **/         
+        prepend:false,
+        /**
+        Error message when list cannot be loaded (e.g. ajax error)
+        
+        @property sourceError 
+        @type string
+        @default Error when loading list
+        **/          
+        sourceError: 'Error when loading list'
+    });
+
+    $.fn.editableform.types.list = List;      
+
+}(window.jQuery));
\ No newline at end of file
diff --git a/src/inputs/select.js b/src/inputs/select.js
index 8cda196..89b4265 100644
--- a/src/inputs/select.js
+++ b/src/inputs/select.js
@@ -2,7 +2,8 @@
 Select (dropdown) input
 
 @class select
-@extends abstract
+@extends list
+@final
 @example
 <a href="#" id="status" data-type="select" data-pk="1" data-url="/post" data-original-title="Select status"></a>
 <script>
@@ -25,160 +26,10 @@ $(function(){
         this.init('select', options, Select.defaults);
     };
 
-    $.fn.editableform.utils.inherit(Select, $.fn.editableform.types.abstract);
+    $.fn.editableform.utils.inherit(Select, $.fn.editableform.types.list);
 
     $.extend(Select.prototype, {
-        render: function () {
-            Select.superclass.render.call(this);
-            var deferred = $.Deferred();
-            this.error = null;
-            this.sourceData = null;
-            this.prependData = null;
-            this.onSourceReady(function () {
-                this.renderOptions();
-                deferred.resolve();
-            }, function () {
-                this.error = this.options.sourceError;
-                deferred.resolve();
-            });
-
-            return deferred.promise();
-        },
-
-        html2value: function (html) {
-            return null; //it's not good idea to set value by text for SELECT. Better set NULL
-        },
-
-        value2html: function (value, element) {
-            var deferred = $.Deferred();
-            this.onSourceReady(function () {
-                var i, text = '';
-                if($.isArray(this.sourceData)) {
-                    for(i=0; i<this.sourceData.length; i++){
-                        /*jshint eqeqeq: false*/
-                        if(this.sourceData[i].value == value) {
-                        /*jshint eqeqeq: true*/                            
-                            text = this.sourceData[i].text;
-                            break; 
-                        }
-                    } 
-                }
-                Select.superclass.value2html(text, element);
-                deferred.resolve();
-            }, function () {
-                Select.superclass.value2html(this.options.sourceError, element);
-                deferred.resolve();
-            });
-
-            return deferred.promise();
-        },  
-
-        // ------------- additional functions ------------
-
-        onSourceReady: function (success, error) {
-            //if allready loaded just call success
-            if($.isArray(this.sourceData)) {
-                success.call(this);
-                return; 
-            }
-
-            // try parse json in single quotes (for double quotes jquery does automatically)
-            try {
-                this.options.source = $.fn.editableform.utils.tryParseJson(this.options.source, false);
-            } catch (e) {
-                error.call(this);
-                return;
-            }
-
-            //loading from url
-            if (typeof this.options.source === 'string') {
-                var cacheID = this.options.source + (this.options.name ? '-' + this.options.name : ''),
-                cache;
-
-                if (!$(document).data(cacheID)) {
-                    $(document).data(cacheID, {});
-                }
-                cache = $(document).data(cacheID);
-
-                //check for cached data
-                if (cache.loading === false && cache.sourceData) { //take source from cache
-                    this.sourceData = cache.sourceData;
-                    success.call(this);
-                    return;
-                } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
-                    cache.callbacks.push($.proxy(function () {
-                        this.sourceData = cache.sourceData;
-                        success.call(this);
-                    }, this));
-
-                    //also collecting error callbacks
-                    cache.err_callbacks.push($.proxy(error, this));
-                    return;
-                } else { //no cache yet, activate it
-                    cache.loading = true;
-                    cache.callbacks = [];
-                    cache.err_callbacks = [];
-                }
-
-                //loading sourceData from server
-                $.ajax({
-                    url: this.options.source,
-                    type: 'get',
-                    cache: false,
-                    data: {name: this.options.name},
-                    dataType: 'json',
-                    success: $.proxy(function (data) {
-                        cache.loading = false;
-                        // this.options.source = data;
-                        this.sourceData = this.makeArray(data);
-                        if($.isArray(this.sourceData)) {
-                            this.doPrepend();
-                            //store result in cache
-                            cache.sourceData = this.sourceData;
-                            success.call(this);
-                            $.each(cache.callbacks, function () { this.call(); }); //run success callbacks for other fields
-                        } else {
-                            error.call(this);
-                            $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
-                        }
-                    }, this),
-                    error: $.proxy(function () {
-                        cache.loading = false;
-                        error.call(this);
-                        $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
-                    }, this)
-                });
-            } else { //options as json/array
-                this.sourceData = this.makeArray(this.options.source);
-                if($.isArray(this.sourceData)) {
-                    this.doPrepend();
-                    success.call(this);   
-                } else {
-                    error.call(this);
-                }
-            }
-        },
-
-        doPrepend: function () {
-            if(this.options.prepend === null || this.options.prepend === undefined) {
-                return;  
-            }
-            
-            if(!$.isArray(this.prependData)) {
-                //try parse json in single quotes
-                this.options.prepend = $.fn.editableform.utils.tryParseJson(this.options.prepend, true);
-                if (typeof this.options.prepend === 'string') {
-                    this.options.prepend = {'': this.options.prepend};
-                }              
-                this.prependData = this.makeArray(this.options.prepend);
-            }
-
-            if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
-                this.sourceData = this.prependData.concat(this.sourceData);
-            }
-        },
-
-        renderOptions: function() {
+        renderList: function() {
             if(!$.isArray(this.sourceData)) {
                 return;
             }
@@ -187,83 +38,24 @@ $(function(){
                 this.$input.append($('<option>', {value: this.sourceData[i].value}).text(this.sourceData[i].text)); 
             }
         },
-
-        /**
-        * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
-        */
-        makeArray: function(data) {
-            var count, obj, result = [], iterateEl;
-            if(!data || typeof data === 'string') {
-                return null; 
+       
+        value2htmlFinal: function(value, element) {
+            var text = '', item = this.itemByVal(value);
+            if(item) {
+                text = item.text;
             }
-
-            if($.isArray(data)) { //array
-                iterateEl = function (k, v) {
-                    obj = {value: k, text: v};
-                    if(count++ >= 2) {
-                        return false;// exit each if object has more than one value
-                    }
-                };
-            
-                for(var i = 0; i < data.length; i++) {
-                    if(typeof data[i] === 'object') {
-                        count = 0;
-                        $.each(data[i], iterateEl);
-                        if(count === 1) {
-                            result.push(obj); 
-                        } else if(count > 1 && data[i].hasOwnProperty('value') && data[i].hasOwnProperty('text')) {
-                            result.push(data[i]);
-                        } else {
-                            //data contains incorrect objects
-                        }
-                    } else {
-                        result.push({value: i, text: data[i]}); 
-                    }
-                }
-            } else {  //object
-                $.each(data, function (k, v) {
-                    result.push({value: k, text: v});
-                });  
-            }
-            return result;
-        }
-
+            Select.superclass.constructor.superclass.value2html(text, element);   
+        }        
     });      
 
-    Select.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
+    Select.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {
         /**
         @property tpl 
         @default <select></select>
         **/         
-        tpl:'<select></select>',
-        /**
-        Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array.
-        Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br>
-        For compability it also supports format <code>{value1: text1, value2: text2 ...}</code> but it does not guarantee elements order.      
-
-        @property source 
-        @type string|array|object
-        @default null
-        **/         
-        source:null, 
-        /**
-        Data automatically prepended to the begining of dropdown list.
-        
-        @property prepend 
-        @type string|array|object
-        @default false
-        **/         
-        prepend:false,
-        /**
-        Error message shown when list cannot be loaded (e.g. ajax error)
-        
-        @property sourceError 
-        @type string
-        @default Error when loading options
-        **/          
-        sourceError: 'Error when loading options'
+        tpl:'<select></select>'
     });
 
     $.fn.editableform.types.select = Select;      
 
-}(window.jQuery));
+}(window.jQuery));
\ No newline at end of file
diff --git a/src/inputs/text.js b/src/inputs/text.js
index 702c3f6..8d2f259 100644
--- a/src/inputs/text.js
+++ b/src/inputs/text.js
@@ -3,6 +3,7 @@ Text input
 
 @class text
 @extends abstract
+@final
 @example
 <a href="#" id="username" data-type="text" data-pk="1">awesome</a>
 <script>
diff --git a/src/inputs/textarea.js b/src/inputs/textarea.js
index c4d867e..eb4cd3c 100644
--- a/src/inputs/textarea.js
+++ b/src/inputs/textarea.js
@@ -3,6 +3,7 @@ Textarea input
 
 @class textarea
 @extends abstract
+@final
 @example
 <a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a>
 <script>
diff --git a/test/loader.js b/test/loader.js
index 136ba5b..ba91063 100644
--- a/test/loader.js
+++ b/test/loader.js
@@ -31,9 +31,11 @@ function getAssets(f, c, src, libs) {
     containers+'editable-container.js',
     element+'editable-element.js',
     inputs+'abstract.js',
+    inputs+'list.js',
     inputs+'text.js',
     inputs+'textarea.js',
-    inputs+'select.js'  
+    inputs+'select.js',  
+    inputs+'checklist.js'  
     ],
 
     css = [

From 0f795a3882de8a3c3823b75ed6acba6d57b9dfad Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 12:13:49 +0400
Subject: [PATCH 02/24] add checklist to build

---
 grunt.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/grunt.js b/grunt.js
index 84c7cb7..3b66d5b 100644
--- a/grunt.js
+++ b/grunt.js
@@ -38,9 +38,11 @@ function getFiles() {
     containers+'editable-container.js', 
     lib+'element/editable-element.js',
     inputs+'abstract.js',
+    inputs+'list.js',
     inputs+'text.js',
     inputs+'textarea.js',
-    inputs+'select.js'    
+    inputs+'select.js',    
+    inputs+'checklist.js'
     ]; 
 
     //common css files

From 0487380535ab9d7db875253638946875e56dbb5c Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 13:22:27 +0400
Subject: [PATCH 03/24] checklist test

---
 src/editable-form/editable-form-utils.js |  2 +-
 src/inputs/checklist.js                  | 12 +--
 src/inputs/list.js                       |  2 +-
 test/index.html                          |  1 +
 test/mocks.js                            | 34 ++++++++-
 test/unit/checklist.js                   | 96 ++++++++++++++++++++++++
 test/unit/select.js                      | 30 --------
 7 files changed, 138 insertions(+), 39 deletions(-)
 create mode 100644 test/unit/checklist.js

diff --git a/src/editable-form/editable-form-utils.js b/src/editable-form/editable-form-utils.js
index 8b09f46..3e2eaa6 100644
--- a/src/editable-form/editable-form-utils.js
+++ b/src/editable-form/editable-form-utils.js
@@ -38,7 +38,7 @@
             * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery
             */
             tryParseJson: function(s, safe) {
-                if (typeof s === 'string' && s.length && s.match(/^\{.*\}$/)) {
+                if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) {
                     if (safe) {
                         try {
                             /*jslint evil: true*/
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 158d509..33fa2b7 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -55,7 +55,7 @@ $(function(){
         str2value: function(str) {
            var reg, value = null;
            if(typeof str === 'string' && str.length) {
-               reg = new RegExp('\s*'+$.trim(this.options.separator)+'\s*');
+               reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*');
                value = str.split(reg);
            } else if($.isArray(str)) {
                value = str; 
@@ -86,9 +86,9 @@ $(function(){
           
        //collect text of checked boxes
         value2htmlFinal: function(value, element) {
-           var selected = [], html = '';
-           if($.isArray(value) && value.length <= this.options.limit) {
-               for(var i=0; i<value.length; i++){
+           var selected = [], item, i, html = '';
+           if($.isArray(value) && value.length <= this.options.limit) {    
+               for(i=0; i<value.length; i++){
                    item = this.itemByVal(value[i]);
                    if(item) {
                        selected.push($('<div>').text(item.text).html());
@@ -140,9 +140,9 @@ $(function(){
 
         @property limitText 
         @type string
-        @default 'Checked {checked} options of {count}'
+        @default 'Selected {checked} of {count}'
         **/         
-        limitText: 'Checked {checked} options of {count}'        
+        limitText: 'Selected {checked} of {count}'        
     });
 
     $.fn.editableform.types.checklist = Checklist;      
diff --git a/src/inputs/list.js b/src/inputs/list.js
index 8dba070..43cc74e 100644
--- a/src/inputs/list.js
+++ b/src/inputs/list.js
@@ -209,7 +209,7 @@ List - abstract class for inputs that have source option loaded from js array or
         //search for item by particular value
         itemByVal: function(val) {
             if($.isArray(this.sourceData)) {
-                for(i=0; i<this.sourceData.length; i++){
+                for(var i=0; i<this.sourceData.length; i++){
                     /*jshint eqeqeq: false*/
                     if(this.sourceData[i].value == val) {
                     /*jshint eqeqeq: true*/                            
diff --git a/test/index.html b/test/index.html
index 7181e62..fe1e4df 100644
--- a/test/index.html
+++ b/test/index.html
@@ -43,6 +43,7 @@
   <script src="unit/select.js"></script>  
   <script src="unit/textarea.js"></script>  
   <script src="unit/api.js"></script>
+  <script src="unit/checklist.js"></script>
   <script>  
     if(fc.f === 'bootstrap') {
         loadJs('unit/date.js'); 
diff --git a/test/mocks.js b/test/mocks.js
index c004cfc..46d7a4f 100644
--- a/test/mocks.js
+++ b/test/mocks.js
@@ -1,7 +1,7 @@
 var timeout = 200;
 $(function () {
     
- $.mockjaxSettings.responseTime = 50;
+    $.mockjaxSettings.responseTime = 50;
     
     $.mockjax({
         url: 'post.php',
@@ -23,6 +23,38 @@ $(function () {
             this.responseText = settings;  
         }
     }); 
+    
+    
+    window.groups =  {
+            0: 'Guest',
+            1: 'Service',
+            2: 'Customer',
+            3: 'Operator',
+            4: 'Support',
+            5: 'Admin',
+            6: '',
+            '': 'Nothing'
+      };
+      
+    //groups as array  
+    window.groupsArr = [];
+    for(var i in groups) {
+        groupsArr.push({value: i, text: groups[i]}); 
+    }
+      
+    window.size = groupsArr.length;
+    
+    $.mockjax({
+        url: 'groups.php',
+        responseText: groups
+    });
+
+    $.mockjax({
+        url: 'groups-error.php',
+        status: 500,
+        responseText: 'Internal Server Error'
+    });       
+    
 });
 
 // usefull functions
diff --git a/test/unit/checklist.js b/test/unit/checklist.js
new file mode 100644
index 0000000..1f4258f
--- /dev/null
+++ b/test/unit/checklist.js
@@ -0,0 +1,96 @@
+$(function () {
+   
+    module("checklist", {
+        setup: function(){
+            sfx = $('#qunit-fixture'),
+            fx = $('#async-fixture');               
+            $.fn.editable.defaults.name = 'name2';
+            //clear cache
+            $(document).removeData('groups.php-'+$.fn.editable.defaults.name);
+            $.support.transition = false;
+        }
+    });  
+   
+    asyncTest("should load options, set correct value and save new value", function () {
+         var sep = '-',
+             newValue,
+             e = $('<a href="#" data-type="checklist" data-url="post.php"></a>').appendTo(fx).editable({
+             pk: 1,
+             source: groupsArr,
+             value: [2, 3],
+             viewseparator: sep
+        });
+           
+        equal(e.text(), groups[2]+sep+groups[3], 'autotext ok');
+        
+        e.click();
+        var p = tip(e);
+        equal(p.find('input[type="checkbox"]').length, groupsArr.length, 'checkboxes rendered');
+        equal(p.find('input[type="checkbox"]:checked').length, 2, 'checked count ok');
+        equal(p.find('input[type="checkbox"]:checked').eq(0).val(), 2, '1st checked');
+        equal(p.find('input[type="checkbox"]:checked').eq(1).val(), 3, '2nd checked');
+
+        //set new value
+        p.find('input[type="checkbox"]:checked').eq(0).click(); 
+        p.find('input[type="checkbox"]').first().click();
+        newValue = p.find('input[type="checkbox"]').first().val();
+        
+        //submit
+        p.find('form').submit(); 
+         
+         setTimeout(function() {
+               ok(!p.is(':visible'), 'popup closed');
+               
+               equal(e.data('editable').value.join(''), [newValue, 3].join(''), 'new value ok')
+               equal(e.text(), groups[newValue]+sep+groups[3], 'new text ok');
+              
+               // open container again to see what checked
+               e.click()
+               p = tip(e);
+               
+               equal(p.find('input[type="checkbox"]').length, groupsArr.length, 'checkboxes rendered');
+               equal(p.find('input[type="checkbox"]:checked').length, 2, 'checked count ok');
+               equal(p.find('input[type="checkbox"]:checked').eq(0).val(), newValue, '1st checked');
+               equal(p.find('input[type="checkbox"]:checked').eq(1).val(), 3, '2nd checked');               
+               
+               e.remove();    
+               start();  
+         }, timeout);                              
+    });                  
+   
+     asyncTest("test limit", function () {
+         var e = $('<a href="#" data-type="checklist" data-value="2,3" data-url="post.php"></a>').appendTo(fx).editable({
+             pk: 1,
+             source: groupsArr,
+             limit: 1,
+             limitText: '{checked} of {count}'
+        });
+           
+        equal(e.text(), '2 of '+groupsArr.length, 'autotext ok');
+        
+        e.click();
+        var p = tip(e);
+
+        equal(p.find('input[type="checkbox"]:checked').length, 2, 'checked count ok');
+        equal(p.find('input[type="checkbox"]:checked').eq(0).val(), 2, '1st checked');
+        equal(p.find('input[type="checkbox"]:checked').eq(1).val(), 3, '2nd checked');
+
+        //set new value
+        p.find('input[type="checkbox"]').first().click();
+        newValue = p.find('input[type="checkbox"]').first().val();
+        
+        //submit
+        p.find('form').submit(); 
+         
+         setTimeout(function() {
+               ok(!p.is(':visible'), 'popup closed');
+               
+               equal(e.text(), '3 of '+groupsArr.length, 'autotext ok');
+               
+               e.remove();    
+               start();  
+         }, timeout);                              
+    });   
+   
+     
+});
\ No newline at end of file
diff --git a/test/unit/select.js b/test/unit/select.js
index a4a1256..dc67931 100644
--- a/test/unit/select.js
+++ b/test/unit/select.js
@@ -1,34 +1,4 @@
 $(function () {
-    
-    window.groups =  {
-            0: 'Guest',
-            1: 'Service',
-            2: 'Customer',
-            3: 'Operator',
-            4: 'Support',
-            5: 'Admin',
-            6: '',
-            '': 'Nothing'
-      };
-      
-    //groups as array  
-    window.groupsArr = [];
-    for(var i in groups) {
-        groupsArr.push({value: i, text: groups[i]}); 
-    }
-      
-    window.size = groupsArr.length;
-    
-    $.mockjax({
-        url: 'groups.php',
-        responseText: groups
-    });
-
-    $.mockjax({
-        url: 'groups-error.php',
-        status: 500,
-        responseText: 'Internal Server Error'
-    });   
    
     module("select", {
         setup: function(){

From 69628f131db0e32ce27dc3baa2766600bbf6aa55 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 13:32:39 +0400
Subject: [PATCH 04/24] comments fixes for docs

---
 src/editable-form/editable-form.js | 2 +-
 src/inputs/checklist.js            | 6 +++---
 src/inputs/dateui/dateui.js        | 4 ++--
 src/inputs/list.js                 | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index d93042a..57cc38e 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -301,7 +301,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         /* see also defaults for input */
         
         /**
-        Type of input. Can be <code>text|textarea|select|date</code>
+        Type of input. Can be <code>text|textarea|select|date|checklist</code>
 
         @property type 
         @type string
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 33fa2b7..2dd4f75 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -1,11 +1,11 @@
 /**
-Checklist input. Internally value stored as array.
+List of checkboxes. Internally value stored as javascript array.
 
 @class checklist
 @extends list
 @final
 @example
-<a href="#" id="status" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a>
+<a href="#" id="options" data-type="checklist" data-pk="1" data-url="/post" data-original-title="Select options"></a>
 <script>
 $(function(){
     $('#options').editable({
@@ -122,7 +122,7 @@ $(function(){
 
         @property viewseparator 
         @type string
-        @default ', '
+        @default '<br>'
         **/         
         viewseparator: '<br>',
         /**
diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js
index 38cc2d3..266d15c 100644
--- a/src/inputs/dateui/dateui.js
+++ b/src/inputs/dateui/dateui.js
@@ -1,7 +1,7 @@
 /**
 jQuery UI Datepicker.  
-Description and examples: http://jqueryui.com/datepicker.  
-Do not use it together with bootstrap-datepicker.
+Description and examples: http://jqueryui.com/datepicker.   
+This input is also accessible as **date** type. Do not use it together with __bootstrap-datepicker__ as both apply <code>$().datepicker()</code> method.
 
 @class dateui
 @extends abstract
diff --git a/src/inputs/list.js b/src/inputs/list.js
index 43cc74e..1eeeeb2 100644
--- a/src/inputs/list.js
+++ b/src/inputs/list.js
@@ -225,7 +225,7 @@ List - abstract class for inputs that have source option loaded from js array or
         /**
         Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array.
         Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br>
-        For compability it also supports format <code>{value1: text1, value2: text2 ...}</code> but it does not guarantee elements order.      
+        For compability it also supports format <code>{value1: "text1", value2: "text2" ...}</code> but it does not guarantee elements order.      
 
         @property source 
         @type string|array|object

From 40eea34004c1593e1e5cc8c967e74bcb1079f4fd Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 15:11:18 +0400
Subject: [PATCH 05/24] add class editable-input

---
 src/editable-form/editable-form.css | 6 ++----
 src/inputs/abstract.js              | 1 +
 src/inputs/checklist.js             | 4 ++--
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 0b2b07e..0ef39e5 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -23,12 +23,11 @@
    color: red;  
 }
 
-.editableform input, 
-.editableform select, 
-.editableform textarea {
+.editableform .editable-input {
     vertical-align: top; 
     display: inline-block;
     width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
+    float: left;
 }
 
 .editableform textarea {
@@ -36,7 +35,6 @@
 }
 
 .editableform .editable-date {
-    float: left;
     padding: 0; 
     margin: 0 0 9px 0;
 }
\ No newline at end of file
diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js
index b0320cd..bb2b243 100644
--- a/src/inputs/abstract.js
+++ b/src/inputs/abstract.js
@@ -28,6 +28,7 @@ To create your own input you should inherit from this class.
        **/       
        render: function() {
             this.$input = $(this.options.tpl);
+            this.$input.addClass('editable-input');
             if(this.options.inputclass) {
                 this.$input.addClass(this.options.inputclass); 
             }
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 2dd4f75..491b095 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -1,5 +1,5 @@
 /**
-List of checkboxes. Internally value stored as javascript array.
+List of checkboxes. Internally value stored as javascript array of values.
 
 @class checklist
 @extends list
@@ -147,4 +147,4 @@ $(function(){
 
     $.fn.editableform.types.checklist = Checklist;      
 
-}(window.jQuery));
\ No newline at end of file
+}(window.jQuery));

From 18cbb07090f06eae90dc530d8ff0b09d8a558606 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 15:14:40 +0400
Subject: [PATCH 06/24] changelog checklist

---
 CHANGELOG.txt           | 1 +
 src/inputs/checklist.js | 6 +++---
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index da494b4..d409431 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,6 +3,7 @@ X-editable changelog
        
 Version 1.0.2 wip
 ----------------------------   
+[enh] new input type: checklist (vitalets)      
 [enh] updated docs: inputs dropdown menu, global templates section (vitalets)      
                             
 
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 491b095..4bd634a 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -11,9 +11,9 @@ $(function(){
     $('#options').editable({
         value: [2, 3],    
         source: [
-              {value: 1, text: 'Active'},
-              {value: 2, text: 'Blocked'},
-              {value: 3, text: 'Deleted'}
+              {value: 1, text: 'option1'},
+              {value: 2, text: 'option2'},
+              {value: 3, text: 'option3'}
            ]
         }
     });

From 2842c6dce355d3c4821258623d66b4401233ed0c Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 16:02:57 +0400
Subject: [PATCH 07/24] when source is simple array - key = text

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

diff --git a/src/inputs/list.js b/src/inputs/list.js
index 1eeeeb2..33be516 100644
--- a/src/inputs/list.js
+++ b/src/inputs/list.js
@@ -195,7 +195,7 @@ List - abstract class for inputs that have source option loaded from js array or
                             //data contains incorrect objects
                         }
                     } else {
-                        result.push({value: i, text: data[i]}); 
+                        result.push({value: data[i], text: data[i]}); 
                     }
                 }
             } else {  //object

From c6da2ed11b8eb5a905703a705f2e2a6fbd863c7f Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Sun, 25 Nov 2012 16:05:43 +0400
Subject: [PATCH 08/24] fix test for simple array

---
 test/unit/select.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/unit/select.js b/test/unit/select.js
index dc67931..fec4a8e 100644
--- a/test/unit/select.js
+++ b/test/unit/select.js
@@ -81,7 +81,7 @@ $(function () {
     
     test("load options from simple array", function () {
          var arr = ['q', 'w', 'x'],
-             e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({
+             e = $('<a href="#" data-type="select" data-value="x" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({
              pk: 1,
              autotext: true,
              source: arr
@@ -92,7 +92,7 @@ $(function () {
         ok(p.is(':visible'), 'popover visible')
         ok(p.find('select').length, 'select exists')
         equal(p.find('select').find('option').length, arr.length, 'options loaded')
-        equal(p.find('select').val(), 2, 'selected value correct') 
+        equal(p.find('select').val(), 'x', 'selected value correct') 
         p.find('button[type=button]').click(); 
         ok(!p.is(':visible'), 'popover was removed');  
     }) 

From c9f2f31adddc02120b751c97479a516682830b56 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Mon, 26 Nov 2012 12:43:17 +0400
Subject: [PATCH 09/24] new form template

---
 src/editable-form/editable-form-bootstrap.js |  7 +++-
 src/editable-form/editable-form-jqueryui.js  | 13 +++----
 src/editable-form/editable-form.css          | 19 +++++++---
 src/editable-form/editable-form.js           | 40 +++++++++++---------
 4 files changed, 48 insertions(+), 31 deletions(-)

diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js
index ff07282..350a0ac 100644
--- a/src/editable-form/editable-form-bootstrap.js
+++ b/src/editable-form/editable-form-bootstrap.js
@@ -5,10 +5,13 @@ Editableform based on Twitter Bootstrap
     
     //form template
     $.fn.editableform.template = '<form class="form-inline editableform"><div class="control-group">' + 
-    '&nbsp;<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button>&nbsp;<button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>' + 
-    '<div style="clear:both"><span class="help-block editable-error-block"></span></div>' + 
+    '<div class="editable-input"></div><div class="editable-buttons"></div>' + 
+    '<div class="help-block editable-error-block"></div>' + 
     '</div></form>'; 
     
+    //buttons
+    $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button><button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>';         
+    
     //error classes
     $.fn.editableform.errorGroupClass = 'error';
     $.fn.editableform.errorBlockClass = null;    
diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js
index e11e331..7ae3670 100644
--- a/src/editable-form/editable-form-jqueryui.js
+++ b/src/editable-form/editable-form-jqueryui.js
@@ -7,8 +7,9 @@ Editableform based on jQuery UI
          initTemplate: function() {
               this.$form = $($.fn.editableform.template);
               
-             //init buttons
-             this.$form.find('button[type=submit]').button({
+              //buttons
+              this.$form.find('div.editable-buttons').append($.fn.editableform.buttons);                
+              this.$form.find('button[type=submit]').button({
                  icons: { primary: "ui-icon-check" },
                  text: false
              });
@@ -16,14 +17,12 @@ Editableform based on jQuery UI
                  icons: { primary: "ui-icon-cancel" },
                  text: false
              });
+ 
          }
     });
     
-    //form template
-    $.fn.editableform.template = '<form class="editableform"><div class="control-group">' + 
-    '&nbsp;<button type="submit" style="height: 24px">submit</button>&nbsp;<button type="button" style="height: 24px">cancel</button></div>' + 
-    '<div class="editable-error-block"></div>' + 
-    '</form>'; 
+    //buttons
+    $.fn.editableform.buttons = '<button type="submit" style="height: 24px">submit</button><button type="button" style="height: 24px">cancel</button>';
     
     //error classes
     $.fn.editableform.errorGroupClass = null;
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 0ef39e5..008cf05 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -1,6 +1,15 @@
 .editableform,
 .editableform div.control-group {
-    margin-bottom: 0; 
+    margin-bottom: 0;
+    overflow: auto; 
+}
+
+.editable-buttons {
+   float: left;  
+}
+
+.editable-buttons button {
+   margin-left: 7px; 
 }
 
 .editableform-loading {
@@ -14,8 +23,7 @@
 
  .editable-error-block {
     max-width: 300px;
-    margin-top: 3px;
-    margin-bottom: 0;
+    margin: 0;
     clear: both; 
 }
 
@@ -23,11 +31,12 @@
    color: red;  
 }
 
-.editableform .editable-input {
+.editable-input {
     vertical-align: top; 
     display: inline-block;
     width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
     float: left;
+    padding-bottom: 9px;
 }
 
 .editableform textarea {
@@ -36,5 +45,5 @@
 
 .editableform .editable-date {
     padding: 0; 
-    margin: 0 0 9px 0;
+    margin: 0;
 }
\ No newline at end of file
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 57cc38e..738d995 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -34,6 +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);              
         },
         /**
         Renders editableform
@@ -57,8 +60,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             //render input
             $.when(this.input.render())
             .then($.proxy(function () {
-                //place input
-                this.$form.find('div.control-group').prepend(this.input.$input);
+                //input
+                this.$form.find('div.editable-input').append(this.input.$input);
+                
+                //clear link
+                if(this.input.$clear) {
+                    this.$form.find('div.editable-input').append(this.input.$clear);  
+                }                
+                
                 //attach 'cancel' handler
                 this.$form.find('button[type=button]').click($.proxy(this.cancel, this));
                 //append form to container
@@ -94,19 +103,11 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             this.$element.triggerHandler('cancel');
         },
         showLoading: function() {
-            var fw, fh, iw, ih;
+            var fw, fh, iw, bh;
             //set loading size equal to form
             if(this.$form) {
-                fh = this.$form.outerHeight() || 0;
-                fw = this.$form.outerWidth() || 0;
-                ih = (this.input && this.input.$input.outerHeight()) || 0;
-                iw = (this.input && this.input.$input.outerWidth()) || 0;
-                if(fh || ih) {
-                    this.$loading.height(fh > ih ? fh : ih);
-                }
-                if(fw || iw) {
-                    this.$loading.width(fw > iw ? fw : iw);
-                }
+                this.$loading.width(this.$form.outerWidth());
+                this.$loading.height(this.$form.outerHeight());
                 this.$form.hide();
             }
             this.$loading.show(); 
@@ -406,14 +407,19 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
       Note: following params could redefined in engine: bootstrap or jqueryui:
       Classes 'control-group' and 'editable-error-block' must always present!
     */      
-      $.fn.editableform.template = '<form class="form-inline editableform"><div class="control-group">' + 
-    '&nbsp;<button type="submit">Ok</button>&nbsp;<button type="button">Cancel</button></div>' + 
-    '<div class="editable-error-block"></div>' + 
-    '</form>';
+      $.fn.editableform.template = '<form class="form-inline editableform">'+
+       '<div class="control-group">' + 
+           '<div class="editable-input"></div><div class="editable-buttons"></div>'+
+       '</div>' + 
+       '<div class="editable-error-block"></div>' + 
+       '</form>';
       
       //loading div
       $.fn.editableform.loading = '<div class="editableform-loading"></div>';
       
+      //buttons
+      $.fn.editableform.buttons = '<button type="submit">Ok</button><button type="button">Cancel</button>';      
+      
       //error class attahced to control-group
       $.fn.editableform.errorGroupClass = null;  
       

From 999bbe25df67eae35d4a28b51c2346ec5e2a518b Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Mon, 26 Nov 2012 13:01:04 +0400
Subject: [PATCH 10/24] new form template fixes

---
 src/editable-form/editable-form-bootstrap.js | 17 ++++++----
 src/editable-form/editable-form-jqueryui.js  | 33 +++++++++-----------
 src/editable-form/editable-form.css          |  5 +++
 src/editable-form/editable-form.js           |  5 +--
 4 files changed, 34 insertions(+), 26 deletions(-)

diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js
index 350a0ac..f6a5273 100644
--- a/src/editable-form/editable-form-bootstrap.js
+++ b/src/editable-form/editable-form-bootstrap.js
@@ -3,14 +3,19 @@ Editableform based on Twitter Bootstrap
 */
 (function ($) {
     
-    //form template
-    $.fn.editableform.template = '<form class="form-inline editableform"><div class="control-group">' + 
-    '<div class="editable-input"></div><div class="editable-buttons"></div>' + 
-    '<div class="help-block editable-error-block"></div>' + 
-    '</div></form>'; 
+      $.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);                
+         }
+    });    
     
     //buttons
-    $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button><button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>';         
+    $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button>'+
+                                '<button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>';         
     
     //error classes
     $.fn.editableform.errorGroupClass = 'error';
diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js
index 7ae3670..c0b6683 100644
--- a/src/editable-form/editable-form-jqueryui.js
+++ b/src/editable-form/editable-form-jqueryui.js
@@ -4,26 +4,23 @@ Editableform based on jQuery UI
 (function ($) {
     
     $.extend($.fn.editableform.Constructor.prototype, {
-         initTemplate: function() {
-              this.$form = $($.fn.editableform.template);
-              
-              //buttons
-              this.$form.find('div.editable-buttons').append($.fn.editableform.buttons);                
-              this.$form.find('button[type=submit]').button({
-                 icons: { primary: "ui-icon-check" },
-                 text: false
-             });
-             this.$form.find('button[type=button]').button({
-                 icons: { primary: "ui-icon-cancel" },
-                 text: false
-             });
- 
-         }
+        initTemplate: function() {
+            this.$form = $($.fn.editableform.template);
+
+            //buttons
+            this.$form.find('div.editable-buttons').append($.fn.editableform.buttons);                
+            this.$form.find('button[type=submit]').button({
+                icons: { primary: "ui-icon-check" },
+                text: false
+            }).removeAttr('title');
+            this.$form.find('button[type=button]').button({
+                icons: { primary: "ui-icon-cancel" },
+                text: false
+            }).removeAttr('title');
+
+        }
     });
     
-    //buttons
-    $.fn.editableform.buttons = '<button type="submit" style="height: 24px">submit</button><button type="button" style="height: 24px">cancel</button>';
-    
     //error classes
     $.fn.editableform.errorGroupClass = null;
     $.fn.editableform.errorBlockClass = 'ui-state-error';
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 008cf05..35f0eb0 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -12,6 +12,11 @@
    margin-left: 7px; 
 }
 
+/*for jquery-ui buttons need set height to look more pretty*/
+.editable-buttons button.ui-button {
+   height: 24px; 
+}
+
 .editableform-loading {
     background: url('img/loading.gif') center center no-repeat;  
     height: 25px;  
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 738d995..4dd82f2 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -410,15 +410,16 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
       $.fn.editableform.template = '<form class="form-inline editableform">'+
        '<div class="control-group">' + 
            '<div class="editable-input"></div><div class="editable-buttons"></div>'+
+           '<div class="editable-error-block"></div>' + 
        '</div>' + 
-       '<div class="editable-error-block"></div>' + 
        '</form>';
       
       //loading div
       $.fn.editableform.loading = '<div class="editableform-loading"></div>';
       
       //buttons
-      $.fn.editableform.buttons = '<button type="submit">Ok</button><button type="button">Cancel</button>';      
+      $.fn.editableform.buttons = '<button type="submit">Ok</button>'+
+                                  '<button type="button">Cancel</button>';      
       
       //error class attahced to control-group
       $.fn.editableform.errorGroupClass = null;  

From 2438ea8da10b6188ec54099359af82b31e84c8ca Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Mon, 26 Nov 2012 13:36:29 +0400
Subject: [PATCH 11/24] clear link in date input

---
 CHANGELOG.txt                       |  4 ++-
 src/editable-form/editable-form.css |  5 ++++
 src/editable-form/editable-form.js  |  4 +--
 src/inputs/abstract.js              | 13 +++++++--
 src/inputs/date/date.js             | 26 +++++++++++++++--
 src/inputs/text.js                  |  2 +-
 test/unit/date.js                   | 44 ++++++++++++++++++++++++++++-
 7 files changed, 89 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index d409431..c15f408 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,8 +1,10 @@
 X-editable changelog
 =============================
        
-Version 1.0.2 wip
+Version 1.1.0 wip
 ----------------------------   
+[enh] 'clear' button added in date (vitalets)      
+[enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets)      
 [enh] new input type: checklist (vitalets)      
 [enh] updated docs: inputs dropdown menu, global templates section (vitalets)      
                             
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 35f0eb0..7a5b7b0 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -51,4 +51,9 @@
 .editableform .editable-date {
     padding: 0; 
     margin: 0;
+}
+
+.editable-clear {
+   float: right; 
+   font-size: 0.9em;
 }
\ No newline at end of file
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 4dd82f2..854e40d 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -418,8 +418,8 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
       $.fn.editableform.loading = '<div class="editableform-loading"></div>';
       
       //buttons
-      $.fn.editableform.buttons = '<button type="submit">Ok</button>'+
-                                  '<button type="button">Cancel</button>';      
+      $.fn.editableform.buttons = '<button type="submit">ok</button>'+
+                                  '<button type="button">cancel</button>';      
       
       //error class attahced to control-group
       $.fn.editableform.errorGroupClass = null;  
diff --git a/src/inputs/abstract.js b/src/inputs/abstract.js
index bb2b243..a40d742 100644
--- a/src/inputs/abstract.js
+++ b/src/inputs/abstract.js
@@ -18,6 +18,7 @@ To create your own input you should inherit from this class.
            this.type = type;
            this.options = $.extend({}, defaults, options); 
            this.$input = null;
+           this.$clear = null;
            this.error = null;
        },
        
@@ -28,14 +29,13 @@ To create your own input you should inherit from this class.
        **/       
        render: function() {
             this.$input = $(this.options.tpl);
-            this.$input.addClass('editable-input');
             if(this.options.inputclass) {
                 this.$input.addClass(this.options.inputclass); 
             }
             
             if (this.options.placeholder) {
                 this.$input.attr('placeholder', this.options.placeholder);
-            }            
+            }   
        }, 
 
        /**
@@ -111,6 +111,15 @@ To create your own input you should inherit from this class.
            if(this.$input.is(':visible')) {
                this.$input.focus();
            }
+       },
+       
+       /**
+        Creares input. 
+        
+        @method clear() 
+       **/        
+       clear:  function() {
+           this.$input.val(null);
        } 
     };
         
diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js
index 5081db2..b16f57e 100644
--- a/src/inputs/date/date.js
+++ b/src/inputs/date/date.js
@@ -54,6 +54,14 @@ $(function(){
         render: function () {
             Date.superclass.render.call(this);
             this.$input.datepicker(this.options.datepicker);
+                        
+            if(this.options.clear) {
+                this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){
+                    e.preventDefault();
+                    e.stopPropagation();
+                    this.clear();
+                }, this));
+            }
         },
 
         value2html: function(value, element) {
@@ -82,7 +90,12 @@ $(function(){
        },       
        
        activate: function() {
-       }        
+       },
+       
+       clear:  function() {
+          this.$input.data('datepicker').date = null;
+          this.$input.find('.active').removeClass('active');
+       }                
 
     });
     
@@ -131,7 +144,16 @@ $(function(){
             weekStart: 0,
             startView: 0,
             autoclose: false
-        }
+        },
+        /**
+        Text shown as clear date button. 
+        If <code>false</code> clear button will not be rendered.
+        
+        @property clear 
+        @type boolean|string
+        @default 'x clear'         
+        **/
+        clear: '&times; clear'
     });   
 
     $.fn.editableform.types.date = Date;
diff --git a/src/inputs/text.js b/src/inputs/text.js
index 8d2f259..8557c2a 100644
--- a/src/inputs/text.js
+++ b/src/inputs/text.js
@@ -25,8 +25,8 @@ $(function(){
     $.extend(Text.prototype, {
         activate: function() {
             if(this.$input.is(':visible')) {
-                $.fn.editableform.utils.setCursorPosition(this.$input.get(0), this.$input.val().length);
                 this.$input.focus();
+                $.fn.editableform.utils.setCursorPosition(this.$input.get(0), this.$input.val().length);
             }
         }  
     });
diff --git a/test/unit/date.js b/test/unit/date.js
index fec2cc7..e2ab4cc 100644
--- a/test/unit/date.js
+++ b/test/unit/date.js
@@ -14,7 +14,7 @@ $(function () {
        return dpg.formatDate(date, dpg.parseFormat(format), 'en');  
     }
      
-    asyncTest("popover should contain datepicker with value and save new entered date", function () {
+    asyncTest("container should contain datepicker with value and save new entered date", function () {
         expect(9);
         
         $.fn.editableform.types.date.defaults.datepicker.weekStart = 1;
@@ -130,5 +130,47 @@ $(function () {
         p.find('button[type=button]').click();
         ok(!p.is(':visible'), 'popover closed');      
       });
+      
+    asyncTest("clear button", function () {
+        var d = '15.05.1984',
+            e = $('<a href="#" data-type="date" data-pk="1" data-url="post-date-clear.php">'+d+'</a>').appendTo(fx).editable({
+                format: f,
+                clear: 'abc'
+            });
+                       
+          $.mockjax({
+              url: 'post-date-clear.php',
+              response: function(settings) {
+                  equal(settings.data.value, '', 'submitted value correct');            
+              }
+          });
+       
+        equal(frmt(e.data('editable').value, 'dd.mm.yyyy'), d, 'value correct');
+            
+        e.click();
+        var p = tip(e);
+        ok(p.find('.datepicker').is(':visible'), 'datepicker exists');
+        
+        equal(frmt(e.data('editable').value, f), d, 'day set correct');
+        equal(p.find('td.day.active').text(), 15, 'day shown correct');
+
+        var clear = p.find('.editable-clear');
+        equal(clear.text(), 'abc', 'clear link shown');
+
+        //click clear
+        clear.click();
+        ok(!p.find('td.day.active').length, 'no active day');
+
+        p.find('form').submit();
+    
+        setTimeout(function() {          
+           ok(!p.is(':visible'), 'popover closed');
+           equal(e.data('editable').value, null, 'null saved to value');
+           equal(e.text(), e.data('editable').options.emptytext, 'empty text shown');
+           e.remove();    
+           start();  
+        }, timeout); 
+        
+     });        
    
 });
\ No newline at end of file

From bd64464a5d46e6c1a3d4d44fd200e1f1c2d4a032 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Mon, 26 Nov 2012 15:32:13 +0400
Subject: [PATCH 12/24] clear button in dateui

---
 CHANGELOG.txt                       |  2 +-
 src/editable-form/editable-form.css |  1 +
 src/inputs/dateui/dateui.js         | 25 ++++++++++++++--
 test/unit/dateui.js                 | 45 +++++++++++++++++++++++++++--
 4 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index c15f408..d9b3c8b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,7 +3,7 @@ X-editable changelog
        
 Version 1.1.0 wip
 ----------------------------   
-[enh] 'clear' button added in date (vitalets)      
+[enh] 'clear' button added in date and dateui (vitalets)      
 [enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets)      
 [enh] new input type: checklist (vitalets)      
 [enh] updated docs: inputs dropdown menu, global templates section (vitalets)      
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 7a5b7b0..6c18918 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -56,4 +56,5 @@
 .editable-clear {
    float: right; 
    font-size: 0.9em;
+   text-decoration: none;
 }
\ No newline at end of file
diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js
index 266d15c..986726a 100644
--- a/src/inputs/dateui/dateui.js
+++ b/src/inputs/dateui/dateui.js
@@ -52,6 +52,14 @@ $(function(){
         render: function () {
             DateUI.superclass.render.call(this);
             this.$input.datepicker(this.options.datepicker);
+            
+            if(this.options.clear) {
+                this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){
+                    e.preventDefault();
+                    e.stopPropagation();
+                    this.clear();
+                }, this));
+            }            
         },
 
         value2html: function(value, element) {
@@ -100,7 +108,11 @@ $(function(){
        },       
        
        activate: function() {
-       }        
+       },
+       
+       clear:  function() {
+           this.$input.datepicker('setDate', null);
+       }   
 
     });
     
@@ -150,7 +162,16 @@ $(function(){
             firstDay: 0,
             changeYear: true,
             changeMonth: true
-        }
+        },
+        /**
+        Text shown as clear date button. 
+        If <code>false</code> clear button will not be rendered.
+        
+        @property clear 
+        @type boolean|string
+        @default 'x clear'         
+        **/
+        clear: '&times; clear'        
     });   
 
     $.fn.editableform.types.dateui = DateUI;
diff --git a/test/unit/dateui.js b/test/unit/dateui.js
index 1fc14fa..91c22fb 100644
--- a/test/unit/dateui.js
+++ b/test/unit/dateui.js
@@ -60,7 +60,7 @@ $(function () {
      
      test("viewformat, init by value", function () {
         var dview = '15/05/1984',
-            d = '1984-05-15',
+            d = '1984-05-15', 
             e = $('<a href="#" data-type="date" data-pk="1" data-weekstart="1" data-value="'+d+'"></a>').appendTo('#qunit-fixture').editable({
                 format: 'yyyy-mm-dd',
                 viewformat: 'dd/mm/yyyy'
@@ -80,6 +80,47 @@ $(function () {
         
         p.find('button[type=button]').click();
         ok(!p.is(':visible'), 'popover closed');      
-      });             
+      }); 
+      
+     asyncTest("clear button", function () {
+        var d = '15.05.1984',
+            f = 'dd.mm.yyyy',
+            e = $('<a href="#" data-type="date" data-pk="1" data-url="post-date-clear.php">'+d+'</a>').appendTo(fx).editable({
+                format: f,
+                clear: 'abc'
+            });
+                       
+          $.mockjax({
+              url: 'post-date-clear.php',
+              response: function(settings) {
+                  equal(settings.data.value, '', 'submitted value correct');            
+              }
+          });
+       
+        equal(frmt(e.data('editable').value, 'dd.mm.yyyy'), d, 'value correct');
+            
+        e.click();
+        var p = tip(e);
+        ok(p.find('.ui-datepicker').is(':visible'), 'datepicker exists');
+        
+        equal(frmt(e.data('editable').value, f), d, 'day set correct');
+        equal(p.find('a.ui-state-active').text(), 15, 'day shown correct');
+
+        var clear = p.find('.editable-clear');
+        equal(clear.text(), 'abc', 'clear link shown');
+
+        //click clear
+        clear.click();
+        p.find('form').submit();
+    
+        setTimeout(function() {          
+           ok(!p.is(':visible'), 'popover closed');
+           equal(e.data('editable').value, null, 'null saved to value');
+           equal(e.text(), e.data('editable').options.emptytext, 'empty text shown');
+           e.remove();    
+           start();  
+        }, timeout); 
+        
+     });                     
     
 });    
\ No newline at end of file

From 612ecc5e55a762ecf739a08b642bb1ac4d4baa08 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Mon, 26 Nov 2012 16:21:55 +0400
Subject: [PATCH 13/24] name is not requred anymore fixes #9

---
 CHANGELOG.txt                   |  1 +
 src/element/editable-element.js |  6 +-----
 src/inputs/list.js              |  5 ++---
 test/unit/api.js                |  1 -
 test/unit/checklist.js          |  5 +----
 test/unit/date.js               |  1 -
 test/unit/dateui.js             |  1 -
 test/unit/select.js             | 36 ++++++++++++++++++++-------------
 test/unit/text.js               |  1 -
 test/unit/textarea.js           |  4 ++--
 10 files changed, 29 insertions(+), 32 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index d9b3c8b..07824fc 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,6 +3,7 @@ X-editable changelog
        
 Version 1.1.0 wip
 ----------------------------   
+[enh #9] 'name' or 'id' is not required anymore (vitalets)      
 [enh] 'clear' button added in date and dateui (vitalets)      
 [enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets)      
 [enh] new input type: checklist (vitalets)      
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index ed282fd..fe581cd 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -29,12 +29,8 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                 return;
             }    
                 
-            //name must be defined
+            //name
             this.options.name = this.options.name || this.$element.attr('id');
-            if (!this.options.name) {
-                $.error('You must define name (or id) for Editable element');
-                return;
-            } 
              
             //create input of specified type. Input will be used for converting value, not in form
             if(typeof $.fn.editableform.types[this.options.type] === 'function') {
diff --git a/src/inputs/list.js b/src/inputs/list.js
index 33be516..fc8412c 100644
--- a/src/inputs/list.js
+++ b/src/inputs/list.js
@@ -93,17 +93,16 @@ List - abstract class for inputs that have source option loaded from js array or
                     cache.callbacks = [];
                     cache.err_callbacks = [];
                 }
-
+                
                 //loading sourceData from server
                 $.ajax({
                     url: this.options.source,
                     type: 'get',
                     cache: false,
-                    data: {name: this.options.name},
+                    data: this.options.name ? {name: this.options.name} : {},
                     dataType: 'json',
                     success: $.proxy(function (data) {
                         cache.loading = false;
-                        // this.options.source = data;
                         this.sourceData = this.makeArray(data);
                         if($.isArray(this.sourceData)) {
                             this.doPrepend();
diff --git a/test/unit/api.js b/test/unit/api.js
index dfb24fb..313e682 100644
--- a/test/unit/api.js
+++ b/test/unit/api.js
@@ -3,7 +3,6 @@ $(function () {
    module("api", {
         setup: function(){
             fx = $('#async-fixture');
-            delete $.fn.editable.defaults.name;
             $.support.transition = false;
         }
     });
diff --git a/test/unit/checklist.js b/test/unit/checklist.js
index 1f4258f..294d2a5 100644
--- a/test/unit/checklist.js
+++ b/test/unit/checklist.js
@@ -4,9 +4,6 @@ $(function () {
         setup: function(){
             sfx = $('#qunit-fixture'),
             fx = $('#async-fixture');               
-            $.fn.editable.defaults.name = 'name2';
-            //clear cache
-            $(document).removeData('groups.php-'+$.fn.editable.defaults.name);
             $.support.transition = false;
         }
     });  
@@ -58,7 +55,7 @@ $(function () {
          }, timeout);                              
     });                  
    
-     asyncTest("test limit", function () {
+     asyncTest("limit option", function () {
          var e = $('<a href="#" data-type="checklist" data-value="2,3" data-url="post.php"></a>').appendTo(fx).editable({
              pk: 1,
              source: groupsArr,
diff --git a/test/unit/date.js b/test/unit/date.js
index e2ab4cc..83f6707 100644
--- a/test/unit/date.js
+++ b/test/unit/date.js
@@ -5,7 +5,6 @@ $(function () {
    module("date", {
         setup: function(){
             fx = $('#async-fixture');
-            $.fn.editable.defaults.name = 'name1';
             dpg = $.fn.datepicker.DPGlobal;
         }
     });
diff --git a/test/unit/dateui.js b/test/unit/dateui.js
index 91c22fb..f6a1d20 100644
--- a/test/unit/dateui.js
+++ b/test/unit/dateui.js
@@ -5,7 +5,6 @@ $(function () {
    module("dateui", {
         setup: function(){
             fx = $('#async-fixture');
-            $.fn.editable.defaults.name = 'name1';
         }
     });
     
diff --git a/test/unit/select.js b/test/unit/select.js
index fec4a8e..b4b969f 100644
--- a/test/unit/select.js
+++ b/test/unit/select.js
@@ -4,9 +4,6 @@ $(function () {
         setup: function(){
             sfx = $('#qunit-fixture'),
             fx = $('#async-fixture');               
-            $.fn.editable.defaults.name = 'name1';
-            //clear cache
-            $(document).removeData('groups.php-'+$.fn.editable.defaults.name);
             $.support.transition = false;
         }
     });  
@@ -167,7 +164,7 @@ $(function () {
         }, timeout);                     
     })           
    
-    asyncTest("popover should save new selected value", function () {
+    asyncTest("should save new selected value", function () {
          var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo(fx).editable({
              pk: 1,
              source: groups
@@ -192,8 +189,8 @@ $(function () {
                e.remove();    
                start();  
          }, timeout);                              
-    });                  
-   
+    });    
+    
      asyncTest("if new text is empty --> show emptytext on save", function () {
         var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo(fx).editable({
              pk: 1,
@@ -248,8 +245,11 @@ $(function () {
      });
      
      asyncTest("cache request for same selects", function () {
-         var e = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(),
-             e1 = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(),
+        //clear cache
+        $(document).removeData('groups.php-name1');         
+                                 
+         var e = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(),
+             e1 = $('<a href="#" data-type="select" data-pk="1" id="name1" data-value="2" data-url="post.php" data-source="groups-cache.php">customer</a>').appendTo(fx).editable(),
              req = 0;
 
         $.mockjax({
@@ -293,6 +293,10 @@ $(function () {
      
     asyncTest("cache simultaneous requests", function () {
         expect(4);
+        
+        //clear cache
+        $(document).removeData('groups.php-name1');          
+        
         var req = 0;
         $.mockjax({
                 url: 'groups-cache-sim.php',
@@ -303,9 +307,9 @@ $(function () {
                 }
          });  
 
-         var e = $('<a href="#" data-type="select" data-pk="1" data-value="1" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(),
-             e1 = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(),
-             e2 = $('<a href="#" data-type="select" data-pk="1" data-value="3" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable();
+         var e = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="1" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(),
+             e1 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="2" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable(),
+             e2 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="3" data-url="post.php" data-source="groups-cache-sim.php"></a>').appendTo(fx).editable();
            
           setTimeout(function() {
 
@@ -324,6 +328,10 @@ $(function () {
      
     asyncTest("cache simultaneous requests (loading error)", function () {
         expect(4);
+        
+        //clear cache
+        $(document).removeData('groups.php-name1');           
+        
         var req = 0;
         $.mockjax({
                 url: 'groups-cache-sim-err.php',
@@ -334,9 +342,9 @@ $(function () {
                 }
          });  
 
-         var e = $('<a href="#" data-type="select" data-pk="1" data-value="1" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(),
-             e1 = $('<a href="#" data-type="select" data-pk="1" data-value="2" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(),
-             e2 = $('<a href="#" data-type="select" data-pk="1" data-value="3" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">6456</a>').appendTo(fx).editable(),
+         var e = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="1" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(),
+             e1 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="2" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">35</a>').appendTo(fx).editable(),
+             e2 = $('<a href="#" data-type="select" data-pk="1" data-name="name1" data-value="3" data-autotext="always" data-url="post.php" data-source="groups-cache-sim-err.php">6456</a>').appendTo(fx).editable(),
              errText = $.fn.editableform.types.select.defaults.sourceError;
            
           setTimeout(function() {
diff --git a/test/unit/text.js b/test/unit/text.js
index df70bda..79f1b90 100644
--- a/test/unit/text.js
+++ b/test/unit/text.js
@@ -3,7 +3,6 @@ $(function () {
     module("text", {
        setup: function() {
            fx = $('#async-fixture');
-           $.fn.editable.defaults.name = 'name1';
            $.support.transition = false;
        }
    });
diff --git a/test/unit/textarea.js b/test/unit/textarea.js
index cee65bc..6a76813 100644
--- a/test/unit/textarea.js
+++ b/test/unit/textarea.js
@@ -5,8 +5,8 @@ $(function () {
     
    module("textarea", {
         setup: function(){
-            fx = $('#async-fixture'),
-            $.fn.editable.defaults.name = 'name1';
+            fx = $('#async-fixture');
+            $.support.transition = false;
         }
     });
       

From 396775bf8cd0e1200f122a27a50e5d8a6cbe9bb5 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 00:37:15 +0400
Subject: [PATCH 14/24] object.keys workaround

---
 src/editable-form/editable-form-utils.js | 196 ++++++++++++-----------
 src/inputs/select.js                     |   2 +-
 2 files changed, 107 insertions(+), 91 deletions(-)

diff --git a/src/editable-form/editable-form-utils.js b/src/editable-form/editable-form-utils.js
index 3e2eaa6..4d8169d 100644
--- a/src/editable-form/editable-form-utils.js
+++ b/src/editable-form/editable-form-utils.js
@@ -2,104 +2,120 @@
 * EditableForm utilites
 */
 (function ($) {
-    $.extend($.fn.editableform, {
-        utils: {
-            /**
-            * classic JS inheritance function
-            */
-            inherit: function (Child, Parent) {
-                var F = function() { };
-                F.prototype = Parent.prototype;
-                Child.prototype = new F();
-                Child.prototype.constructor = Child;
-                Child.superclass = Parent.prototype;
-            },
+    $.fn.editableform.utils = {
+        /**
+        * classic JS inheritance function
+        */
+        inherit: function (Child, Parent) {
+            var F = function() { };
+            F.prototype = Parent.prototype;
+            Child.prototype = new F();
+            Child.prototype.constructor = Child;
+            Child.superclass = Parent.prototype;
+        },
 
-            /**
-            * set caret position in input
-            * see http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
-            */        
-            setCursorPosition: function(elem, pos) {
-                if (elem.setSelectionRange) {
-                    elem.setSelectionRange(pos, pos);
-                } else if (elem.createTextRange) {
-                    var range = elem.createTextRange();
-                    range.collapse(true);
-                    range.moveEnd('character', pos);
-                    range.moveStart('character', pos);
-                    range.select();
-                }
-            },
+        /**
+        * set caret position in input
+        * see http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
+        */        
+        setCursorPosition: function(elem, pos) {
+            if (elem.setSelectionRange) {
+                elem.setSelectionRange(pos, pos);
+            } else if (elem.createTextRange) {
+                var range = elem.createTextRange();
+                range.collapse(true);
+                range.moveEnd('character', pos);
+                range.moveStart('character', pos);
+                range.select();
+            }
+        },
 
-            /**
-            * function to parse JSON in *single* quotes. (jquery automatically parse only double quotes)
-            * That allows such code as: <a data-source="{'a': 'b', 'c': 'd'}">
-            * safe = true --> means no exception will be thrown
-            * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery
-            */
-            tryParseJson: function(s, safe) {
-                if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) {
-                    if (safe) {
-                        try {
-                            /*jslint evil: true*/
-                            s = (new Function('return ' + s))();
-                            /*jslint evil: false*/
-                        } catch (e) {} finally {
-                            return s;
-                        }
-                    } else {
+        /**
+        * function to parse JSON in *single* quotes. (jquery automatically parse only double quotes)
+        * That allows such code as: <a data-source="{'a': 'b', 'c': 'd'}">
+        * safe = true --> means no exception will be thrown
+        * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery
+        */
+        tryParseJson: function(s, safe) {
+            if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) {
+                if (safe) {
+                    try {
                         /*jslint evil: true*/
                         s = (new Function('return ' + s))();
                         /*jslint evil: false*/
+                    } catch (e) {} finally {
+                        return s;
                     }
+                } else {
+                    /*jslint evil: true*/
+                    s = (new Function('return ' + s))();
+                    /*jslint evil: false*/
                 }
-                return s;
-            },
+            }
+            return s;
+        },
 
-            /**
-            * slice object by specified keys
-            */
-            sliceObj: function(obj, keys, caseSensitive /* default: false */) {
-                var key, keyLower, newObj = {};
-
-                if (!$.isArray(keys) || !keys.length) {
-                    return newObj;
-                }
-
-                for (var i = 0; i < keys.length; i++) {
-                    key = keys[i];
-                    if (obj.hasOwnProperty(key)) {
-                        newObj[key] = obj[key];
-                    }
-
-                    if(caseSensitive === true) {
-                        continue;
-                    }
-
-                    //when getting data-* attributes via $.data() it's converted to lowercase.
-                    //details: http://stackoverflow.com/questions/7602565/using-data-attributes-with-jquery
-                    //workaround is code below.
-                    keyLower = key.toLowerCase();
-                    if (obj.hasOwnProperty(keyLower)) {
-                        newObj[key] = obj[keyLower];
-                    }
-                }
+        /**
+        * slice object by specified keys
+        */
+        sliceObj: function(obj, keys, caseSensitive /* default: false */) {
+            var key, keyLower, newObj = {};
 
+            if (!$.isArray(keys) || !keys.length) {
                 return newObj;
-            },
-            
-            /**
-            * exclude complex objects from $.data() before pass to config
-            */
-            getConfigData: function($element) {
-                var data = {};
-                $.each($element.data(), function(k, v) {
-                   if(typeof v !== 'object' || (v && typeof v === 'object' && v.constructor === Object)) {
-                      data[k] = v;
-                   }
-                });
-                return data;
-            }            
-        }
-    });      
+            }
+
+            for (var i = 0; i < keys.length; i++) {
+                key = keys[i];
+                if (obj.hasOwnProperty(key)) {
+                    newObj[key] = obj[key];
+                }
+
+                if(caseSensitive === true) {
+                    continue;
+                }
+
+                //when getting data-* attributes via $.data() it's converted to lowercase.
+                //details: http://stackoverflow.com/questions/7602565/using-data-attributes-with-jquery
+                //workaround is code below.
+                keyLower = key.toLowerCase();
+                if (obj.hasOwnProperty(keyLower)) {
+                    newObj[key] = obj[keyLower];
+                }
+            }
+
+            return newObj;
+        },
+
+        /**
+        * exclude complex objects from $.data() before pass to config
+        */
+        getConfigData: function($element) {
+            var data = {};
+            $.each($element.data(), function(k, v) {
+                if(typeof v !== 'object' || (v && typeof v === 'object' && v.constructor === Object)) {
+                    data[k] = v;
+                }
+            });
+            return data;
+        },
+
+        objectKeys: function(o) {
+            if (Object.keys) {
+                return Object.keys(o);  
+            } else {
+                if (o !== Object(o)) {
+                    throw new TypeError('Object.keys called on a non-object');
+                }
+                var k=[], p;
+                for (p in o) {
+                    if (Object.prototype.hasOwnProperty.call(o,p)) {
+                        k.push(p);
+                    }
+                }
+                return k;
+            }
+
+        }   
+    };      
 }(window.jQuery));
\ No newline at end of file
diff --git a/src/inputs/select.js b/src/inputs/select.js
index 89b4265..9497ae1 100644
--- a/src/inputs/select.js
+++ b/src/inputs/select.js
@@ -1,5 +1,5 @@
 /**
-Select (dropdown) input
+Select (dropdown)
 
 @class select
 @extends list

From 1e757b833fe086a2722bbec70680cc7d32d12f5d Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 00:37:57 +0400
Subject: [PATCH 15/24] css workaround to nowrap buttons in position absolute
 mode

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

diff --git a/src/containers/editable-container.css b/src/containers/editable-container.css
index 927ab10..9098275 100644
--- a/src/containers/editable-container.css
+++ b/src/containers/editable-container.css
@@ -1,6 +1,8 @@
-.editable-container,
-.editable-container.popover {
-    width: auto; 
-    white-space: nowrap; /* without this rule buttons wrap input in non-static elements */
+.editable-container {
     max-width: none; /* without this rule poshytip does not stretch */
+}  
+
+.editable-container.popover {
+/*   width: 300px;*/  /* debug */
+    width: auto; /* without this rule popover does not stretch */
 }
\ No newline at end of file
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 6c18918..446e3fa 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -1,11 +1,20 @@
-.editableform,
-.editableform div.control-group {
-    margin-bottom: 0;
-    overflow: auto; 
+.editableform {
+    margin-bottom: 0; /* overwrites bootstrap margin */
+}
+
+.editableform .control-group {
+    margin-bottom: 0; /* overwrites bootstrap margin */
+    white-space: nowrap; /* prevent wrapping buttons on new line */
 }
 
 .editable-buttons {
-   float: left;  
+   display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
+}
+
+.editable-input {
+    vertical-align: top; 
+    display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
+    width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
 }
 
 .editable-buttons button {
@@ -28,6 +37,7 @@
 
  .editable-error-block {
     max-width: 300px;
+    padding-top: 3px;
     margin: 0;
     clear: both; 
 }
@@ -36,14 +46,6 @@
    color: red;  
 }
 
-.editable-input {
-    vertical-align: top; 
-    display: inline-block;
-    width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
-    float: left;
-    padding-bottom: 9px;
-}
-
 .editableform textarea {
     height: 150px; /*default height for textarea*/
 }
@@ -51,9 +53,11 @@
 .editableform .editable-date {
     padding: 0; 
     margin: 0;
+    float: left;
 }
 
 .editable-clear {
+    clear: both;
    float: right; 
    font-size: 0.9em;
    text-decoration: none;
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 854e40d..8cf8bd2 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -23,7 +23,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             //create input of specified type
             if(typeof $.fn.editableform.types[this.options.type] === 'function') {
                 TypeConstructor = $.fn.editableform.types[this.options.type];
-                typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults));
+                typeOptions = $.fn.editableform.utils.sliceObj(this.options, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults));
                 this.input = new TypeConstructor(typeOptions);
             } else {
                 $.error('Unknown type: '+ this.options.type);

From 18f29820f3f6cb4936143161f3eac51e8557eb4d Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 13:25:42 +0400
Subject: [PATCH 16/24] many fixes for ie7+ compatibility

---
 src/editable-form/editable-form-bootstrap.js |   4 +-
 src/editable-form/editable-form-jqueryui.js  |   6 +-
 src/editable-form/editable-form.css          |  20 +-
 src/editable-form/editable-form.js           | 247 ++++++++++---------
 src/element/editable-element.js              |   8 +-
 src/inputs/date/date.js                      |   2 +-
 src/inputs/dateui/dateui.js                  |   2 +-
 7 files changed, 154 insertions(+), 135 deletions(-)

diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js
index f6a5273..d55e3fd 100644
--- a/src/editable-form/editable-form-bootstrap.js
+++ b/src/editable-form/editable-form-bootstrap.js
@@ -14,8 +14,8 @@ Editableform based on Twitter Bootstrap
     });    
     
     //buttons
-    $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i></button>'+
-                                '<button type="button" class="btn clearfix"><i class="icon-ban-circle"></i></button>';         
+    $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary editable-submit"><i class="icon-ok icon-white"></i></button>'+
+                                '<button type="button" class="btn editable-cancel"><i class="icon-ban-circle"></i></button>';         
     
     //error classes
     $.fn.editableform.errorGroupClass = 'error';
diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js
index c0b6683..b5983c8 100644
--- a/src/editable-form/editable-form-jqueryui.js
+++ b/src/editable-form/editable-form-jqueryui.js
@@ -8,12 +8,12 @@ Editableform based on jQuery UI
             this.$form = $($.fn.editableform.template);
 
             //buttons
-            this.$form.find('div.editable-buttons').append($.fn.editableform.buttons);                
-            this.$form.find('button[type=submit]').button({
+            this.$form.find('.editable-buttons').append($.fn.editableform.buttons);                
+            this.$form.find('.editable-submit').button({
                 icons: { primary: "ui-icon-check" },
                 text: false
             }).removeAttr('title');
-            this.$form.find('button[type=button]').button({
+            this.$form.find('.editable-cancel').button({
                 icons: { primary: "ui-icon-cancel" },
                 text: false
             }).removeAttr('title');
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 446e3fa..f7acde3 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -9,15 +9,24 @@
 
 .editable-buttons {
    display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
+   vertical-align: top;
+   margin-left: 7px;
+   /* display-inline emulation for IE7*/
+   zoom: 1; 
+   *display: inline;
 }
 
 .editable-input {
     vertical-align: top; 
     display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
     width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
+    white-space: normal; /* reset white-space decalred in parent*/
+   /* display-inline emulation for IE7*/
+   zoom: 1; 
+   *display: inline;   
 }
 
-.editable-buttons button {
+.editable-buttons .editable-cancel {
    margin-left: 7px; 
 }
 
@@ -28,7 +37,8 @@
 
 .editableform-loading {
     background: url('img/loading.gif') center center no-repeat;  
-    height: 25px;  
+    height: 25px;
+    width: auto;  
 }
 
 .editable-inline .editableform-loading {
@@ -39,7 +49,7 @@
     max-width: 300px;
     padding-top: 3px;
     margin: 0;
-    clear: both; 
+    width: auto;
 }
 
 .editable-error {
@@ -57,8 +67,8 @@
 }
 
 .editable-clear {
-    clear: both;
-   float: right; 
+   clear: both;
    font-size: 0.9em;
    text-decoration: none;
+   text-align: right;
 }
\ No newline at end of file
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index 8cf8bd2..ed0ac53 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -8,7 +8,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
 @uses textarea
 **/
 (function ($) {
-    
+
     var EditableForm = function (element, options) {
         this.options = $.extend({}, $.fn.editableform.defaults, options);
         this.$element = $(element); //div (usually), containing form. not form tag!
@@ -19,7 +19,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         constructor: EditableForm,
         initInput: function() {  //called once
             var TypeConstructor, typeOptions;
-            
+
             //create input of specified type
             if(typeof $.fn.editableform.types[this.options.type] === 'function') {
                 TypeConstructor = $.fn.editableform.types[this.options.type];
@@ -34,7 +34,7 @@ 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);              
         },
@@ -47,54 +47,57 @@ 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();
-           
+
             this.initTemplate(); 
-            
+
             /**        
             Fired when rendering starts
             @event rendering 
             @param {Object} event event object
             **/            
             this.$element.triggerHandler('rendering');
-            
+
             //render input
             $.when(this.input.render())
             .then($.proxy(function () {
                 //input
                 this.$form.find('div.editable-input').append(this.input.$input);
-                
-                //clear link
+
+                //"clear" link
                 if(this.input.$clear) {
-                    this.$form.find('div.editable-input').append(this.input.$clear);  
+                    this.$form.find('div.editable-input').append($('<div class="editable-clear">').append(this.input.$clear));  
                 }                
-                
-                //attach 'cancel' handler
-                this.$form.find('button[type=button]').click($.proxy(this.cancel, this));
+
                 //append form to container
                 this.$element.append(this.$form);
+
+                //attach 'cancel' handler
+                this.$form.find('.editable-cancel').click($.proxy(this.cancel, this));
+                //                this.$form.find('.editable-buttons button').eq(1).click($.proxy(this.cancel, this));
+
                 if(this.input.error) {
                     this.error(this.input.error);
-                    this.$form.find('button[type=submit]').attr('disabled', true);
+                    this.$form.find('.editable-submit').attr('disabled', true);
                     this.input.$input.attr('disabled', true);
                 } else {
                     this.error(false);
                     this.input.$input.removeAttr('disabled');
-                    this.$form.find('button[type=submit]').removeAttr('disabled');
+                    this.$form.find('.editable-submit').removeAttr('disabled');
                     this.input.value2input(this.value);
                     this.$form.submit($.proxy(this.submit, this));
                 }
-                
+
                 /**        
                 Fired when form is rendered
                 @event rendered
                 @param {Object} event event object
                 **/            
                 this.$element.triggerHandler('rendered');                
-                
+
                 this.showForm();
             }, this));
         },
-        cancel: function() {
+        cancel: function() {   
             /**        
             Fired when form was cancelled by user
             @event cancel 
@@ -103,12 +106,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             this.$element.triggerHandler('cancel');
         },
         showLoading: function() {
-            var fw, fh, iw, bh;
-            //set loading size equal to form
+            var w;
             if(this.$form) {
+                //set loading size equal to form 
                 this.$loading.width(this.$form.outerWidth());
                 this.$loading.height(this.$form.outerHeight());
                 this.$form.hide();
+            } else {
+                //stretch loading to fill container width
+                w = this.$loading.parent().width();
+                if(w) {
+                    this.$loading.width(w);
+                }
             }
             this.$loading.show(); 
         },
@@ -124,11 +133,11 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             **/                    
             this.$element.triggerHandler('show');
         },
-        
+
         error: function(msg) {
             var $group = this.$form.find('.control-group'),
-                $block = this.$form.find('.editable-error-block');
-                
+            $block = this.$form.find('.editable-error-block');
+
             if(msg === false) {
                 $group.removeClass($.fn.editableform.errorGroupClass);
                 $block.removeClass($.fn.editableform.errorBlockClass).empty().hide(); 
@@ -137,15 +146,15 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 $block.addClass($.fn.editableform.errorBlockClass).text(msg).show();
             }
         },
-               
+
         submit: function(e) {
             e.stopPropagation();
             e.preventDefault();
 
             var error,
-                //get value from input
-                newValue = this.input.input2value(),
-                newValueStr;
+            //get value from input
+            newValue = this.input.input2value(),
+            newValueStr;
 
             //validation
             if (error = this.validate(newValue)) {
@@ -153,14 +162,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;
             } 
@@ -175,36 +184,36 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                     this.showForm();
                     return;
                 }                
-                
-               //clear error message
-               this.error(false);   
-               this.value = newValue;
-               /**        
-               Fired when form is submitted
-               @event save 
-               @param {Object} event event object
-               @param {Object} params additional params
-                    @param {mixed} params.newValue submitted value
-                    @param {Object} params.response ajax response
-                    
-               @example
-                   $('#form-div').on('save'), function(e, params){
-                       if(params.newValue === 'username') {...}
-                   });                    
-               **/                
-               this.$element.triggerHandler('save', {newValue: newValue, response: response});
+
+                //clear error message
+                this.error(false);   
+                this.value = newValue;
+                /**        
+                Fired when form is submitted
+                @event save 
+                @param {Object} event event object
+                @param {Object} params additional params
+                @param {mixed} params.newValue submitted value
+                @param {Object} params.response ajax response
+
+                @example
+                $('#form-div').on('save'), function(e, params){
+                if(params.newValue === 'username') {...}
+                });                    
+                **/                
+                this.$element.triggerHandler('save', {newValue: newValue, response: response});
             }, this))
             .fail($.proxy(function(xhr) {
-               this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!'); 
-               this.showForm();  
+                this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!'); 
+                this.showForm();  
             }, this));
         },
 
         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;
-                
+            send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))),
+            params;
+
             if (send) { //send to server
                 this.showLoading();
 
@@ -214,7 +223,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                     value: value,
                     pk: pk 
                 };
-                
+
                 //additional params
                 if(typeof this.options.params === 'function') {
                     $.extend(params, this.options.params.call(this, params));  
@@ -236,7 +245,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 }
             }
         }, 
-        
+
         validate: function (value) {
             if (value === undefined) {
                 value = this.value;
@@ -245,38 +254,38 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
                 return this.options.validate.call(this, value);
             }
         },
-        
-       option: function(key, value) {
-          this.options[key] = value;
-          if(key === 'value') {
-              this.setValue(value);
-          }
-       },
-       
-       setValue: function(value, convertStr) {
-          if(convertStr) {
-              this.value = this.input.str2value(value);
-          } else {
-              this.value = value;
-          }
-       }               
+
+        option: function(key, value) {
+            this.options[key] = value;
+            if(key === 'value') {
+                this.setValue(value);
+            }
+        },
+
+        setValue: function(value, convertStr) {
+            if(convertStr) {
+                this.value = this.input.str2value(value);
+            } else {
+                this.value = value;
+            }
+        }               
     };
 
     /*
     Initialize editableform. Applied to jQuery object.
-    
+
     @method $().editableform(options)
     @params {Object} options
     @example
-        var $form = $('&lt;div&gt;').editableform({
-            type: 'text',
-            name: 'username',
-            url: '/post',
-            value: 'vitaliy'
-        });
-        
-        //to display form you should call 'render' method
-        $form.editableform('render');     
+    var $form = $('&lt;div&gt;').editableform({
+    type: 'text',
+    name: 'username',
+    url: '/post',
+    value: 'vitaliy'
+    });
+
+    //to display form you should call 'render' method
+    $form.editableform('render');     
     */
     $.fn.editableform = function (option) {
         var args = arguments;
@@ -287,20 +296,20 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
             if (!data) {
                 $this.data('editableform', (data = new EditableForm(this, options)));
             }
-            
+
             if (typeof option === 'string') { //call method 
                 data[option].apply(data, Array.prototype.slice.call(args, 1));
             } 
         });
     };
-    
+
     //keep link to constructor to allow inheritance
     $.fn.editableform.Constructor = EditableForm;    
 
     //defaults
     $.fn.editableform.defaults = {
         /* see also defaults for input */
-        
+
         /**
         Type of input. Can be <code>text|textarea|select|date|checklist</code>
 
@@ -318,12 +327,12 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @default null
         @example
         url: function(params) {
-           if(params.value === 'abc') {
-               var d = new $.Deferred;
-               return d.reject('field cannot be "abc"'); //returning error via deferred object
-           } else {
-               someModel.set(params.name, params.value); //save data in some js model
-           }
+        if(params.value === 'abc') {
+        var d = new $.Deferred;
+        return d.reject('field cannot be "abc"'); //returning error via deferred object
+        } else {
+        someModel.set(params.name, params.value); //save data in some js model
+        }
         }        
         **/        
         url:null,
@@ -331,7 +340,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() {
-           return { a: 1 };
+        return { a: 1 };
         }
 
         @property params 
@@ -382,54 +391,54 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @default null
         @example
         validate: function(value) {
-            if($.trim(value) == '') {
-                return 'This field is required';
-            }
+        if($.trim(value) == '') {
+        return 'This field is required';
+        }
         }
         **/         
         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.
-        
+
         @property success 
         @type function
         @default null
         @example
         success: function(response, newValue) {
-            if(!response.success) return response.msg;
+        if(!response.success) return response.msg;
         }
         **/          
         success: function(response, newValue) {}         
     };   
 
     /*
-      Note: following params could redefined in engine: bootstrap or jqueryui:
-      Classes 'control-group' and 'editable-error-block' must always present!
+    Note: following params could redefined in engine: bootstrap or jqueryui:
+    Classes 'control-group' and 'editable-error-block' must always present!
     */      
-      $.fn.editableform.template = '<form class="form-inline editableform">'+
-       '<div class="control-group">' + 
-           '<div class="editable-input"></div><div class="editable-buttons"></div>'+
-           '<div class="editable-error-block"></div>' + 
-       '</div>' + 
-       '</form>';
-      
-      //loading div
-      $.fn.editableform.loading = '<div class="editableform-loading"></div>';
-      
-      //buttons
-      $.fn.editableform.buttons = '<button type="submit">ok</button>'+
-                                  '<button type="button">cancel</button>';      
-      
-      //error class attahced to control-group
-      $.fn.editableform.errorGroupClass = null;  
-      
-      //error class attahced to editable-error-block
-      $.fn.editableform.errorBlockClass = 'editable-error';
+    $.fn.editableform.template = '<form class="form-inline editableform">'+
+    '<div class="control-group">' + 
+    '<div><div class="editable-input"></div><div class="editable-buttons"></div></div>'+
+    '<div class="editable-error-block"></div>' + 
+    '</div>' + 
+    '</form>';
 
-      //input types
-      $.fn.editableform.types = {};
-      //utils
-      $.fn.editableform.utils = {};
+    //loading div
+    $.fn.editableform.loading = '<div class="editableform-loading"></div>';
+
+    //buttons
+    $.fn.editableform.buttons = '<button type="submit" class="editable-submit">ok</button>'+
+    '<button type="button" class="editable-cancel">cancel</button>';      
+
+    //error class attahced to control-group
+    $.fn.editableform.errorGroupClass = null;  
+
+    //error class attahced to editable-error-block
+    $.fn.editableform.errorBlockClass = 'editable-error';
+
+    //input types
+    $.fn.editableform.types = {};
+    //utils
+    $.fn.editableform.utils = {};
 
 }(window.jQuery));
\ No newline at end of file
diff --git a/src/element/editable-element.js b/src/element/editable-element.js
index fe581cd..f51afec 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -35,7 +35,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             //create input of specified type. Input will be used for converting value, not in form
             if(typeof $.fn.editableform.types[this.options.type] === 'function') {
                 TypeConstructor = $.fn.editableform.types[this.options.type];
-                this.typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults));
+                this.typeOptions = $.fn.editableform.utils.sliceObj(this.options, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults));
                 this.input = new TypeConstructor(this.typeOptions);
             } else {
                 $.error('Unknown type: '+ this.options.type);
@@ -53,7 +53,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
             //attach handler to close any container on escape
             $(document).off('keyup.editable').on('keyup.editable', function (e) {
                 if (e.which === 27) {
-                    $('.editable-container').find('button[type=button]').click();
+                    $('.editable-container').find('.editable-cancel').click();
                 }
             }); 
             
@@ -65,7 +65,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
                     return;
                 }
                 //close all other containers
-                $('.editable-container').find('button[type=button]').click();
+                $('.editable-container').find('.editable-cancel').click();
             });
             
             //add 'editable' class
@@ -229,7 +229,7 @@ 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('button[type=button]').click();
+            $('.editable-container').find('.editable-cancel').click();
             
             //show container
             this.container.show();
diff --git a/src/inputs/date/date.js b/src/inputs/date/date.js
index b16f57e..6490021 100644
--- a/src/inputs/date/date.js
+++ b/src/inputs/date/date.js
@@ -56,7 +56,7 @@ $(function(){
             this.$input.datepicker(this.options.datepicker);
                         
             if(this.options.clear) {
-                this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){
+                this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
                     e.preventDefault();
                     e.stopPropagation();
                     this.clear();
diff --git a/src/inputs/dateui/dateui.js b/src/inputs/dateui/dateui.js
index 986726a..5b39f27 100644
--- a/src/inputs/dateui/dateui.js
+++ b/src/inputs/dateui/dateui.js
@@ -54,7 +54,7 @@ $(function(){
             this.$input.datepicker(this.options.datepicker);
             
             if(this.options.clear) {
-                this.$clear = $('<a href="#">').addClass('editable-clear').html(this.options.clear).click($.proxy(function(e){
+                this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
                     e.preventDefault();
                     e.stopPropagation();
                     this.clear();

From 02c4c685ae915326a63754c4dc3f88b7f5fc7149 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 13:47:29 +0400
Subject: [PATCH 17/24] fix tests for abs position and date clear

---
 test/unit/common.js | 12 +++++++++++-
 test/unit/date.js   |  2 +-
 test/unit/dateui.js |  4 ++--
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/test/unit/common.js b/test/unit/common.js
index c6fd49e..468c48e 100644
--- a/test/unit/common.js
+++ b/test/unit/common.js
@@ -122,7 +122,17 @@
         ok(!p.is(':visible'), 'popover closed');
      });        
       
-      
+     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.click();
+        var p = tip(e);
+        ok(p.find('button').offset().top < p.find('.editable-input').offset().top + p.find('.editable-input').height(), 'buttons top ok');
+        ok(p.find('button').offset().left > p.find('.editable-input').offset().left + p.find('.editable-input').width(), 'buttons left ok');
+       
+        d.remove();
+     });       
       
       //unfortunatly, testing this feature does not always work in browsers. Tested manually.
       /*
diff --git a/test/unit/date.js b/test/unit/date.js
index 83f6707..16ca25a 100644
--- a/test/unit/date.js
+++ b/test/unit/date.js
@@ -153,7 +153,7 @@ $(function () {
         equal(frmt(e.data('editable').value, f), d, 'day set correct');
         equal(p.find('td.day.active').text(), 15, 'day shown correct');
 
-        var clear = p.find('.editable-clear');
+        var clear = p.find('.editable-clear a');
         equal(clear.text(), 'abc', 'clear link shown');
 
         //click clear
diff --git a/test/unit/dateui.js b/test/unit/dateui.js
index f6a1d20..5af7255 100644
--- a/test/unit/dateui.js
+++ b/test/unit/dateui.js
@@ -105,7 +105,7 @@ $(function () {
         equal(frmt(e.data('editable').value, f), d, 'day set correct');
         equal(p.find('a.ui-state-active').text(), 15, 'day shown correct');
 
-        var clear = p.find('.editable-clear');
+        var clear = p.find('.editable-clear a');
         equal(clear.text(), 'abc', 'clear link shown');
 
         //click clear
@@ -118,7 +118,7 @@ $(function () {
            equal(e.text(), e.data('editable').options.emptytext, 'empty text shown');
            e.remove();    
            start();  
-        }, timeout); 
+        }, 500); 
         
      });                     
     

From 5ef1a9e18465f77d46b712a2ac935bce4622ec6f Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 14:09:48 +0400
Subject: [PATCH 18/24] chcklist vertical alignment

---
 src/editable-form/editable-form.css |  8 ++++++++
 src/inputs/checklist.js             | 13 ++++++++++---
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index f7acde3..049eb56 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -66,6 +66,14 @@
     float: left;
 }
 
+
+/* checklist vertical alignment */
+.editable-checklist label input[type="checkbox"], 
+.editable-checklist label span {
+    vertical-align: middle;
+    margin: 0;
+}
+
 .editable-clear {
    clear: both;
    font-size: 0.9em;
diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index 4bd634a..ffdc7c9 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -36,12 +36,12 @@ $(function(){
             }
 
             for(var i=0; i<this.sourceData.length; i++) {
-                $label = $('<label>').text(' '+this.sourceData[i].text)
-                                     .prepend($('<input>', {
+                $label = $('<label>').append($('<input>', {
                                            type: 'checkbox',
                                            value: this.sourceData[i].value, 
                                            name: this.options.name
-                                     }));
+                                     }))
+                                     .append($('<span>').text(' '+this.sourceData[i].text));
                 
                 $('<div>').append($label).appendTo(this.$input);
             }
@@ -109,6 +109,13 @@ $(function(){
         **/         
         tpl:'<div></div>',
         
+        /**
+        @property inputclass 
+        @type string
+        @default span2 editable-checklist
+        **/         
+        inputclass: 'span2 editable-checklist',        
+        
         /**
         Separator of values in string when sending to server
 

From 5d8861a49ab5743c8e84aab920383d0b7c5df046 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 14:21:08 +0400
Subject: [PATCH 19/24] change cancel button

---
 CHANGELOG.txt                                | 1 +
 README.md                                    | 2 +-
 src/editable-form/editable-form-bootstrap.js | 2 +-
 src/editable-form/editable-form-jqueryui.js  | 2 +-
 src/editable-form/editable-form.css          | 8 ++++++--
 5 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 07824fc..6b1e46b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,6 +3,7 @@ X-editable changelog
        
 Version 1.1.0 wip
 ----------------------------   
+[enh] added support for IE7+ (vitalets)  
 [enh #9] 'name' or 'id' is not required anymore (vitalets)      
 [enh] 'clear' button added in date and dateui (vitalets)      
 [enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets)      
diff --git a/README.md b/README.md
index d8fe40a..1a3ef1a 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ Your feedback is very appreciated!
 ## Contribution
 A few steps how to start contributing:  
 
-1.[Fork X-editable](https://github.com/vitalets/x-editable/fork)  
+1.[Fork X-editable](https://github.com/vitalets/x-editable/fork) and pull the latest changes from <code>dev</code> branch
 
 2.Arrange local directory structure. It should be:  
 **x-editable**  
diff --git a/src/editable-form/editable-form-bootstrap.js b/src/editable-form/editable-form-bootstrap.js
index d55e3fd..50f72bb 100644
--- a/src/editable-form/editable-form-bootstrap.js
+++ b/src/editable-form/editable-form-bootstrap.js
@@ -15,7 +15,7 @@ Editableform based on Twitter Bootstrap
     
     //buttons
     $.fn.editableform.buttons = '<button type="submit" class="btn btn-primary editable-submit"><i class="icon-ok icon-white"></i></button>'+
-                                '<button type="button" class="btn editable-cancel"><i class="icon-ban-circle"></i></button>';         
+                                '<button type="button" class="btn editable-cancel"><i class="icon-remove"></i></button>';         
     
     //error classes
     $.fn.editableform.errorGroupClass = 'error';
diff --git a/src/editable-form/editable-form-jqueryui.js b/src/editable-form/editable-form-jqueryui.js
index b5983c8..804b6d2 100644
--- a/src/editable-form/editable-form-jqueryui.js
+++ b/src/editable-form/editable-form-jqueryui.js
@@ -14,7 +14,7 @@ Editableform based on jQuery UI
                 text: false
             }).removeAttr('title');
             this.$form.find('.editable-cancel').button({
-                icons: { primary: "ui-icon-cancel" },
+                icons: { primary: "ui-icon-closethick" },
                 text: false
             }).removeAttr('title');
 
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 049eb56..1ff18fe 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -47,11 +47,15 @@
 
  .editable-error-block {
     max-width: 300px;
-    padding-top: 3px;
-    margin: 0;
+    margin: 5px 0 0 0;
     width: auto;
 }
 
+/*add padding for jquery ui*/
+.editable-error-block.ui-state-error {
+    padding: 3px;  
+}  
+
 .editable-error {
    color: red;  
 }

From f50bb8d4cb1b1537ddde5f1d86e2c1be6e6fb538 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 14:27:10 +0400
Subject: [PATCH 20/24] tiny fixes

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

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 6b1e46b..dd7da67 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,6 +3,7 @@ X-editable changelog
        
 Version 1.1.0 wip
 ----------------------------   
+[enh #11] cancel button changed to 'cross' (tarciozemel)  
 [enh] added support for IE7+ (vitalets)  
 [enh #9] 'name' or 'id' is not required anymore (vitalets)      
 [enh] 'clear' button added in date and dateui (vitalets)      
diff --git a/README.md b/README.md
index 1a3ef1a..ed6d827 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,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  
-You will get distributive in **lib/dist** and updated docs in **gh-pages/*.html**.
+You will get distributive in **lib/dist** and updated docs in **gh-pages/index.html** and **gh-pages/docs.html** (don't edit these files directly).
 
 6.Commit changes on <code>dev</code> branch and make pull request as usual.  
 

From d3ccad1c2b86f7925ad4583e0e4e660a9511e09c Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 15:38:12 +0400
Subject: [PATCH 21/24] readme updated

---
 README.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index ed6d827..ca3354d 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,8 @@ 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  
-You will get distributive in **lib/dist** and updated docs in **gh-pages/index.html** and **gh-pages/docs.html** (don't edit these files directly).
+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.  
 

From 30f7c203604cdbc6ace450eab7de7e3f00b8d962 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 15:40:36 +0400
Subject: [PATCH 22/24] set version 1.1.0

---
 CHANGELOG.txt | 7 ++++---
 package.json  | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index dd7da67..8257188 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,13 +1,14 @@
 X-editable changelog
 =============================
        
-Version 1.1.0 wip
+       
+Version 1.1.0 Nov 27, 2012
 ----------------------------   
-[enh #11] cancel button changed to 'cross' (tarciozemel)  
+[enh #11] icon cancel changed to 'cross' (tarciozemel)  
 [enh] added support for IE7+ (vitalets)  
 [enh #9] 'name' or 'id' is not required anymore (vitalets)      
 [enh] 'clear' button added in date and dateui (vitalets)      
-[enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform buttons (vitalets)      
+[enh] form template changed: added DIV.editable-input, DIV.editable.buttons and $.fn.editableform.buttons (vitalets)      
 [enh] new input type: checklist (vitalets)      
 [enh] updated docs: inputs dropdown menu, global templates section (vitalets)      
                             
diff --git a/package.json b/package.json
index 40065b1..f7cf0e1 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.0.1",
+  "version": "1.1.0",
   "homepage": "http://github.com/vitalets/x-editable",
   "author": {
     "name": "Vitaliy Potapov",

From 4e616680fd1db77762ec438113f97abb10264fc0 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 15:46:17 +0400
Subject: [PATCH 23/24] fix indent in examples

---
 src/editable-form/editable-form.js | 34 +++++++++++++++---------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index ed0ac53..b6f8018 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -198,7 +198,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
 
                 @example
                 $('#form-div').on('save'), function(e, params){
-                if(params.newValue === 'username') {...}
+                    if(params.newValue === 'username') {...}
                 });                    
                 **/                
                 this.$element.triggerHandler('save', {newValue: newValue, response: response});
@@ -278,10 +278,10 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
     @params {Object} options
     @example
     var $form = $('&lt;div&gt;').editableform({
-    type: 'text',
-    name: 'username',
-    url: '/post',
-    value: 'vitaliy'
+        type: 'text',
+        name: 'username',
+        url: '/post',
+        value: 'vitaliy'
     });
 
     //to display form you should call 'render' method
@@ -327,20 +327,20 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @default null
         @example
         url: function(params) {
-        if(params.value === 'abc') {
-        var d = new $.Deferred;
-        return d.reject('field cannot be "abc"'); //returning error via deferred object
-        } else {
-        someModel.set(params.name, params.value); //save data in some js model
-        }
-        }        
+            if(params.value === 'abc') {
+                var d = new $.Deferred;
+                return d.reject('field cannot be "abc"'); //returning error via deferred object
+            } else {
+                someModel.set(params.name, params.value); //save data in some js model
+            }
+        } 
         **/        
         url:null,
         /**
         Additional params for submit. Function can be used to calculate params dynamically
         @example
         params: function() {
-        return { a: 1 };
+            return { a: 1 };
         }
 
         @property params 
@@ -391,9 +391,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @default null
         @example
         validate: function(value) {
-        if($.trim(value) == '') {
-        return 'This field is required';
-        }
+            if($.trim(value) == '') {
+                return 'This field is required';
+            }
         }
         **/         
         validate: null,
@@ -406,7 +406,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
         @default null
         @example
         success: function(response, newValue) {
-        if(!response.success) return response.msg;
+            if(!response.success) return response.msg;
         }
         **/          
         success: function(response, newValue) {}         

From 54919a21775d6d6ef6dda647c0bd35c18f12cd83 Mon Sep 17 00:00:00 2001
From: vitalets <noginsk@rambler.ru>
Date: Tue, 27 Nov 2012 15:51:31 +0400
Subject: [PATCH 24/24] list comments tiny fix

---
 src/inputs/checklist.js | 2 +-
 src/inputs/list.js      | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/inputs/checklist.js b/src/inputs/checklist.js
index ffdc7c9..1a5b149 100644
--- a/src/inputs/checklist.js
+++ b/src/inputs/checklist.js
@@ -143,7 +143,7 @@ $(function(){
         limit: 4,
         /**
         Text shown when count of checked items is greater than <code>limit</code> parameter.
-        You can use <code>{checked}</code> and <code>count</code> placeholders.
+        You can use <code>{checked}</code> and <code>{count}</code> placeholders.
 
         @property limitText 
         @type string
diff --git a/src/inputs/list.js b/src/inputs/list.js
index fc8412c..ad53e68 100644
--- a/src/inputs/list.js
+++ b/src/inputs/list.js
@@ -222,7 +222,7 @@ List - abstract class for inputs that have source option loaded from js array or
 
     List.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
         /**
-        Source data for dropdown list. If string - considered ajax url to load items. Otherwise should be an array.
+        Source data for list. If string - considered ajax url to load items. Otherwise should be an array.
         Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br>
         For compability it also supports format <code>{value1: "text1", value2: "text2" ...}</code> but it does not guarantee elements order.