diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 6498081..131c27b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -2,6 +2,34 @@ X-editable changelog
=============================
+Version 1.2.0 Dec 6, 2012
+----------------------------
+[enh #36] 'submit' method: added 'ajaxOptions' property to modify ajax request (vitalets)
+[enh] inputs now internally use 'value2submit' method instead of previous 'value2str' (vitalets)
+[enh] editableContainer removed from docs (vitalets)
+[enh] editableContainer: removed 'autohide' option and 'cancel' event. Use 'hidden' event instead (vitalets)
+[enh] 'hidden' event: added param 'reason' that points to reason caused hiding (vitalets)
+[enh] 'select' submit by enter (vitalets)
+[bug #37] fix incorrectly shown datepicker in jquery 1.7.1 + webkit (vitalets)
+[enh] added url param 'jquery' to run tests in different versions of jquery, e.g. '&jquery=1.7.2' (vitalets)
+[enh] 'enablefocus' option removed. More efficient to use 'save/hide' events to set focus to any element (vitalets)
+[enh] 'init' event was added due to removal of render event (vitalets)
+[enh] 'render' event was removed, use 'display' callback instead (vitalets)
+[enh] 'checklist' submit value as array, not comma separated string (vitalets)
+[enh] 'checklist' was refactored: options 'viewseparator', 'limit', 'limitText' are supressed by 'display' callback (vitalets)
+[enh] new option: 'display' callback. Makes far more flexible rendering value into element's text. (vitalets)
+[bug] fix typos (atrophic)
+[enh] all callbacks scope changed to element (vitalets)
+[enh] new option: 'savenochange' to save or cancel value when it was not changed in form (vitalets)
+[enh] composite pk can be defined as JSON in data-pk attribute (vitalets)
+[enh #30] new option 'sourceCache' true|false to disable cache for select (vitalets)
+[bug #34] inputclass span* broken with fluid bootstrap layout. Classes changed to 'input-*'. (vitalets)
+[enh] utils now added to $.fn.editableutils instead of $.fn.editableform.utils (vitalets)
+[enh] input types now added to $.fn.editabletypes instead of $.fn.editableform.types (vitalets)
+[enh] playground and tests now use requirejs (vitalets)
+[bug #27] 'today' button toggle bug in bootstrap-datepicker (vitalets)
+
+
Version 1.1.1 Nov 30, 2012
----------------------------
[enh] 'showbuttons' option to hide buttons in form (vitalets)
@@ -29,7 +57,7 @@ Version 1.0.1 Nov 22, 2012
[enh] contribution guide in README.md (vitalets)
[enh #7] 'shown', 'hidden' events added (vitalets)
[enh #1] params can be a function to calculate it dynamically (vitalets)
-[enh #6] do not preventDetault() in click when toggle='manual'. This allows to have clickable links (vitalets)
+[enh #6] do not preventDefault() in click when toggle='manual'. This allows to have clickable links (vitalets)
[bug #3] should not mark element with unsave css if url is user's function (vitalets)
@@ -48,13 +76,13 @@ Here list of differences to help you to upgrade your application:
[change] 'toggle' option value can be only click|manual (not toggling element id). In case of 'manual' you should write handler calling 'show' method.
[change] 'validate' option cannot be defined as object anymore.
[change] events 'init', 'update', 'shown', 'hidden' removed. Events 'save', 'cancel' added. Event 'render' remains.
-[change] input's optiom 'template' renamed to 'tpl' (to exclude conflict with container's template).
-[change] value can be stored internaly as object (previously was always string). Usefull for date inupt.
+[change] input's option 'template' renamed to 'tpl' (to exclude conflict with container's template).
+[change] value can be stored internally as object (previously was always string). Useful for date input.
[change] 'error' callback option is removed. 'success' callback remained.
-[enh] 'source' option in select can be array of structure [{value: 1, text: 'abc'}, {...}]. This allows to keep ordering of items in dropdown list. Previous format is supported for compability.
+[enh] 'source' option in select can be array of structure [{value: 1, text: 'abc'}, {...}]. This allows to keep ordering of items in dropdown list. Previous format is supported for compatibility.
[enh] api method 'setValue' to set manually value of editable.
[change] locales directory is excluded from bootstrap-datepicker input. If you need localization you should jus download corresponding file from github.
[change] date and dateui specific options can be set only via 'datepicker' option in first level of config (previously it was possible to set some options directly in config, e.g. weekStart).
-[change] if 'url' option defined as function - it is used as submit method instead of ajax (previously it was dynamically return url string and ajax occured anyway)
+[change] if 'url' option defined as function - it is used as submit method instead of ajax (previously it was dynamically return url string and ajax occurred anyway)
-Also all known bugs of bootstrap-editable were closed.
\ No newline at end of file
+Also all known bugs of bootstrap-editable were closed.
diff --git a/README.md b/README.md
index aa774c1..1f3320b 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ It is a new life of [bootstrap-editable plugin](http://github.com/vitalets/boots
See **http://vitalets.github.com/x-editable**
## Reporting issues
-When creating issues please provide jsFiddle example. You can just fork [this fiddle](http://jsfiddle.net/xBB5x/5/) as starting point.
+When creating issues please provide jsFiddle example. You can easily fork one of templates: [bootstrap](http://jsfiddle.net/xBB5x/25/), [jqueryui](http://jsfiddle.net/xBB5x/24/), [plain](http://jsfiddle.net/xBB5x/23/).
Your feedback is very appreciated!
## Contribution
@@ -19,7 +19,7 @@ A few steps how to start contributing:
**x-editable**
| -- **lib** (repo related to dev and master branches)
| -- **gh-pages** (repo related to gh-pages branch for docs & demo)
- | -- **playground** (simple node-server and html page for testing, [playground.zip](https://github.com/downloads/vitalets/x-editable/playground.zip))
+ | -- **playground** (simple node-server and html page for testing, [playground_1.2.zip](https://github.com/downloads/vitalets/x-editable/playground_1.2.zip), **updated in 1.2.0!**)
To make it easy follow this script ( _assuming you have [nodejs](http://nodejs.org) installed_ ).
Please replace <your-github-name> with your name:
@@ -43,7 +43,7 @@ npm install
cd ..
#playground
-#download playground.zip from https://github.com/downloads/vitalets/x-editable/playground.zip
+#download playground.zip from https://github.com/downloads/vitalets/x-editable/playground_1.2.zip
unzip playground.zip
cd playground
npm install
diff --git a/grunt.js b/grunt.js
index 4282da2..e7427c9 100644
--- a/grunt.js
+++ b/grunt.js
@@ -100,10 +100,19 @@ module.exports = function(grunt) {
//module for testing
var module = '';
-//module = '&module=textarea';
+// module = '&module=textarea';
//module = '&module=select';
//module = '&module=text';
+ var qunit_testover = [];
+ ['bootstrap', 'jqueryui', 'plain'].forEach(function(f){
+ ['popup', 'inline'].forEach(function(c){
+ ['1.6.4', '1.7.1', '1.7.2', '1.8.2', '1.8.3'].forEach(function(jqver) {
+ qunit_testover.push('http://localhost:8000/test/index.html?f='+f+'&c='+c+'&jquery='+jqver+module);
+ });
+ });
+ });
+
//get js and css for different builds
var files = getFiles();
@@ -133,8 +142,9 @@ module.exports = function(grunt) {
plain: [
'http://localhost:8000/test/index.html?f=plain&c=popup'+module,
'http://localhost:8000/test/index.html?f=plain&c=inline'+module
- ]
-// files: ['test/index.html']
+ ],
+ //test all builds under several versions of jquery
+ testover: qunit_testover
},
server: {
port: 8000,
@@ -208,38 +218,14 @@ module.exports = function(grunt) {
}
}
},
-
- //compress does not work properly for MAC OS (see https://github.com/vitalets/bootstrap-editable/issues/19)
- //zip will be created manually
- /*
- compress: {
- zip: {
- options: {
- mode: "zip",
- //TODO: unfortunatly here <%= dist_source %> and does not work
- basePath: "dist"
- },
- files: {
- "<%= dist %>/bootstrap-editable-v<%= pkg.version %>.zip": ["<%= dist_source %>/ **", "<%= dist %>/libs/ **"]
- }
- },
- tgz: {
- options: {
- mode: "tgz",
- basePath: "dist"
- },
- files: {
- "<%= dist %>/bootstrap-editable-v<%= pkg.version %>.tar.gz": ["<%= dist_source %>/ **", "<%= dist %>/libs/ **"]
- }
- }
- },
- */
+
uglify: {}
});
//test task
grunt.registerTask('test', 'lint server qunit:bootstrap');
- grunt.registerTask('testall', 'lint server qunit');
+ grunt.registerTask('testall', 'lint server qunit:bootstrap qunit:jqueryui qunit:plain');
+ grunt.registerTask('testover', 'lint server qunit:testover');
// Default task.
// grunt.registerTask('default', 'lint qunit');
diff --git a/package.json b/package.json
index e0700a2..5f92913 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "X-editable",
"title": "X-editable",
"description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery",
- "version": "1.1.1",
+ "version": "1.2.0",
"homepage": "http://github.com/vitalets/x-editable",
"author": {
"name": "Vitaliy Potapov",
diff --git a/src/containers/editable-container.css b/src/containers/editable-container.css
index 1a91e98..bebf690 100644
--- a/src/containers/editable-container.css
+++ b/src/containers/editable-container.css
@@ -1,5 +1,5 @@
.editable-container {
- max-width: none; /* without this rule poshytip does not stretch */
+ max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
}
.editable-container.popover {
@@ -10,4 +10,8 @@
.editable-container.editable-inline {
display: inline;
vertical-align: middle;
+}
+
+.editable-container.ui-widget {
+ font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */
}
\ No newline at end of file
diff --git a/src/containers/editable-container.js b/src/containers/editable-container.js
index 0029eba..1094c46 100644
--- a/src/containers/editable-container.js
+++ b/src/containers/editable-container.js
@@ -20,7 +20,7 @@ Applied as jQuery method.
init: function(element, options) {
this.$element = $(element);
//todo: what is in priority: data or js?
- this.options = $.extend({}, $.fn.editableContainer.defaults, $.fn.editableform.utils.getConfigData(this.$element), options);
+ this.options = $.extend({}, $.fn.editableContainer.defaults, $.fn.editableutils.getConfigData(this.$element), options);
this.splitOptions();
this.initContainer();
@@ -75,12 +75,15 @@ Applied as jQuery method.
},
initForm: function() {
+ this.formOptions.scope = this.$element[0]; //set scope of form callbacks to element
this.$form = $('
')
.editableform(this.formOptions)
.on({
save: $.proxy(this.save, this),
- cancel: $.proxy(this.cancel, this),
- show: $.proxy(this.setPosition, this), //re-position container every time form is shown (after loading state)
+ cancel: $.proxy(function(){
+ this.hide('cancel');
+ }, this),
+ show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
rendered: $.proxy(function(){
/**
@@ -119,7 +122,7 @@ Applied as jQuery method.
/**
Shows container with form
@method show()
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
show: function (closeAll) {
this.$element.addClass('editable-open');
@@ -143,8 +146,9 @@ Applied as jQuery method.
/**
Hides container with form
@method hide()
+ @param {string} reason Reason caused hiding. Can be save|cancel|onblur|undefined (=manual)
**/
- hide: function() {
+ hide: function(reason) {
if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
return;
}
@@ -154,9 +158,17 @@ Applied as jQuery method.
Fired when container was hidden. It occurs on both save or cancel.
@event hidden
- @param {Object} event event object
+ @param {object} event event object
+ @param {string} reason Reason caused hiding. Can be save|cancel|onblur|undefined (=manual)
+ @example
+ $('#username').on('hidden', function(e, reason) {
+ if(reason === 'save' || reason === 'cancel') {
+ //auto-open next editable
+ $(this).closest('tr').next().find('.editable').editable('show');
+ }
+ });
**/
- this.$element.triggerHandler('hidden');
+ this.$element.triggerHandler('hidden', reason);
},
/* internal hide method. To be overwritten in child classes */
@@ -167,7 +179,7 @@ Applied as jQuery method.
/**
Toggles container visibility (show / hide)
@method toggle()
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
toggle: function(closeAll) {
if(this.tip && this.tip().is(':visible')) {
@@ -185,23 +197,8 @@ Applied as jQuery method.
//tbd in child class
},
- cancel: function() {
- if(this.options.autohide) {
- this.hide();
- }
- /**
- Fired when form was cancelled by user
-
- @event cancel
- @param {Object} event event object
- **/
- this.$element.triggerHandler('cancel');
- },
-
save: function(e, params) {
- if(this.options.autohide) {
- this.hide();
- }
+ this.hide('save');
/**
Fired when new value was submitted. You can use $(this).data('editableContainer') inside handler to access to editableContainer instance
@@ -262,8 +259,8 @@ Applied as jQuery method.
*/
closeOthers: function(element) {
$('.editable-open').each(function(i, el){
- //do nothing with passed element
- if(el === element) {
+ //do nothing with passed element and it's children
+ if(el === element || $(el).find(element).length) {
return;
}
@@ -276,7 +273,7 @@ Applied as jQuery method.
}
if(ec.options.onblur === 'cancel') {
- $el.data('editableContainer').hide();
+ $el.data('editableContainer').hide('onblur');
} else if(ec.options.onblur === 'submit') {
$el.data('editableContainer').tip().find('form').submit();
}
@@ -350,7 +347,7 @@ Applied as jQuery method.
**/
placement: 'top',
/**
- Wether to hide container on save/cancel.
+ Whether to hide container on save/cancel.
@property autohide
@type boolean
@@ -365,6 +362,7 @@ Applied as jQuery method.
@property onblur
@type string
@default 'cancel'
+ @since 1.1.1
**/
onblur: 'cancel'
};
diff --git a/src/containers/editable-inline.js b/src/containers/editable-inline.js
index df585a6..05800dc 100644
--- a/src/containers/editable-inline.js
+++ b/src/containers/editable-inline.js
@@ -43,10 +43,6 @@
innerHide: function () {
this.$form.hide(this.options.anim, $.proxy(function() {
this.$element.show();
- //return focus on element
- if (this.options.enablefocus) {
- this.$element.focus();
- }
}, this));
},
@@ -57,8 +53,7 @@
//defaults
$.fn.editableContainer.defaults = $.extend({}, $.fn.editableContainer.defaults, {
- anim: 'fast',
- enablefocus: false
+ anim: 'fast'
});
diff --git a/src/containers/editable-popover.js b/src/containers/editable-popover.js
index e369b6f..863148b 100644
--- a/src/containers/editable-popover.js
+++ b/src/containers/editable-popover.js
@@ -14,7 +14,7 @@
$.extend(this.containerOptions, {
trigger: 'manual',
selector: false,
- content: 'dfgh'
+ content: ' '
});
this.call(this.containerOptions);
},
diff --git a/src/editable-form/editable-form-utils.js b/src/editable-form/editable-form-utils.js
index 4d8169d..aa22f64 100644
--- a/src/editable-form/editable-form-utils.js
+++ b/src/editable-form/editable-form-utils.js
@@ -2,10 +2,11 @@
* EditableForm utilites
*/
(function ($) {
- $.fn.editableform.utils = {
+ //utils
+ $.fn.editableutils = {
/**
* classic JS inheritance function
- */
+ */
inherit: function (Child, Parent) {
var F = function() { };
F.prototype = Parent.prototype;
@@ -116,6 +117,13 @@
return k;
}
- }
+ },
+
+ /**
+ method to escape html.
+ **/
+ escape: function(str) {
+ return $('
').text(str).html();
+ }
};
}(window.jQuery));
\ No newline at end of file
diff --git a/src/editable-form/editable-form.css b/src/editable-form/editable-form.css
index 874705a..c7bc226 100644
--- a/src/editable-form/editable-form.css
+++ b/src/editable-form/editable-form.css
@@ -16,6 +16,8 @@
*display: inline;
}
+
+
.editable-input {
vertical-align: top;
display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
@@ -31,8 +33,9 @@
}
/*for jquery-ui buttons need set height to look more pretty*/
-.editable-buttons button.ui-button {
+.editable-buttons button.ui-button-icon-only {
height: 24px;
+ width: 30px;
}
.editableform-loading {
@@ -79,6 +82,10 @@
margin: 0;
}
+.editable-checklist label {
+ white-space: nowrap;
+}
+
.editable-clear {
clear: both;
font-size: 0.9em;
diff --git a/src/editable-form/editable-form.js b/src/editable-form/editable-form.js
index a2db423..f85e170 100644
--- a/src/editable-form/editable-form.js
+++ b/src/editable-form/editable-form.js
@@ -1,7 +1,7 @@
/**
Form with single input element, two buttons and two states: normal/loading.
-Applied as jQuery method to DIV tag (not to form tag!)
-Editableform is linked with one of input types, e.g. 'text' or 'select'.
+Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown.
+Editableform is linked with one of input types, e.g. 'text', 'select' etc.
@class editableform
@uses text
@@ -9,9 +9,12 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
**/
(function ($) {
- var EditableForm = function (element, options) {
+ var EditableForm = function (div, options) {
this.options = $.extend({}, $.fn.editableform.defaults, options);
- this.$element = $(element); //div, containing form. Not form tag! Not editable-element.
+ this.$div = $(div); //div, containing form. Not form tag! Not editable-element.
+ if(!this.options.scope) {
+ this.options.scope = this;
+ }
this.initInput();
};
@@ -21,9 +24,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
var TypeConstructor, typeOptions;
//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, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults));
+ if(typeof $.fn.editabletypes[this.options.type] === 'function') {
+ TypeConstructor = $.fn.editabletypes[this.options.type];
+ typeOptions = $.fn.editableutils.sliceObj(this.options, $.fn.editableutils.objectKeys(TypeConstructor.defaults));
this.input = new TypeConstructor(typeOptions);
} else {
$.error('Unknown type: '+ this.options.type);
@@ -45,7 +48,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
**/
render: function() {
this.$loading = $($.fn.editableform.loading);
- this.$element.empty().append(this.$loading);
+ this.$div.empty().append(this.$loading);
this.showLoading();
//init form template and buttons
@@ -61,7 +64,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
@event rendering
@param {Object} event event object
**/
- this.$element.triggerHandler('rendering');
+ this.$div.triggerHandler('rendering');
//render input
$.when(this.input.render())
@@ -80,21 +83,23 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
}
//append form to container
- this.$element.append(this.$form);
-
+ this.$div.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('.editable-submit').attr('disabled', true);
this.input.$input.attr('disabled', true);
+ //prevent form from submitting
+ this.$form.submit(function(e){ e.preventDefault(); });
} else {
this.error(false);
this.input.$input.removeAttr('disabled');
this.$form.find('.editable-submit').removeAttr('disabled');
this.input.value2input(this.value);
+ //attach submit handler
this.$form.submit($.proxy(this.submit, this));
}
@@ -103,7 +108,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
@event rendered
@param {Object} event event object
**/
- this.$element.triggerHandler('rendered');
+ this.$div.triggerHandler('rendered');
this.showForm();
}, this));
@@ -114,7 +119,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
@event cancel
@param {Object} event event object
**/
- this.$element.triggerHandler('cancel');
+ this.$div.triggerHandler('cancel');
},
showLoading: function() {
var w;
@@ -133,16 +138,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
this.$loading.show();
},
- showForm: function() {
+ showForm: function(activate) {
this.$loading.hide();
this.$form.show();
- this.input.activate();
+ if(activate !== false) {
+ this.input.activate();
+ }
/**
Fired when form is shown
@event show
@param {Object} event event object
**/
- this.$element.triggerHandler('show');
+ this.$div.triggerHandler('show');
},
error: function(msg) {
@@ -163,8 +170,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
e.preventDefault();
var error,
- newValue = this.input.input2value(), //get new value from input
- newValueStr;
+ newValue = this.input.input2value(); //get new value from input
//validation
if (error = this.validate(newValue)) {
@@ -173,25 +179,29 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
return;
}
- //value as string
- newValueStr = this.input.value2str(newValue);
-
//if value not changed --> cancel
/*jslint eqeq: true*/
- if (newValueStr == this.input.value2str(this.value)) {
+ if (!this.options.savenochange && this.input.value2str(newValue) == this.input.value2str(this.value)) {
/*jslint eqeq: false*/
this.cancel();
return;
}
//sending data to server
- $.when(this.save(newValueStr))
+ $.when(this.save(newValue))
.done($.proxy(function(response) {
//run success callback
- var res = typeof this.options.success === 'function' ? this.options.success.call(this, response, newValue) : null;
+ var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
- //if success callback returns string --> show error
- if(res && typeof res === 'string') {
+ //if success callback returns false --> keep form open and do not activate input
+ if(res === false) {
+ this.error(false);
+ this.showForm(false);
+ return;
+ }
+
+ //if success callback returns string --> keep form open, show error and activate input
+ if(typeof res === 'string') {
this.error(res);
this.showForm();
return;
@@ -218,7 +228,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
if(params.newValue === 'username') {...}
});
**/
- this.$element.triggerHandler('save', {newValue: newValue, response: response});
+ this.$div.triggerHandler('save', {newValue: newValue, response: response});
}, this))
.fail($.proxy(function(xhr) {
this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!');
@@ -226,10 +236,16 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
}, this));
},
- save: function(value) {
- var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this) : this.options.pk,
+ save: function(newValue) {
+ //convert value for submitting to server
+ var submitValue = this.input.value2submit(newValue);
+
+ //try parse composite pk defined as json string in data-pk
+ this.options.pk = $.fn.editableutils.tryParseJson(this.options.pk, true);
+
+ var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this.options.scope) : this.options.pk,
send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))),
- params, ajaxOptions;
+ params;
if (send) { //send to server
this.showLoading();
@@ -237,30 +253,29 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
//standard params
params = {
name: this.options.name || '',
- value: value,
+ value: submitValue,
pk: pk
};
//additional params
if(typeof this.options.params === 'function') {
- $.extend(params, this.options.params.call(this, params));
+ $.extend(params, this.options.params.call(this.options.scope, params));
} else {
//try parse json in single quotes (from data-params attribute)
- this.options.params = $.fn.editableform.utils.tryParseJson(this.options.params, true);
+ this.options.params = $.fn.editableutils.tryParseJson(this.options.params, true);
$.extend(params, this.options.params);
}
if(typeof this.options.url === 'function') { //user's function
- return this.options.url.call(this, params);
- } else { //send ajax to server and return deferred object
- ajaxOptions = $.extend({
+ return this.options.url.call(this.options.scope, params);
+ } else {
+ //send ajax to server and return deferred object
+ return $.ajax($.extend({
url : this.options.url,
data : params,
type : 'post',
dataType: 'json'
- }, this.options.ajaxOptions);
-
- return $.ajax(ajaxOptions);
+ }, this.options.ajaxOptions));
}
}
},
@@ -270,7 +285,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
value = this.value;
}
if (typeof this.options.validate === 'function') {
- return this.options.validate.call(this, value);
+ return this.options.validate.call(this.options.scope, value);
}
},
@@ -377,7 +392,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
name: null,
/**
Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. {id: 1, lang: 'en'}.
- Can be calculated dinamically via function.
+ Can be calculated dynamically via function.
@property pk
@type string|object|function
@@ -418,7 +433,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
validate: null,
/**
Success callback. Called when value successfully sent on server and **response status = 200**.
- Usefull to work with json response. For example, if your backend response can be {success: true}
+ Useful to work with json response. For example, if your backend response can be {success: true}
or {success: false, msg: "server error"} you can check it inside this callback.
If it returns **string** - means error occured and string is shown as error message.
If it returns **object like** {newValue: <something>} - it overwrites value, submitted by user.
@@ -440,28 +455,44 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
@property ajaxOptions
@type object
@default null
+ @since 1.1.1
**/
ajaxOptions: null,
/**
- Wether to show buttons or not.
+ Whether to show buttons or not.
Form without buttons can be auto-submitted by input or by onblur = 'submit'.
+ @example
+ ajaxOptions: {
+ method: 'PUT',
+ dataType: 'xml'
+ }
@property showbuttons
@type boolean
@default true
+ @since 1.1.1
**/
- showbuttons: true
-
- /*todo:
- Submit strategy. Can be normal|never
- submitmode='never' usefull for turning into classic form several inputs and submitting them together manually.
- Works pretty with showbuttons=false
+ showbuttons: true,
+ /**
+ Scope for callback methods (success, validate).
+ If null means editableform instance itself.
- @property submitmode
- @type string
- @default normal
- */
-// submitmode: 'normal'
+ @property scope
+ @type DOMElement|object
+ @default null
+ @since 1.2.0
+ @private
+ **/
+ scope: null,
+ /**
+ Whether to save or cancel value when it was not changed but form was submitted
+
+ @property savenochange
+ @type boolean
+ @default false
+ @since 1.2.0
+ **/
+ savenochange: false
};
/*
@@ -482,15 +513,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
$.fn.editableform.buttons = ''+
'';
- //error class attahced to control-group
+ //error class attached to control-group
$.fn.editableform.errorGroupClass = null;
- //error class attahced to editable-error-block
+ //error class attached 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 bd23548..1a20c7b 100644
--- a/src/element/editable-element.js
+++ b/src/element/editable-element.js
@@ -8,7 +8,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
var Editable = function (element, options) {
this.$element = $(element);
- this.options = $.extend({}, $.fn.editable.defaults, $.fn.editableform.utils.getConfigData(this.$element), options);
+ this.options = $.extend({}, $.fn.editable.defaults, $.fn.editableutils.getConfigData(this.$element), options);
this.init();
};
@@ -20,9 +20,6 @@ Makes editable any HTML element on the page. Applied as jQuery method.
doAutotext,
finalize;
- //initialization flag
- this.isInit = true;
-
//editableContainer must be defined
if(!$.fn.editableContainer) {
$.error('You must define $.fn.editableContainer via including corresponding file (e.g. editable-popover.js)');
@@ -33,9 +30,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
this.options.name = this.options.name || this.$element.attr('id');
//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, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults));
+ if(typeof $.fn.editabletypes[this.options.type] === 'function') {
+ TypeConstructor = $.fn.editabletypes[this.options.type];
+ this.typeOptions = $.fn.editableutils.sliceObj(this.options, $.fn.editableutils.objectKeys(TypeConstructor.defaults));
this.input = new TypeConstructor(this.typeOptions);
} else {
$.error('Unknown type: '+ this.options.type);
@@ -47,13 +44,20 @@ Makes editable any HTML element on the page. Applied as jQuery method.
this.value = this.input.html2value($.trim(this.$element.html()));
isValueByText = true;
} else {
+ /*
+ value can be string when received from 'data-value' attribute
+ for complext objects value can be set as json string in data-value attribute,
+ e.g. data-value="{city: 'Moscow', street: 'Lenina'}"
+ */
+ this.options.value = $.fn.editableutils.tryParseJson(this.options.value, true);
if(typeof this.options.value === 'string') {
- this.options.value = $.trim(this.options.value);
+ this.value = this.input.str2value(this.options.value);
+ } else {
+ this.value = this.options.value;
}
- this.value = this.input.str2value(this.options.value);
}
- //add 'editable' class
+ //add 'editable' class to every editable element
this.$element.addClass('editable');
//attach handler activating editable. In disabled mode it just prevent default action (useful for links)
@@ -81,29 +85,46 @@ Makes editable any HTML element on the page. Applied as jQuery method.
//if value was generated by text or value is empty, no sense to run autotext
doAutotext = !isValueByText && this.value !== null && this.value !== undefined;
doAutotext &= (this.options.autotext === 'always') || (this.options.autotext === 'auto' && !this.$element.text().length);
- $.when(doAutotext ? this.input.value2html(this.value, this.$element) : true).then($.proxy(function() {
+ $.when(doAutotext ? this.render() : true).then($.proxy(function() {
if(this.options.disabled) {
this.disable();
} else {
this.enable();
}
/**
- Fired each time when element's text is rendered. Occurs on initialization and on each update of value.
- Can be used for display customization.
+ Fired when element was initialized by editable method.
- @event render
+ @event init
@param {Object} event event object
@param {Object} editable editable instance
- @example
- $('#action').on('render', function(e, editable) {
- var colors = {0: "gray", 1: "green", 2: "blue", 3: "red"};
- $(this).css("color", colors[editable.value]);
- });
+ @since 1.2.0
**/
- this.$element.triggerHandler('render', this);
- this.isInit = false;
+ this.$element.triggerHandler('init', this);
}, this));
},
+
+ /*
+ Renders value into element's text.
+ Can call custom display method from options.
+ Can return deferred object.
+ @method render()
+ */
+ render: function() {
+ //do not display anything
+ if(this.options.display === false) {
+ return;
+ }
+ //if it is input with source, we pass callback in third param to be called when source is loaded
+ if(this.input.options.hasOwnProperty('source')) {
+ return this.input.value2html(this.value, this.$element[0], this.options.display);
+ //if display method defined --> use it
+ } else if(typeof this.options.display === 'function') {
+ return this.options.display.call(this.$element[0], this.value);
+ //else use input's original value2html() method
+ } else {
+ return this.input.value2html(this.value, this.$element[0]);
+ }
+ },
/**
Enables editable
@@ -191,6 +212,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
* set emptytext if element is empty (reverse: remove emptytext if needed)
*/
handleEmpty: function () {
+ //do not handle empty if we do not display anything
+ if(this.options.display === false) {
+ return;
+ }
+
var emptyClass = 'editable-empty';
//emptytext shown only for enabled
if(!this.options.disabled) {
@@ -211,7 +237,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
/**
Shows container with form
@method show()
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
show: function (closeAll) {
if(this.options.disabled) {
@@ -221,13 +247,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
//init editableContainer: popover, tooltip, inline, etc..
if(!this.container) {
var containerOptions = $.extend({}, this.options, {
- value: this.value,
- autohide: false //element will take care to show/hide container
+ value: this.value
});
this.$element.editableContainer(containerOptions);
this.$element.on({
- save: $.proxy(this.save, this),
- cancel: $.proxy(this.hide, this)
+ save: $.proxy(this.save, this)
});
this.container = this.$element.data('editableContainer');
} else if(this.container.tip().is(':visible')) {
@@ -246,17 +270,12 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.container) {
this.container.hide();
}
-
- //return focus on element
- if (this.options.enablefocus && this.options.toggle === 'click') {
- this.$element.focus();
- }
},
/**
Toggles container visibility (show / hide)
@method toggle()
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
**/
toggle: function(closeAll) {
if(this.container && this.container.tip().is(':visible')) {
@@ -271,13 +290,13 @@ Makes editable any HTML element on the page. Applied as jQuery method.
*/
save: function(e, params) {
//if url is not user's function and value was not sent to server and value changed --> mark element with unsaved css.
- if(typeof this.options.url !== 'function' && params.response === undefined && this.input.value2str(this.value) !== this.input.value2str(params.newValue)) {
+ if(typeof this.options.url !== 'function' && this.options.display !== false && params.response === undefined && this.input.value2str(this.value) !== this.input.value2str(params.newValue)) {
this.$element.addClass('editable-unsaved');
} else {
this.$element.removeClass('editable-unsaved');
}
- this.hide();
+ // this.hide();
this.setValue(params.newValue);
/**
@@ -312,7 +331,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
Sets new value of editable
@method setValue(value, convertStr)
@param {mixed} value new value
- @param {boolean} convertStr wether to convert value from string to internal format
+ @param {boolean} convertStr whether to convert value from string to internal format
**/
setValue: function(value, convertStr) {
if(convertStr) {
@@ -323,10 +342,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.container) {
this.container.option('value', this.value);
}
- $.when(this.input.value2html(this.value, this.$element))
+ $.when(this.render())
.then($.proxy(function() {
this.handleEmpty();
- this.$element.triggerHandler('render', this);
}, this));
},
@@ -338,7 +356,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.container) {
this.container.activate();
}
- }
+ }
};
/* EDITABLE PLUGIN DEFINITION
@@ -369,7 +387,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
$('#username, #fullname').editable('validate');
// possible result:
{
- username: "username is requied",
+ username: "username is required",
fullname: "fullname should be minimum 3 letters length"
}
**/
@@ -398,7 +416,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
this.each(function () {
var $this = $(this), data = $this.data(datakey);
if (data && data.value !== undefined && data.value !== null) {
- result[data.options.name] = data.input.value2str(data.value);
+ result[data.options.name] = data.input.value2submit(data.value);
}
});
return result;
@@ -411,6 +429,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
@param {object} options
@param {object} options.url url to submit data
@param {object} options.data additional data to submit
+ @param {object} options.ajaxOptions additional ajax options
@param {function} options.error(obj) error handler (called on both client-side and server-side validation errors)
@param {function} options.success(obj) success handler
@returns {Object} jQuery object
@@ -421,21 +440,20 @@ Makes editable any HTML element on the page. Applied as jQuery method.
errors = this.editable('validate'),
values;
- if(typeof config.error !== 'function') {
- config.error = function() {};
- }
-
if($.isEmptyObject(errors)) {
values = this.editable('getValue');
if(config.data) {
$.extend(values, config.data);
- }
- $.ajax({
- type: 'POST',
+ }
+
+ $.ajax($.extend({
url: config.url,
data: values,
+ type: 'POST',
dataType: 'json'
- }).success(function(response) {
+ }, config.ajaxOptions))
+ .success(function(response) {
+ //successful response
if(typeof response === 'object' && response.id) {
$elems.editable('option', 'pk', response.id);
$elems.removeClass('editable-unsaved');
@@ -443,13 +461,20 @@ Makes editable any HTML element on the page. Applied as jQuery method.
config.success.apply($elems, arguments);
}
} else { //server-side validation error
+ if(typeof config.error === 'function') {
+ config.error.apply($elems, arguments);
+ }
+ }
+ })
+ .error(function(){ //ajax error
+ if(typeof config.error === 'function') {
config.error.apply($elems, arguments);
}
- }).error(function(){ //ajax error
- config.error.apply($elems, arguments);
});
} else { //client-side validation error
- config.error.call($elems, {errors: errors});
+ if(typeof config.error === 'function') {
+ config.error.call($elems, {errors: errors});
+ }
}
return this;
}
@@ -505,7 +530,6 @@ Makes editable any HTML element on the page. Applied as jQuery method.
@default 'click'
**/
toggle: 'click',
-
/**
Text shown when element is empty.
@@ -515,7 +539,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
**/
emptytext: 'Empty',
/**
- Allows to automatically set element's text based on it's value. Can be auto|always|never. Usefull for select and date.
+ Allows to automatically set element's text based on it's value. Can be auto|always|never. Useful for select and date.
For example, if dropdown list is {1: 'a', 2: 'b'} and element's value set to 1, it's html will be automatically set to 'a'.
auto - text will be automatically set only if element is empty.
always|never - always(never) try to set element's text.
@@ -526,22 +550,31 @@ Makes editable any HTML element on the page. Applied as jQuery method.
**/
autotext: 'auto',
/**
- Wether to return focus on element after form is closed.
- This allows fully keyboard input.
-
- @property enablefocus
- @type boolean
- @default false
- **/
- enablefocus: false,
- /**
Initial value of input. Taken from data-value or element's text.
@property value
@type mixed
@default element's text
**/
- value: null
+ value: null,
+ /**
+ Callback to perform custom displaying of value in element's text.
+ If null, default input's value2html() will be called.
+ If false, no displaying methods will be called, element's text will no change.
+ Runs under element's scope.
+ Second parameter __sourceData__ is passed for inputs with source (select, checklist).
+
+ @property display
+ @type function|boolean
+ @default null
+ @since 1.2.0
+ @example
+ display: function(value, sourceData) {
+ var escapedValue = $('
').text(value).html();
+ $(this).html(''+escapedValue+'');
+ }
+ **/
+ display: null
};
}(window.jQuery));
\ No newline at end of file
diff --git a/src/inputs-ext/address/address.js b/src/inputs-ext/address/address.js
index ec144b8..fa62147 100644
--- a/src/inputs-ext/address/address.js
+++ b/src/inputs-ext/address/address.js
@@ -26,19 +26,21 @@ $(function(){
this.init('address', options, Address.defaults);
};
- $.fn.editableform.utils.inherit(Address, $.fn.editableform.types.abstract);
+ $.fn.editableutils.inherit(Address, $.fn.editabletypes.abstract);
$.extend(Address.prototype, {
render: function() {
Address.superclass.render.call(this);
-
- // this.$input.
},
-
+ //standard way to show value in element. Used only if display option not defined.
value2html: function(value, element) {
- var html = value.city + ', ' + value.street + ' st., bld. ' + value.building;
- $(element).text(html);
+ if(!value) {
+ $(element).empty();
+ return;
+ }
+ var html = $('