This commit is contained in:
vitalets 2013-06-23 17:40:34 +04:00
parent 1e05597d40
commit 9136f8d34a
11 changed files with 1838 additions and 861 deletions

20
dist/CHANGELOG.txt vendored

@ -1,6 +1,26 @@
X-editable changelog
=============================
Version 1.4.5 Jun 23, 2013
----------------------------
[enh #245] highlight element after update (vitalets)
[enh] select2 now works with ajax source (vitalets)
[bug] fix datefield (datetimefield) to return null for incorrect dates (vitalets)
[bug #224] do not close popup when it is saving value (vitalets)
[enh] added `submitValue` to `save` event params (vitalets)
[enh #259] allow `getValue` method to return value itself, not object (vitalets)
[enh] add `destroy` method to inputs (vitalets)
[enh #164] allow emptytext to be html (vitalets)
[enh #78] allow html in editable content (vitalets)
[enh] update container position when datetimepicker viewMode changes (vitalets)
[enh #255] remove xxxView options from first level config of datetimepicker (vitalets)
[enh] if `display` defined as function call it on init (vitalets)
[enh #218] sourceCache now disables cache totally (vitalets)
[bug #156] solve conflict of bootstrap datepicker and jQuery UI datepicker (vitalets)
[enh] update bootstrap-datepicker to 1.1.2 (vitalets)
[enh] allow follow links in disabled state (vitalets)
[enh] update combodate to 1.0.4, fix #222 (vitalets)
Version 1.4.4 May 4, 2013
----------------------------

35
dist/README.md vendored

@ -1,16 +1,35 @@
# X-editable
In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery.
It is a new life of [bootstrap-editable plugin](http://github.com/vitalets/bootstrap-editable) that was strongly refactored and improved.
## Demo + Docs + Download
See **http://vitalets.github.com/x-editable**
## Live demo
**http://vitalets.github.io/x-editable/demo.html**
## Installation
### Manual download
Use **http://vitalets.github.io/x-editable**
### Bower
````
bower install x-editable
````
### CDN
````js
<link href="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.4.5/bootstrap-editable/css/bootstrap-editable.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/x-editable/1.4.5/bootstrap-editable/js/bootstrap-editable.min.js"></script>
````
## Documentation
**http://vitalets.github.io/x-editable**
## Reporting issues
When creating issues please provide [jsFiddle](http://jsfiddle.net) example. You can easily fork one of **jsFiddle templates**:
1. [bootstrap](http://jsfiddle.net/xBB5x/195)
2. [jqueryui](http://jsfiddle.net/xBB5x/196)
3. [plain](http://jsfiddle.net/xBB5x/197)
When creating issues please provide [jsFiddle](http://jsfiddle.net) example. You can easily fork one of following:
1. [jsFiddle bootstrap template](http://jsfiddle.net/xBB5x/195)
2. [jsFiddle jqueryui template](http://jsfiddle.net/xBB5x/196)
3. [jsFiddle jquery template](http://jsfiddle.net/xBB5x/197)
Your feedback is very appreciated!
## Contribution
@ -68,4 +87,4 @@ Thanks for your support!
## License
Copyright (c) 2012 Vitaliy Potapov
Licensed under the MIT licenses.
Licensed under the MIT license.

@ -1,4 +1,4 @@
/*! X-editable - v1.4.4
/*! X-editable - v1.4.5
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@ -184,6 +184,14 @@ a.editable-click.editable-disabled:hover {
/* content: '*'*/
}
.editable-bg-transition {
-webkit-transition: background-color 1400ms ease-in;
-moz-transition: background-color 1400ms ease-in;
-o-transition: background-color 1400ms ease-in;
-ms-transition: background-color 1400ms ease-in;
transition: background-color 1400ms ease-in;
}
/*see https://github.com/vitalets/x-editable/issues/139 */
.form-horizontal .editable
{
@ -305,7 +313,7 @@ a.editable-click.editable-disabled:hover {
border-color: #fdf59a #fdf59a #fbed50;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #000 !important;
color: #000;
}
.datepicker table tr td.today:hover,
.datepicker table tr td.today:hover:hover,
@ -339,6 +347,124 @@ a.editable-click.editable-disabled:hover {
.datepicker table tr td.today.disabled:hover.active {
background-color: #fbf069 \9;
}
.datepicker table tr td.today:hover:hover {
color: #000;
}
.datepicker table tr td.today.active:hover {
color: #fff;
}
.datepicker table tr td.range,
.datepicker table tr td.range:hover,
.datepicker table tr td.range.disabled,
.datepicker table tr td.range.disabled:hover {
background: #eeeeee;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today,
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today.disabled:hover {
background-color: #f3d17a;
background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
background-image: linear-gradient(top, #f3c17a, #f3e97a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
border-color: #f3e97a #f3e97a #edde34;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.datepicker table tr td.range.today:hover,
.datepicker table tr td.range.today:hover:hover,
.datepicker table tr td.range.today.disabled:hover,
.datepicker table tr td.range.today.disabled:hover:hover,
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active,
.datepicker table tr td.range.today.disabled,
.datepicker table tr td.range.today:hover.disabled,
.datepicker table tr td.range.today.disabled.disabled,
.datepicker table tr td.range.today.disabled:hover.disabled,
.datepicker table tr td.range.today[disabled],
.datepicker table tr td.range.today:hover[disabled],
.datepicker table tr td.range.today.disabled[disabled],
.datepicker table tr td.range.today.disabled:hover[disabled] {
background-color: #f3e97a;
}
.datepicker table tr td.range.today:active,
.datepicker table tr td.range.today:hover:active,
.datepicker table tr td.range.today.disabled:active,
.datepicker table tr td.range.today.disabled:hover:active,
.datepicker table tr td.range.today.active,
.datepicker table tr td.range.today:hover.active,
.datepicker table tr td.range.today.disabled.active,
.datepicker table tr td.range.today.disabled:hover.active {
background-color: #efe24b \9;
}
.datepicker table tr td.selected,
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected.disabled:hover {
background-color: #9e9e9e;
background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
background-image: -o-linear-gradient(top, #b3b3b3, #808080);
background-image: linear-gradient(top, #b3b3b3, #808080);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
border-color: #808080 #808080 #595959;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datepicker table tr td.selected:hover,
.datepicker table tr td.selected:hover:hover,
.datepicker table tr td.selected.disabled:hover,
.datepicker table tr td.selected.disabled:hover:hover,
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active,
.datepicker table tr td.selected.disabled,
.datepicker table tr td.selected:hover.disabled,
.datepicker table tr td.selected.disabled.disabled,
.datepicker table tr td.selected.disabled:hover.disabled,
.datepicker table tr td.selected[disabled],
.datepicker table tr td.selected:hover[disabled],
.datepicker table tr td.selected.disabled[disabled],
.datepicker table tr td.selected.disabled:hover[disabled] {
background-color: #808080;
}
.datepicker table tr td.selected:active,
.datepicker table tr td.selected:hover:active,
.datepicker table tr td.selected.disabled:active,
.datepicker table tr td.selected.disabled:hover:active,
.datepicker table tr td.selected.active,
.datepicker table tr td.selected:hover.active,
.datepicker table tr td.selected.disabled.active,
.datepicker table tr td.selected.disabled:hover.active {
background-color: #666666 \9;
}
.datepicker table tr td.active,
.datepicker table tr td.active:hover,
.datepicker table tr td.active.disabled,
@ -462,18 +588,19 @@ a.editable-click.editable-disabled:hover {
.datepicker table tr td span.active.disabled:hover.active {
background-color: #003399 \9;
}
.datepicker table tr td span.old {
.datepicker table tr td span.old,
.datepicker table tr td span.new {
color: #999999;
}
.datepicker th.switch {
.datepicker th.datepicker-switch {
width: 145px;
}
.datepicker thead tr:first-child th,
.datepicker tfoot tr:first-child th {
.datepicker tfoot tr th {
cursor: pointer;
}
.datepicker thead tr:first-child th:hover,
.datepicker tfoot tr:first-child th:hover {
.datepicker tfoot tr th:hover {
background: #eeeeee;
}
.datepicker .cw {
@ -493,3 +620,32 @@ a.editable-click.editable-disabled:hover {
width: 16px;
height: 16px;
}
.input-daterange input {
text-align: center;
}
.input-daterange input:first-child {
-webkit-border-radius: 3px 0 0 3px;
-moz-border-radius: 3px 0 0 3px;
border-radius: 3px 0 0 3px;
}
.input-daterange input:last-child {
-webkit-border-radius: 0 3px 3px 0;
-moz-border-radius: 0 3px 3px 0;
border-radius: 0 3px 3px 0;
}
.input-daterange .add-on {
display: inline-block;
width: auto;
min-width: 16px;
height: 18px;
padding: 4px 5px;
font-weight: normal;
line-height: 18px;
text-align: center;
text-shadow: 0 1px 0 #ffffff;
vertical-align: middle;
background-color: #eeeeee;
border: 1px solid #ccc;
margin-left: -5px;
margin-right: -5px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
/*! X-editable - v1.4.4
/*! X-editable - v1.4.5
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@ -184,6 +184,14 @@ a.editable-click.editable-disabled:hover {
/* content: '*'*/
}
.editable-bg-transition {
-webkit-transition: background-color 1400ms ease-in;
-moz-transition: background-color 1400ms ease-in;
-o-transition: background-color 1400ms ease-in;
-ms-transition: background-color 1400ms ease-in;
transition: background-color 1400ms ease-in;
}
/*see https://github.com/vitalets/x-editable/issues/139 */
.form-horizontal .editable
{

@ -1,4 +1,4 @@
/*! X-editable - v1.4.4
/*! X-editable - v1.4.5
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@ -65,6 +65,10 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
//show loading state
this.showLoading();
//flag showing is form now saving value to server.
//It is needed to wait when closing form.
this.isSaving = false;
/**
Fired when rendering starts
@event rendering
@ -217,31 +221,38 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
return;
}
//convert value for submitting to server
var submitValue = this.input.value2submit(newValue);
this.isSaving = true;
//sending data to server
$.when(this.save(newValue))
$.when(this.save(submitValue))
.done($.proxy(function(response) {
this.isSaving = false;
//run success callback
var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
//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;
}
}
//if success callback returns object like {newValue: <something>} --> use that value instead of submitted
//it is usefull if you want to chnage value in url-function
if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
newValue = res.newValue;
}
}
//clear error message
this.error(false);
@ -251,37 +262,42 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
@event save
@param {Object} event event object
@param {Object} params additional params
@param {mixed} params.newValue submitted value
@param {mixed} params.newValue raw new value
@param {mixed} params.submitValue submitted value as string
@param {Object} params.response ajax response
@example
$('#form-div').on('save'), function(e, params){
if(params.newValue === 'username') {...}
});
**/
this.$div.triggerHandler('save', {newValue: newValue, response: response});
});
**/
this.$div.triggerHandler('save', {newValue: newValue, submitValue: submitValue, response: response});
}, this))
.fail($.proxy(function(xhr) {
this.isSaving = false;
var msg;
if(typeof this.options.error === 'function') {
msg = this.options.error.call(this.options.scope, xhr, newValue);
} else {
msg = typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!';
}
this.error(msg);
this.showForm();
}, this));
},
save: function(newValue) {
//convert value for submitting to server
var submitValue = this.input.value2submit(newValue);
save: function(submitValue) {
//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 on server in following cases:
1. url is function
2. url is string AND (pk defined OR send option = always)
*/
send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk !== null && pk !== undefined)))),
params;
@ -816,6 +832,27 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
$.error('Unknown type: '+ type);
return false;
}
},
//see http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
supportsTransitions: function () {
var b = document.body || document.documentElement,
s = b.style,
p = 'transition',
v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
if(typeof s[p] === 'string') {
return true;
}
// Tests for vendor specific prop
p = p.charAt(0).toUpperCase() + p.substr(1);
for(var i=0; i<v.length; i++) {
if(typeof s[v[i] + p] === 'string') {
return true;
}
}
return false;
}
};
@ -856,6 +893,9 @@ Applied as jQuery method.
this.formOptions.scope = this.$element[0];
this.initContainer();
//flag to hide container, when saving value will finish
this.delayedHide = false;
//bind 'destroyed' listener to destroy container when element is removed from dom
this.$element.on('destroyed', $.proxy(function(){
@ -960,7 +1000,14 @@ Applied as jQuery method.
save: $.proxy(this.save, this), //click on submit button (value changed)
nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
show: $.proxy(function() {
if(this.delayedHide) {
this.hide(this.delayedHide.reason);
this.delayedHide = false;
} else {
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
resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
rendered: $.proxy(function(){
@ -1004,11 +1051,11 @@ Applied as jQuery method.
/*
Currently, form is re-rendered on every show.
The main reason is that we dont know, what container will do with content when closed:
remove(), detach() or just hide().
The main reason is that we dont know, what will container do with content when closed:
remove(), detach() or just hide() - it depends on container.
Detaching form itself before hide and re-insert before show is good solution,
but visually it looks ugly, as container changes size before hide.
but visually it looks ugly --> container changes size before hide.
*/
//if form already exist - delete previous data
@ -1041,10 +1088,18 @@ Applied as jQuery method.
return;
}
//if form is saving value, schedule hide
if(this.$form.data('editableform').isSaving) {
this.delayedHide = {reason: reason};
return;
} else {
this.delayedHide = false;
}
this.$element.removeClass('editable-open');
this.innerHide();
/**
/**
Fired when container was hidden. It occurs on both save or cancel.
**Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one.
The workaround is to check `arguments.length` that is always `2` for x-editable.
@ -1058,20 +1113,20 @@ Applied as jQuery method.
//auto-open next editable
$(this).closest('tr').next().find('.editable').editable('show');
}
});
**/
});
**/
this.$element.triggerHandler('hidden', reason || 'manual');
},
/* internal show method. To be overwritten in child classes */
innerShow: function () {
},
/* internal hide method. To be overwritten in child classes */
innerHide: function () {
},
},
/**
Toggles container visibility (show / hide)
@ -1116,7 +1171,7 @@ Applied as jQuery method.
**/
this.$element.triggerHandler('save', params);
//hide must be after trigger, as saving value may require methods od plugin, applied to input
//hide must be after trigger, as saving value may require methods of plugin, applied to input
this.hide('save');
},
@ -1276,7 +1331,7 @@ Applied as jQuery method.
onblur: 'cancel',
/**
Animation speed (inline mode)
Animation speed (inline mode only)
@property anim
@type string
@default false
@ -1380,6 +1435,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
} else {
this.init();
}
//check for transition support
if(this.options.highlight && !$.fn.editableutils.supportsTransitions()) {
this.options.highlight = false;
}
};
Editable.prototype = {
@ -1424,25 +1484,33 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.options.toggle !== 'manual') {
this.$element.addClass('editable-click');
this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
//prevent following link
e.preventDefault();
//prevent following link if editable enabled
if(!this.options.disabled) {
e.preventDefault();
}
//stop propagation not required because in document click handler it checks event target
//e.stopPropagation();
if(this.options.toggle === 'mouseenter') {
//for hover only show container
this.show();
this.show();
} else {
//when toggle='click' we should not close all other containers as they will be closed automatically in document click listener
var closeAll = (this.options.toggle !== 'click');
this.toggle(closeAll);
}
}
}, this));
} else {
this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
}
//if display is function it's far more convinient to have autotext = always to render correctly on init
//see https://github.com/vitalets/x-editable-yii/issues/34
if(typeof this.options.display === 'function') {
this.options.autotext = 'always';
}
//check conditions for autotext:
switch(this.options.autotext) {
case 'always':
@ -1621,12 +1689,29 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === '';
/*
isEmpty may be set directly as param of method.
It is required when we enable/disable field and can't rely on content
as node content is text: "Empty" that is not empty %)
*/
if(isEmpty !== undefined) {
this.isEmpty = isEmpty;
} else {
//detect empty
if($.trim(this.$element.html()) === '') {
this.isEmpty = true;
} else if($.trim(this.$element.text()) !== '') {
this.isEmpty = false;
} else {
//e.g. '<img>'
this.isEmpty = !this.$element.height() || !this.$element.width();
}
}
//emptytext shown only for enabled
if(!this.options.disabled) {
if (this.isEmpty) {
this.$element.text(this.options.emptytext);
this.$element.html(this.options.emptytext);
if(this.options.emptyclass) {
this.$element.addClass(this.options.emptyclass);
}
@ -1721,6 +1806,21 @@ Makes editable any HTML element on the page. Applied as jQuery method.
}
}
//highlight when saving
if(this.options.highlight) {
var $e = this.$element,
$bgColor = $e.css('background-color');
$e.css('background-color', this.options.highlight);
setTimeout(function(){
$e.css('background-color', $bgColor);
$e.addClass('editable-bg-transition');
setTimeout(function(){
$e.removeClass('editable-bg-transition');
}, 1500);
}, 0);
}
//set new value
this.setValue(params.newValue, false, params.response);
@ -1787,6 +1887,8 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.container) {
this.container.destroy();
}
this.input.destroy();
if(this.options.toggle !== 'manual') {
this.$element.removeClass('editable-click');
@ -1844,28 +1946,37 @@ Makes editable any HTML element on the page. Applied as jQuery method.
/**
Returns current values of editable elements.
Note that it returns an **object** with name-value pairs, not a value itself. It allows to get data from several elements.
If value of some editable is `null` or `undefined` it is excluded from result object.
If value of some editable is `null` or `undefined` it is excluded from result object.
When param `isSingle` is set to **true** - it is supposed you have single element and will return value of editable instead of object.
@method getValue()
@param {bool} isSingle whether to return just value of single element
@returns {Object} object of element names and values
@example
$('#username, #fullname').editable('getValue');
// possible result:
//result:
{
username: "superuser",
fullname: "John"
}
//isSingle = true
$('#username').editable('getValue', true);
//result "superuser"
**/
case 'getValue':
this.each(function () {
var $this = $(this), data = $this.data(datakey);
if (data && data.value !== undefined && data.value !== null) {
result[data.options.name] = data.input.value2submit(data.value);
}
});
if(arguments.length === 2 && arguments[1] === true) { //isSingle = true
result = this.eq(0).data(datakey).value;
} else {
this.each(function () {
var $this = $(this), data = $this.data(datakey);
if (data && data.value !== undefined && data.value !== null) {
result[data.options.name] = data.input.value2submit(data.value);
}
});
}
return result;
/**
/**
This method collects values from several editable elements and submit them all to server.
Internally it runs client-side validation for all fields and submits only in case of success.
See <a href="#newrecord">creating new records</a> for details.
@ -2089,7 +2200,16 @@ Makes editable any HTML element on the page. Applied as jQuery method.
});
</script>
**/
selector: null
selector: null,
/**
Color used to highlight element after update. Implemented via CSS3 transition, works in modern browsers.
@property highlight
@type string|boolean
@since 1.4.5
@default #FFFF80
**/
highlight: '#FFFF80'
};
}(window.jQuery));
@ -2103,23 +2223,23 @@ To create your own input you can inherit from this class.
**/
(function ($) {
"use strict";
//types
$.fn.editabletypes = {};
var AbstractInput = function () { };
AbstractInput.prototype = {
/**
Initializes input
@method init()
**/
init: function(type, options, defaults) {
this.type = type;
this.options = $.extend({}, defaults, options);
},
/*
this method called before render to init $tpl that is inserted in DOM
*/
@ -2133,107 +2253,107 @@ To create your own input you can inherit from this class.
/**
Renders input from tpl. Can return jQuery deferred object.
Can be overwritten in child objects
@method render()
**/
@method render()
**/
render: function() {
},
/**
Sets element's html by value.
@method value2html(value, element)
@method value2html(value, element)
@param {mixed} value
@param {DOMElement} element
**/
**/
value2html: function(value, element) {
$(element).text(value);
$(element).text($.trim(value));
},
/**
Converts element's html to value
@method html2value(html)
@method html2value(html)
@param {string} html
@returns {mixed}
**/
**/
html2value: function(html) {
return $('<div>').html(html).text();
},
/**
Converts value to string (for internal compare). For submitting to server used value2submit().
@method value2str(value)
@param {mixed} value
@returns {string}
**/
**/
value2str: function(value) {
return value;
},
/**
Converts string received from server into value. Usually from `data-value` attribute.
@method str2value(str)
@method str2value(str)
@param {string} str
@returns {mixed}
**/
**/
str2value: function(str) {
return str;
},
/**
Converts value for submitting to server. Result can be string or object.
@method value2submit(value)
@param {mixed} value
@returns {mixed}
**/
**/
value2submit: function(value) {
return value;
},
},
/**
Sets value of input.
@method value2input(value)
@param {mixed} value
**/
**/
value2input: function(value) {
this.$input.val(value);
},
/**
Returns value of input. Value can be object (e.g. datepicker)
@method input2value()
**/
**/
input2value: function() {
return this.$input.val();
},
/**
Activates input. For text it sets focus.
@method activate()
**/
**/
activate: function() {
if(this.$input.is(':visible')) {
this.$input.focus();
}
},
/**
Creates input.
@method clear()
**/
clear: function() {
this.$input.val(null);
},
/**
method to escape html.
**/
@ -2243,18 +2363,24 @@ To create your own input you can inherit from this class.
/**
attach handler to automatically submit form when value changed (useful when buttons not shown)
**/
**/
autosubmit: function() {
},
/**
Additional actions when destroying element
**/
destroy: function() {
},
// -------- helper functions --------
setClass: function() {
if(this.options.inputclass) {
this.$input.addClass(this.options.inputclass);
}
},
setAttr: function(attr) {
if (this.options[attr] !== undefined && this.options[attr] !== null) {
this.$input.attr(attr, this.options[attr]);
@ -2356,30 +2482,33 @@ List - abstract class for inputs that have source option loaded from js array or
// ------------- additional functions ------------
onSourceReady: function (success, error) {
//run source if it function
var source;
if ($.isFunction(this.options.source)) {
source = this.options.source.call(this.options.scope);
this.sourceData = null;
//note: if function returns the same source as URL - sourceData will be taken from cahce and no extra request performed
} else {
source = this.options.source;
}
//if allready loaded just call success
if($.isArray(this.sourceData)) {
if(this.options.sourceCache && $.isArray(this.sourceData)) {
success.call(this);
return;
}
// try parse json in single quotes (for double quotes jquery does automatically)
//try parse json in single quotes (for double quotes jquery does automatically)
try {
this.options.source = $.fn.editableutils.tryParseJson(this.options.source, false);
source = $.fn.editableutils.tryParseJson(source, false);
} catch (e) {
error.call(this);
return;
}
var source = this.options.source;
//run source if it function
if ($.isFunction(source)) {
source = source.call(this.options.scope);
}
//loading from url
if (typeof source === 'string') {
//try to get from cache
//try to get sourceData from cache
if(this.options.sourceCache) {
var cacheID = source,
cache;
@ -3299,25 +3428,29 @@ Range (inherit from number)
}(window.jQuery));
/**
Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
Please see [original docs](http://ivaynberg.github.com/select2) for detailed description and options.
You should manually include select2 distributive:
Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options.
Compatible **select2 version is 3.4.1**!
You should manually download and include select2 distributive:
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
<script src="select2/select2.js"></script>
For make it **Bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css):
To make it **bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css):
<link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link>
**Note:** currently `ajax` source for select2 is not supported, as it's not possible to load it in closed select2 state.
The solution is to load source manually and assign statically.
**Note:** currently `autotext` feature does not work for select2 with `ajax` remote source.
You need initially put both `data-value` and element's text youself:
<a href="#" data-type="select2" data-value="1">Text1</a>
@class select2
@extends abstractinput
@since 1.4.1
@final
@example
<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-original-title="Select country"></a>
<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a>
<script>
$(function(){
$('#country').editable({
@ -3338,56 +3471,47 @@ $(function(){
var Constructor = function (options) {
this.init('select2', options, Constructor.defaults);
options.select2 = options.select2 || {};
this.sourceData = null;
var that = this,
mixin = { //mixin to select2 options
placeholder: options.placeholder
};
//detect whether it is multi-valued
this.isMultiple = options.select2.tags || options.select2.multiple;
//if not `tags` mode, we need define initSelection to set data from source
if(!options.select2.tags) {
if(options.source) {
mixin.data = options.source;
}
//this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710
mixin.initSelection = function (element, callback) {
//temp: try update results
/*
if(options.select2 && options.select2.ajax) {
console.log('attached');
var original = $(element).data('select2').postprocessResults;
console.log(original);
$(element).data('select2').postprocessResults = function(data, initial) {
console.log('postprocess');
// this.element.triggerHandler('loaded', [data]);
original.apply(this, arguments);
}
// $(element).on('loaded', function(){console.log('loaded');});
$(element).data('select2').updateResults(true);
}
*/
var val = that.str2value(element.val()),
data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id');
//for single-valued mode should not use array. Take first element instead.
if($.isArray(data) && data.length && !that.isMultiple) {
data = data[0];
}
callback(data);
};
//placeholder
if(options.placeholder) {
options.select2.placeholder = options.placeholder;
}
//if not `tags` mode, use source
if(!options.select2.tags && options.source) {
var source = options.source;
//if source is function, call it (once!)
if ($.isFunction(options.source)) {
source = options.source.call(options.scope);
}
if (typeof source === 'string') {
options.select2.ajax = options.select2.ajax || {};
//some default ajax params
if(!options.select2.ajax.data) {
options.select2.ajax.data = function(term) {return { query:term };};
}
if(!options.select2.ajax.results) {
options.select2.ajax.results = function(data) { return {results:data };};
}
options.select2.ajax.url = source;
} else {
//check format and convert x-editable format to select2 format (if needed)
this.sourceData = this.convertSource(source);
options.select2.data = this.sourceData;
}
}
//overriding objects in config (as by default jQuery extend() is not recursive)
this.options.select2 = $.extend({}, Constructor.defaults.select2, mixin, options.select2);
this.options.select2 = $.extend({}, Constructor.defaults.select2, options.select2);
//detect whether it is multi-valued
this.isMultiple = this.options.select2.tags || this.options.select2.multiple;
this.isRemote = ('ajax' in this.options.select2);
};
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
@ -3395,21 +3519,17 @@ $(function(){
$.extend(Constructor.prototype, {
render: function() {
this.setClass();
//apply select2
this.$input.select2(this.options.select2);
//when data is loaded via ajax, we need to know when it's done
if('ajax' in this.options.select2) {
/*
console.log('attached');
var original = this.$input.data('select2').postprocessResults;
this.$input.data('select2').postprocessResults = function(data, initial) {
this.element.triggerHandler('loaded', [data]);
original.apply(this, arguments);
}
*/
//when data is loaded via ajax, we need to know when it's done to populate listData
if(this.isRemote) {
//listen to loaded event to populate data
this.$input.on('select2-loaded', $.proxy(function(e) {
this.sourceData = e.items.results;
}, this));
}
//trigger resize of editableform to re-position container in multi-valued mode
if(this.isMultiple) {
@ -3421,20 +3541,16 @@ $(function(){
value2html: function(value, element) {
var text = '', data;
if(this.$input) { //called when submitting form and select2 already exists
data = this.$input.select2('data');
} else { //on init (autotext)
//here select2 instance not created yet and data may be even not loaded.
//we can check data/tags property of select config and if exist lookup text
if(this.options.select2.tags) {
data = value;
} else if(this.options.select2.data) {
data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id');
} else {
//if('ajax' in this.options.select2) {
}
if(this.options.select2.tags) { //in tags mode just assign value
data = value;
} else if(this.sourceData) {
data = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
} else {
//can not get list of possible values (e.g. autotext for select2 with ajax source)
}
//data may be array (when multiple values allowed)
if($.isArray(data)) {
//collect selected data and show with separator
text = [];
@ -3455,7 +3571,26 @@ $(function(){
},
value2input: function(value) {
this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
//for remote source .val() is not working, need to look in sourceData
if(this.isRemote) {
//todo: check value for array
var item, items;
//if sourceData loaded, use it to get text for display
if(this.sourceData) {
items = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
if(items.length) {
item = items[0];
}
}
//if item not found by sourceData, use element text (e.g. for the first show)
if(!item) {
item = {id: value, text: $(this.options.scope).text()};
}
//select2('data', ...) allows to set both id and text --> usefull for initial show when items are not loaded
this.$input.select2('data', item).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
} else {
this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
}
},
input2value: function() {
@ -3488,6 +3623,22 @@ $(function(){
$(this).closest('form').submit();
}
});
},
/*
Converts source from x-editable format: {value: 1, text: "1"} to
select2 format: {id: 1, text: "1"}
*/
convertSource: function(source) {
if($.isArray(source) && source.length && source[0].value !== undefined) {
for(var i = 0; i<source.length; i++) {
if(source[i].value !== undefined) {
source[i].id = source[i].value;
delete source[i].value;
}
}
}
return source;
}
});
@ -3539,12 +3690,22 @@ $(function(){
}(window.jQuery));
/**
* Combodate - 1.0.3
* Combodate - 1.0.4
* Dropdown date and time picker.
* Converts text input into dropdowns to pick day, month, year, hour, minute and second.
* Uses momentjs as datetime library http://momentjs.com.
* For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
*
* Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
* In combodate:
* 12:00 pm --> 12:00 (24-h format, midday)
* 12:00 am --> 00:00 (24-h format, midnight, start of day)
*
* Differs from momentjs parse rules:
* 00:00 pm, 12:00 pm --> 12:00 (24-h format, day not change)
* 00:00 am, 12:00 am --> 00:00 (24-h format, day not change)
*
*
* Author: Vitaliy Potapov
* Project page: http://github.com/vitalets/combodate
* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
@ -3694,9 +3855,10 @@ $(function(){
for(i=0; i<=11; i++) {
if(longNames) {
name = moment().month(i).format('MMMM');
//see https://github.com/timrwood/momentjs.com/pull/36
name = moment().date(1).month(i).format('MMMM');
} else if(shortNames) {
name = moment().month(i).format('MMM');
name = moment().date(1).month(i).format('MMM');
} else if(twoDigit) {
name = this.leadZero(i+1);
} else {
@ -3732,9 +3894,10 @@ $(function(){
h12 = this.options.template.indexOf('h') !== -1,
h24 = this.options.template.indexOf('H') !== -1,
twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
min = h12 ? 1 : 0,
max = h12 ? 12 : 23;
for(i=0; i<=max; i++) {
for(i=min; i<=max; i++) {
name = twoDigit ? this.leadZero(i) : i;
items.push([i, name]);
}
@ -3783,7 +3946,7 @@ $(function(){
},
/*
Returns current date value.
Returns current date value from combos.
If format not specified - `options.format` used.
If format = `null` - Moment object returned.
*/
@ -3812,12 +3975,14 @@ $(function(){
return '';
}
//convert hours if 12h format
//convert hours 12h --> 24h
if(this.$ampm) {
values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
if(values.hour === 24) {
values.hour = 0;
}
//12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
if(values.hour === 12) {
values.hour = this.$ampm.val() === 'am' ? 0 : 12;
} else {
values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
}
}
dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]);
@ -3868,11 +4033,17 @@ $(function(){
});
if(this.$ampm) {
if(values.hour > 12) {
values.hour -= 12;
//12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
if(values.hour >= 12) {
values.ampm = 'pm';
if(values.hour > 12) {
values.hour -= 12;
}
} else {
values.ampm = 'am';
values.ampm = 'am';
if(values.hour === 0) {
values.hour = 12;
}
}
}

File diff suppressed because one or more lines are too long

@ -1,4 +1,4 @@
/*! X-editable - v1.4.4
/*! X-editable - v1.4.5
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@ -184,6 +184,14 @@ a.editable-click.editable-disabled:hover {
/* content: '*'*/
}
.editable-bg-transition {
-webkit-transition: background-color 1400ms ease-in;
-moz-transition: background-color 1400ms ease-in;
-o-transition: background-color 1400ms ease-in;
-ms-transition: background-color 1400ms ease-in;
transition: background-color 1400ms ease-in;
}
/*see https://github.com/vitalets/x-editable/issues/139 */
.form-horizontal .editable
{

@ -1,4 +1,4 @@
/*! X-editable - v1.4.4
/*! X-editable - v1.4.5
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
* http://github.com/vitalets/x-editable
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@ -65,6 +65,10 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
//show loading state
this.showLoading();
//flag showing is form now saving value to server.
//It is needed to wait when closing form.
this.isSaving = false;
/**
Fired when rendering starts
@event rendering
@ -217,31 +221,38 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
return;
}
//convert value for submitting to server
var submitValue = this.input.value2submit(newValue);
this.isSaving = true;
//sending data to server
$.when(this.save(newValue))
$.when(this.save(submitValue))
.done($.proxy(function(response) {
this.isSaving = false;
//run success callback
var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
//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;
}
}
//if success callback returns object like {newValue: <something>} --> use that value instead of submitted
//it is usefull if you want to chnage value in url-function
if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
newValue = res.newValue;
}
}
//clear error message
this.error(false);
@ -251,37 +262,42 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
@event save
@param {Object} event event object
@param {Object} params additional params
@param {mixed} params.newValue submitted value
@param {mixed} params.newValue raw new value
@param {mixed} params.submitValue submitted value as string
@param {Object} params.response ajax response
@example
$('#form-div').on('save'), function(e, params){
if(params.newValue === 'username') {...}
});
**/
this.$div.triggerHandler('save', {newValue: newValue, response: response});
});
**/
this.$div.triggerHandler('save', {newValue: newValue, submitValue: submitValue, response: response});
}, this))
.fail($.proxy(function(xhr) {
this.isSaving = false;
var msg;
if(typeof this.options.error === 'function') {
msg = this.options.error.call(this.options.scope, xhr, newValue);
} else {
msg = typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!';
}
this.error(msg);
this.showForm();
}, this));
},
save: function(newValue) {
//convert value for submitting to server
var submitValue = this.input.value2submit(newValue);
save: function(submitValue) {
//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 on server in following cases:
1. url is function
2. url is string AND (pk defined OR send option = always)
*/
send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk !== null && pk !== undefined)))),
params;
@ -816,6 +832,27 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
$.error('Unknown type: '+ type);
return false;
}
},
//see http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
supportsTransitions: function () {
var b = document.body || document.documentElement,
s = b.style,
p = 'transition',
v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
if(typeof s[p] === 'string') {
return true;
}
// Tests for vendor specific prop
p = p.charAt(0).toUpperCase() + p.substr(1);
for(var i=0; i<v.length; i++) {
if(typeof s[v[i] + p] === 'string') {
return true;
}
}
return false;
}
};
@ -856,6 +893,9 @@ Applied as jQuery method.
this.formOptions.scope = this.$element[0];
this.initContainer();
//flag to hide container, when saving value will finish
this.delayedHide = false;
//bind 'destroyed' listener to destroy container when element is removed from dom
this.$element.on('destroyed', $.proxy(function(){
@ -960,7 +1000,14 @@ Applied as jQuery method.
save: $.proxy(this.save, this), //click on submit button (value changed)
nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
show: $.proxy(function() {
if(this.delayedHide) {
this.hide(this.delayedHide.reason);
this.delayedHide = false;
} else {
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
resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
rendered: $.proxy(function(){
@ -1004,11 +1051,11 @@ Applied as jQuery method.
/*
Currently, form is re-rendered on every show.
The main reason is that we dont know, what container will do with content when closed:
remove(), detach() or just hide().
The main reason is that we dont know, what will container do with content when closed:
remove(), detach() or just hide() - it depends on container.
Detaching form itself before hide and re-insert before show is good solution,
but visually it looks ugly, as container changes size before hide.
but visually it looks ugly --> container changes size before hide.
*/
//if form already exist - delete previous data
@ -1041,10 +1088,18 @@ Applied as jQuery method.
return;
}
//if form is saving value, schedule hide
if(this.$form.data('editableform').isSaving) {
this.delayedHide = {reason: reason};
return;
} else {
this.delayedHide = false;
}
this.$element.removeClass('editable-open');
this.innerHide();
/**
/**
Fired when container was hidden. It occurs on both save or cancel.
**Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one.
The workaround is to check `arguments.length` that is always `2` for x-editable.
@ -1058,20 +1113,20 @@ Applied as jQuery method.
//auto-open next editable
$(this).closest('tr').next().find('.editable').editable('show');
}
});
**/
});
**/
this.$element.triggerHandler('hidden', reason || 'manual');
},
/* internal show method. To be overwritten in child classes */
innerShow: function () {
},
/* internal hide method. To be overwritten in child classes */
innerHide: function () {
},
},
/**
Toggles container visibility (show / hide)
@ -1116,7 +1171,7 @@ Applied as jQuery method.
**/
this.$element.triggerHandler('save', params);
//hide must be after trigger, as saving value may require methods od plugin, applied to input
//hide must be after trigger, as saving value may require methods of plugin, applied to input
this.hide('save');
},
@ -1276,7 +1331,7 @@ Applied as jQuery method.
onblur: 'cancel',
/**
Animation speed (inline mode)
Animation speed (inline mode only)
@property anim
@type string
@default false
@ -1380,6 +1435,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
} else {
this.init();
}
//check for transition support
if(this.options.highlight && !$.fn.editableutils.supportsTransitions()) {
this.options.highlight = false;
}
};
Editable.prototype = {
@ -1424,25 +1484,33 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.options.toggle !== 'manual') {
this.$element.addClass('editable-click');
this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
//prevent following link
e.preventDefault();
//prevent following link if editable enabled
if(!this.options.disabled) {
e.preventDefault();
}
//stop propagation not required because in document click handler it checks event target
//e.stopPropagation();
if(this.options.toggle === 'mouseenter') {
//for hover only show container
this.show();
this.show();
} else {
//when toggle='click' we should not close all other containers as they will be closed automatically in document click listener
var closeAll = (this.options.toggle !== 'click');
this.toggle(closeAll);
}
}
}, this));
} else {
this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
}
//if display is function it's far more convinient to have autotext = always to render correctly on init
//see https://github.com/vitalets/x-editable-yii/issues/34
if(typeof this.options.display === 'function') {
this.options.autotext = 'always';
}
//check conditions for autotext:
switch(this.options.autotext) {
case 'always':
@ -1621,12 +1689,29 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return;
}
this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === '';
/*
isEmpty may be set directly as param of method.
It is required when we enable/disable field and can't rely on content
as node content is text: "Empty" that is not empty %)
*/
if(isEmpty !== undefined) {
this.isEmpty = isEmpty;
} else {
//detect empty
if($.trim(this.$element.html()) === '') {
this.isEmpty = true;
} else if($.trim(this.$element.text()) !== '') {
this.isEmpty = false;
} else {
//e.g. '<img>'
this.isEmpty = !this.$element.height() || !this.$element.width();
}
}
//emptytext shown only for enabled
if(!this.options.disabled) {
if (this.isEmpty) {
this.$element.text(this.options.emptytext);
this.$element.html(this.options.emptytext);
if(this.options.emptyclass) {
this.$element.addClass(this.options.emptyclass);
}
@ -1721,6 +1806,21 @@ Makes editable any HTML element on the page. Applied as jQuery method.
}
}
//highlight when saving
if(this.options.highlight) {
var $e = this.$element,
$bgColor = $e.css('background-color');
$e.css('background-color', this.options.highlight);
setTimeout(function(){
$e.css('background-color', $bgColor);
$e.addClass('editable-bg-transition');
setTimeout(function(){
$e.removeClass('editable-bg-transition');
}, 1500);
}, 0);
}
//set new value
this.setValue(params.newValue, false, params.response);
@ -1787,6 +1887,8 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.container) {
this.container.destroy();
}
this.input.destroy();
if(this.options.toggle !== 'manual') {
this.$element.removeClass('editable-click');
@ -1844,28 +1946,37 @@ Makes editable any HTML element on the page. Applied as jQuery method.
/**
Returns current values of editable elements.
Note that it returns an **object** with name-value pairs, not a value itself. It allows to get data from several elements.
If value of some editable is `null` or `undefined` it is excluded from result object.
If value of some editable is `null` or `undefined` it is excluded from result object.
When param `isSingle` is set to **true** - it is supposed you have single element and will return value of editable instead of object.
@method getValue()
@param {bool} isSingle whether to return just value of single element
@returns {Object} object of element names and values
@example
$('#username, #fullname').editable('getValue');
// possible result:
//result:
{
username: "superuser",
fullname: "John"
}
//isSingle = true
$('#username').editable('getValue', true);
//result "superuser"
**/
case 'getValue':
this.each(function () {
var $this = $(this), data = $this.data(datakey);
if (data && data.value !== undefined && data.value !== null) {
result[data.options.name] = data.input.value2submit(data.value);
}
});
if(arguments.length === 2 && arguments[1] === true) { //isSingle = true
result = this.eq(0).data(datakey).value;
} else {
this.each(function () {
var $this = $(this), data = $this.data(datakey);
if (data && data.value !== undefined && data.value !== null) {
result[data.options.name] = data.input.value2submit(data.value);
}
});
}
return result;
/**
/**
This method collects values from several editable elements and submit them all to server.
Internally it runs client-side validation for all fields and submits only in case of success.
See <a href="#newrecord">creating new records</a> for details.
@ -2089,7 +2200,16 @@ Makes editable any HTML element on the page. Applied as jQuery method.
});
</script>
**/
selector: null
selector: null,
/**
Color used to highlight element after update. Implemented via CSS3 transition, works in modern browsers.
@property highlight
@type string|boolean
@since 1.4.5
@default #FFFF80
**/
highlight: '#FFFF80'
};
}(window.jQuery));
@ -2103,23 +2223,23 @@ To create your own input you can inherit from this class.
**/
(function ($) {
"use strict";
//types
$.fn.editabletypes = {};
var AbstractInput = function () { };
AbstractInput.prototype = {
/**
Initializes input
@method init()
**/
init: function(type, options, defaults) {
this.type = type;
this.options = $.extend({}, defaults, options);
},
/*
this method called before render to init $tpl that is inserted in DOM
*/
@ -2133,107 +2253,107 @@ To create your own input you can inherit from this class.
/**
Renders input from tpl. Can return jQuery deferred object.
Can be overwritten in child objects
@method render()
**/
@method render()
**/
render: function() {
},
/**
Sets element's html by value.
@method value2html(value, element)
@method value2html(value, element)
@param {mixed} value
@param {DOMElement} element
**/
**/
value2html: function(value, element) {
$(element).text(value);
$(element).text($.trim(value));
},
/**
Converts element's html to value
@method html2value(html)
@method html2value(html)
@param {string} html
@returns {mixed}
**/
**/
html2value: function(html) {
return $('<div>').html(html).text();
},
/**
Converts value to string (for internal compare). For submitting to server used value2submit().
@method value2str(value)
@param {mixed} value
@returns {string}
**/
**/
value2str: function(value) {
return value;
},
/**
Converts string received from server into value. Usually from `data-value` attribute.
@method str2value(str)
@method str2value(str)
@param {string} str
@returns {mixed}
**/
**/
str2value: function(str) {
return str;
},
/**
Converts value for submitting to server. Result can be string or object.
@method value2submit(value)
@param {mixed} value
@returns {mixed}
**/
**/
value2submit: function(value) {
return value;
},
},
/**
Sets value of input.
@method value2input(value)
@param {mixed} value
**/
**/
value2input: function(value) {
this.$input.val(value);
},
/**
Returns value of input. Value can be object (e.g. datepicker)
@method input2value()
**/
**/
input2value: function() {
return this.$input.val();
},
/**
Activates input. For text it sets focus.
@method activate()
**/
**/
activate: function() {
if(this.$input.is(':visible')) {
this.$input.focus();
}
},
/**
Creates input.
@method clear()
**/
clear: function() {
this.$input.val(null);
},
/**
method to escape html.
**/
@ -2243,18 +2363,24 @@ To create your own input you can inherit from this class.
/**
attach handler to automatically submit form when value changed (useful when buttons not shown)
**/
**/
autosubmit: function() {
},
/**
Additional actions when destroying element
**/
destroy: function() {
},
// -------- helper functions --------
setClass: function() {
if(this.options.inputclass) {
this.$input.addClass(this.options.inputclass);
}
},
setAttr: function(attr) {
if (this.options[attr] !== undefined && this.options[attr] !== null) {
this.$input.attr(attr, this.options[attr]);
@ -2356,30 +2482,33 @@ List - abstract class for inputs that have source option loaded from js array or
// ------------- additional functions ------------
onSourceReady: function (success, error) {
//run source if it function
var source;
if ($.isFunction(this.options.source)) {
source = this.options.source.call(this.options.scope);
this.sourceData = null;
//note: if function returns the same source as URL - sourceData will be taken from cahce and no extra request performed
} else {
source = this.options.source;
}
//if allready loaded just call success
if($.isArray(this.sourceData)) {
if(this.options.sourceCache && $.isArray(this.sourceData)) {
success.call(this);
return;
}
// try parse json in single quotes (for double quotes jquery does automatically)
//try parse json in single quotes (for double quotes jquery does automatically)
try {
this.options.source = $.fn.editableutils.tryParseJson(this.options.source, false);
source = $.fn.editableutils.tryParseJson(source, false);
} catch (e) {
error.call(this);
return;
}
var source = this.options.source;
//run source if it function
if ($.isFunction(source)) {
source = source.call(this.options.scope);
}
//loading from url
if (typeof source === 'string') {
//try to get from cache
//try to get sourceData from cache
if(this.options.sourceCache) {
var cacheID = source,
cache;
@ -3299,25 +3428,29 @@ Range (inherit from number)
}(window.jQuery));
/**
Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
Please see [original docs](http://ivaynberg.github.com/select2) for detailed description and options.
You should manually include select2 distributive:
Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options.
Compatible **select2 version is 3.4.1**!
You should manually download and include select2 distributive:
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
<script src="select2/select2.js"></script>
For make it **Bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css):
To make it **bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css):
<link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link>
**Note:** currently `ajax` source for select2 is not supported, as it's not possible to load it in closed select2 state.
The solution is to load source manually and assign statically.
**Note:** currently `autotext` feature does not work for select2 with `ajax` remote source.
You need initially put both `data-value` and element's text youself:
<a href="#" data-type="select2" data-value="1">Text1</a>
@class select2
@extends abstractinput
@since 1.4.1
@final
@example
<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-original-title="Select country"></a>
<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a>
<script>
$(function(){
$('#country').editable({
@ -3338,56 +3471,47 @@ $(function(){
var Constructor = function (options) {
this.init('select2', options, Constructor.defaults);
options.select2 = options.select2 || {};
this.sourceData = null;
var that = this,
mixin = { //mixin to select2 options
placeholder: options.placeholder
};
//detect whether it is multi-valued
this.isMultiple = options.select2.tags || options.select2.multiple;
//if not `tags` mode, we need define initSelection to set data from source
if(!options.select2.tags) {
if(options.source) {
mixin.data = options.source;
}
//this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710
mixin.initSelection = function (element, callback) {
//temp: try update results
/*
if(options.select2 && options.select2.ajax) {
console.log('attached');
var original = $(element).data('select2').postprocessResults;
console.log(original);
$(element).data('select2').postprocessResults = function(data, initial) {
console.log('postprocess');
// this.element.triggerHandler('loaded', [data]);
original.apply(this, arguments);
}
// $(element).on('loaded', function(){console.log('loaded');});
$(element).data('select2').updateResults(true);
}
*/
var val = that.str2value(element.val()),
data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id');
//for single-valued mode should not use array. Take first element instead.
if($.isArray(data) && data.length && !that.isMultiple) {
data = data[0];
}
callback(data);
};
//placeholder
if(options.placeholder) {
options.select2.placeholder = options.placeholder;
}
//if not `tags` mode, use source
if(!options.select2.tags && options.source) {
var source = options.source;
//if source is function, call it (once!)
if ($.isFunction(options.source)) {
source = options.source.call(options.scope);
}
if (typeof source === 'string') {
options.select2.ajax = options.select2.ajax || {};
//some default ajax params
if(!options.select2.ajax.data) {
options.select2.ajax.data = function(term) {return { query:term };};
}
if(!options.select2.ajax.results) {
options.select2.ajax.results = function(data) { return {results:data };};
}
options.select2.ajax.url = source;
} else {
//check format and convert x-editable format to select2 format (if needed)
this.sourceData = this.convertSource(source);
options.select2.data = this.sourceData;
}
}
//overriding objects in config (as by default jQuery extend() is not recursive)
this.options.select2 = $.extend({}, Constructor.defaults.select2, mixin, options.select2);
this.options.select2 = $.extend({}, Constructor.defaults.select2, options.select2);
//detect whether it is multi-valued
this.isMultiple = this.options.select2.tags || this.options.select2.multiple;
this.isRemote = ('ajax' in this.options.select2);
};
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
@ -3395,21 +3519,17 @@ $(function(){
$.extend(Constructor.prototype, {
render: function() {
this.setClass();
//apply select2
this.$input.select2(this.options.select2);
//when data is loaded via ajax, we need to know when it's done
if('ajax' in this.options.select2) {
/*
console.log('attached');
var original = this.$input.data('select2').postprocessResults;
this.$input.data('select2').postprocessResults = function(data, initial) {
this.element.triggerHandler('loaded', [data]);
original.apply(this, arguments);
}
*/
//when data is loaded via ajax, we need to know when it's done to populate listData
if(this.isRemote) {
//listen to loaded event to populate data
this.$input.on('select2-loaded', $.proxy(function(e) {
this.sourceData = e.items.results;
}, this));
}
//trigger resize of editableform to re-position container in multi-valued mode
if(this.isMultiple) {
@ -3421,20 +3541,16 @@ $(function(){
value2html: function(value, element) {
var text = '', data;
if(this.$input) { //called when submitting form and select2 already exists
data = this.$input.select2('data');
} else { //on init (autotext)
//here select2 instance not created yet and data may be even not loaded.
//we can check data/tags property of select config and if exist lookup text
if(this.options.select2.tags) {
data = value;
} else if(this.options.select2.data) {
data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id');
} else {
//if('ajax' in this.options.select2) {
}
if(this.options.select2.tags) { //in tags mode just assign value
data = value;
} else if(this.sourceData) {
data = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
} else {
//can not get list of possible values (e.g. autotext for select2 with ajax source)
}
//data may be array (when multiple values allowed)
if($.isArray(data)) {
//collect selected data and show with separator
text = [];
@ -3455,7 +3571,26 @@ $(function(){
},
value2input: function(value) {
this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
//for remote source .val() is not working, need to look in sourceData
if(this.isRemote) {
//todo: check value for array
var item, items;
//if sourceData loaded, use it to get text for display
if(this.sourceData) {
items = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
if(items.length) {
item = items[0];
}
}
//if item not found by sourceData, use element text (e.g. for the first show)
if(!item) {
item = {id: value, text: $(this.options.scope).text()};
}
//select2('data', ...) allows to set both id and text --> usefull for initial show when items are not loaded
this.$input.select2('data', item).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
} else {
this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
}
},
input2value: function() {
@ -3488,6 +3623,22 @@ $(function(){
$(this).closest('form').submit();
}
});
},
/*
Converts source from x-editable format: {value: 1, text: "1"} to
select2 format: {id: 1, text: "1"}
*/
convertSource: function(source) {
if($.isArray(source) && source.length && source[0].value !== undefined) {
for(var i = 0; i<source.length; i++) {
if(source[i].value !== undefined) {
source[i].id = source[i].value;
delete source[i].value;
}
}
}
return source;
}
});
@ -3539,12 +3690,22 @@ $(function(){
}(window.jQuery));
/**
* Combodate - 1.0.3
* Combodate - 1.0.4
* Dropdown date and time picker.
* Converts text input into dropdowns to pick day, month, year, hour, minute and second.
* Uses momentjs as datetime library http://momentjs.com.
* For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
*
* Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
* In combodate:
* 12:00 pm --> 12:00 (24-h format, midday)
* 12:00 am --> 00:00 (24-h format, midnight, start of day)
*
* Differs from momentjs parse rules:
* 00:00 pm, 12:00 pm --> 12:00 (24-h format, day not change)
* 00:00 am, 12:00 am --> 00:00 (24-h format, day not change)
*
*
* Author: Vitaliy Potapov
* Project page: http://github.com/vitalets/combodate
* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
@ -3694,9 +3855,10 @@ $(function(){
for(i=0; i<=11; i++) {
if(longNames) {
name = moment().month(i).format('MMMM');
//see https://github.com/timrwood/momentjs.com/pull/36
name = moment().date(1).month(i).format('MMMM');
} else if(shortNames) {
name = moment().month(i).format('MMM');
name = moment().date(1).month(i).format('MMM');
} else if(twoDigit) {
name = this.leadZero(i+1);
} else {
@ -3732,9 +3894,10 @@ $(function(){
h12 = this.options.template.indexOf('h') !== -1,
h24 = this.options.template.indexOf('H') !== -1,
twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
min = h12 ? 1 : 0,
max = h12 ? 12 : 23;
for(i=0; i<=max; i++) {
for(i=min; i<=max; i++) {
name = twoDigit ? this.leadZero(i) : i;
items.push([i, name]);
}
@ -3783,7 +3946,7 @@ $(function(){
},
/*
Returns current date value.
Returns current date value from combos.
If format not specified - `options.format` used.
If format = `null` - Moment object returned.
*/
@ -3812,12 +3975,14 @@ $(function(){
return '';
}
//convert hours if 12h format
//convert hours 12h --> 24h
if(this.$ampm) {
values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
if(values.hour === 24) {
values.hour = 0;
}
//12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
if(values.hour === 12) {
values.hour = this.$ampm.val() === 'am' ? 0 : 12;
} else {
values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
}
}
dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]);
@ -3868,11 +4033,17 @@ $(function(){
});
if(this.$ampm) {
if(values.hour > 12) {
values.hour -= 12;
//12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
if(values.hour >= 12) {
values.ampm = 'pm';
if(values.hour > 12) {
values.hour -= 12;
}
} else {
values.ampm = 'am';
values.ampm = 'am';
if(values.hour === 0) {
values.hour = 12;
}
}
}

File diff suppressed because one or more lines are too long