init
This commit is contained in:
.gitignoreCHANGELOG.txtLICENSE-GPLLICENSE-MITREADME.mdgrunt.jsjquery.ui.datepicker-ru.jsmakezippackage.json
libs
bootstrap204
css
img
js
bootstrap211
css
img
js
bootstrap221
css
img
js
jquery-ui-1.9.1.custom
css
redmond
images
ui-bg_flat_0_aaaaaa_40x100.pngui-bg_flat_55_fbec88_40x100.pngui-bg_glass_75_d0e5f5_1x400.pngui-bg_glass_85_dfeffc_1x400.pngui-bg_glass_95_fef1ec_1x400.pngui-bg_gloss-wave_55_5c9ccc_500x100.pngui-bg_inset-hard_100_f5f8f9_1x100.pngui-bg_inset-hard_100_fcfdfd_1x100.pngui-icons_217bc0_256x240.pngui-icons_2e83ff_256x240.pngui-icons_469bdd_256x240.pngui-icons_6da8d5_256x240.pngui-icons_cd0a0a_256x240.pngui-icons_d8e7f3_256x240.pngui-icons_f9bd01_256x240.png
jquery-ui-1.9.1.custom.cssjquery-ui-1.9.1.custom.min.csssmoothness
images
ui-bg_flat_0_aaaaaa_40x100.pngui-bg_flat_75_ffffff_40x100.pngui-bg_glass_55_fbf9ee_1x400.pngui-bg_glass_65_ffffff_1x400.pngui-bg_glass_75_dadada_1x400.pngui-bg_glass_75_e6e6e6_1x400.pngui-bg_glass_95_fef1ec_1x400.pngui-bg_highlight-soft_75_cccccc_1x100.pngui-icons_222222_256x240.pngui-icons_2e83ff_256x240.pngui-icons_454545_256x240.pngui-icons_888888_256x240.pngui-icons_cd0a0a_256x240.png
jquery-ui-1.9.1.custom.cssjquery-ui-1.9.1.custom.min.cssjs
jquery-ui-datepicker
jquery
mockjax
poshytip
jquery.poshytip.jsjquery.poshytip.min.jsjquery.poshytip_corrected.js
tip-darkgray
tip-green
tip-skyblue
tip-twitter
tip-violet
tip-yellow
tip-yellowsimple
qunit
src
containers
editable-container.csseditable-container.jseditable-inline.jseditable-popover.jseditable-poshytip.jseditable-tooltip.js
editable-form
editable-form-bootstrap.jseditable-form-jqueryui.jseditable-form-utils.jseditable-form.csseditable-form.js
img
element
inputs
abstract.js
date
bootstrap-datepicker.jsdate.jsdatepicker.css
dateui.jsselect.jstext.jstextarea.jslocales
bootstrap-datepicker.bg.jsbootstrap-datepicker.br.jsbootstrap-datepicker.cs.jsbootstrap-datepicker.da.jsbootstrap-datepicker.de.jsbootstrap-datepicker.es.jsbootstrap-datepicker.fi.jsbootstrap-datepicker.fr.jsbootstrap-datepicker.id.jsbootstrap-datepicker.is.jsbootstrap-datepicker.it.jsbootstrap-datepicker.ja.jsbootstrap-datepicker.kr.jsbootstrap-datepicker.lt.jsbootstrap-datepicker.lv.jsbootstrap-datepicker.ms.jsbootstrap-datepicker.nb.jsbootstrap-datepicker.nl.jsbootstrap-datepicker.pl.jsbootstrap-datepicker.pt-BR.jsbootstrap-datepicker.pt.jsbootstrap-datepicker.ru.jsbootstrap-datepicker.sl.jsbootstrap-datepicker.sv.jsbootstrap-datepicker.th.jsbootstrap-datepicker.tr.jsbootstrap-datepicker.zh-CN.jsbootstrap-datepicker.zh-TW.js
test
25
src/element/editable-element.css
Normal file
25
src/element/editable-element.css
Normal file
@ -0,0 +1,25 @@
|
||||
.editable-click, a.editable-click, a.editable-click:hover {
|
||||
text-decoration: none;
|
||||
border-bottom: dashed 1px #0088cc;
|
||||
}
|
||||
|
||||
.editable-empty, .editable-empty:hover{
|
||||
font-style: italic;
|
||||
color: #DD1144;
|
||||
border-bottom: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.editable-unsaved {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.editable-unsaved:after {
|
||||
/* content: '*'*/
|
||||
}
|
||||
|
||||
.editable-disabled, a.editable-disabled, a.editable-disabled:hover {
|
||||
color: black;
|
||||
cursor: default;
|
||||
text-decoration: none;
|
||||
}
|
373
src/element/editable-element.js
Normal file
373
src/element/editable-element.js
Normal file
@ -0,0 +1,373 @@
|
||||
/**
|
||||
* Editable-element
|
||||
* Initialize HTML element that can be editable by click.
|
||||
* 1. methods
|
||||
*
|
||||
* 2. events
|
||||
* - render
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
var Editable = function (element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, $.fn.editable.defaults, $.fn.editableform.utils.getConfigData(this.$element), options);
|
||||
this.init();
|
||||
};
|
||||
|
||||
Editable.prototype = {
|
||||
constructor: Editable,
|
||||
init: function () {
|
||||
var TypeConstructor,
|
||||
isValueByText = false,
|
||||
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. editablePopover)');
|
||||
return;
|
||||
}
|
||||
|
||||
//name must be defined
|
||||
this.options.name = this.options.name || this.$element.attr('id');
|
||||
if (!this.options.name) {
|
||||
$.error('You must define name (or id) for Editable element');
|
||||
return;
|
||||
}
|
||||
|
||||
//create input of specified type. Input will be used for converting value, not in form
|
||||
if(typeof $.fn.editableform.types[this.options.type] === 'function') {
|
||||
TypeConstructor = $.fn.editableform.types[this.options.type];
|
||||
this.typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults));
|
||||
this.input = new TypeConstructor(this.typeOptions);
|
||||
} else {
|
||||
$.error('Unknown type: '+ this.options.type);
|
||||
return;
|
||||
}
|
||||
|
||||
//set value from settings or by element's text
|
||||
if (this.options.value === undefined || this.options.value === null) {
|
||||
this.value = this.input.html2value($.trim(this.$element.html()));
|
||||
isValueByText = true;
|
||||
} else {
|
||||
this.value = this.input.str2value($.trim(this.options.value));
|
||||
}
|
||||
|
||||
//attach handler to close any container on escape
|
||||
$(document).off('keyup.editable').on('keyup.editable', function (e) {
|
||||
if (e.which === 27) {
|
||||
$('.editable-container').find('button[type=button]').click();
|
||||
}
|
||||
});
|
||||
|
||||
//attach handler to close container when click outside
|
||||
$(document).off('click.editable').on('click.editable', function(e) {
|
||||
//if click inside container --> do nothing
|
||||
var $target = $(e.target);
|
||||
if($target.is('.editable-container') || $target.parents('.editable-container').length || $target.parents('.ui-datepicker-header').length) {
|
||||
return;
|
||||
}
|
||||
$('.editable-container').find('button[type=button]').click();
|
||||
});
|
||||
|
||||
//add 'editable' class
|
||||
this.$element.addClass('editable');
|
||||
|
||||
//always attach click handler, but in disabled mode it just prevent default action (useful for links)
|
||||
this.$element.on('click.editable', $.proxy(this.click, this));
|
||||
|
||||
//check conditions for autotext:
|
||||
//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() {
|
||||
if(this.options.disabled) {
|
||||
this.disable();
|
||||
} else {
|
||||
this.enable();
|
||||
}
|
||||
this.$element.triggerHandler('render', this);
|
||||
this.isInit = false;
|
||||
}, this));
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this.options.disabled = false;
|
||||
this.$element.removeClass('editable-disabled');
|
||||
this.handleEmpty();
|
||||
if(this.options.toggle === 'click') {
|
||||
this.$element.addClass('editable-click');
|
||||
if(this.$element.attr('tabindex') === -1) {
|
||||
this.$element.removeAttr('tabindex');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
this.options.disabled = true;
|
||||
this.hide();
|
||||
this.$element.addClass('editable-disabled');
|
||||
this.handleEmpty();
|
||||
if(this.options.toggle === 'click') {
|
||||
this.$element.removeClass('editable-click');
|
||||
this.$element.attr('tabindex', -1);
|
||||
}
|
||||
},
|
||||
|
||||
toggleDisabled: function() {
|
||||
if(this.options.disabled) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
},
|
||||
|
||||
option: function(key, value) {
|
||||
if(key === 'disabled') {
|
||||
if(value) {
|
||||
this.disable();
|
||||
} else {
|
||||
this.enable();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.options[key] = value;
|
||||
|
||||
//transfer new option to container!
|
||||
if(this.container) {
|
||||
this.container.option(key, value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* set emptytext if element is empty (reverse: remove emptytext if needed)
|
||||
*/
|
||||
handleEmpty: function () {
|
||||
var emptyClass = 'editable-empty';
|
||||
//emptytext shown only for enabled
|
||||
if(!this.options.disabled) {
|
||||
if ($.trim(this.$element.text()) === '') {
|
||||
this.$element.addClass(emptyClass).text(this.options.emptytext);
|
||||
} else {
|
||||
this.$element.removeClass(emptyClass);
|
||||
}
|
||||
} else {
|
||||
//below required if element disable property was changed
|
||||
if(this.$element.hasClass(emptyClass)) {
|
||||
this.$element.empty();
|
||||
this.$element.removeClass(emptyClass);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
click: function (e) {
|
||||
e.preventDefault();
|
||||
if(this.options.disabled || this.options.toggle !== 'click') {
|
||||
return;
|
||||
}
|
||||
//stop propagation bacause document listen any click to hide all containers
|
||||
e.stopPropagation();
|
||||
this.toggle();
|
||||
},
|
||||
|
||||
/**
|
||||
* show container with form
|
||||
*/
|
||||
show: function () {
|
||||
if(this.options.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
//init editableContainer: popover, tooltip, inline, etc..
|
||||
if(!this.container) {
|
||||
var containerOptions = $.extend({}, this.options, {
|
||||
value: this.value,
|
||||
autohide: false
|
||||
});
|
||||
this.$element.editableContainer(containerOptions);
|
||||
this.$element.on({
|
||||
save: $.proxy(this.save, this),
|
||||
cancel: $.proxy(this.hide, this)
|
||||
});
|
||||
this.container = this.$element.data('editableContainer');
|
||||
} else if(this.container.tip().is(':visible')) {
|
||||
return;
|
||||
}
|
||||
|
||||
//hide all other editable containers. Required to work correctly with toggle = manual
|
||||
$('.editable-container').find('button[type=button]').click();
|
||||
|
||||
//show container
|
||||
this.container.show();
|
||||
},
|
||||
|
||||
/**
|
||||
* hide container with form
|
||||
*/
|
||||
hide: function () {
|
||||
if(this.container && this.container.tip().is(':visible')) {
|
||||
this.container.hide();
|
||||
|
||||
//return focus on element
|
||||
if (this.options.enablefocus && this.options.toggle === 'click') {
|
||||
this.$element.focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* show/hide form container
|
||||
*/
|
||||
toggle: function () {
|
||||
if(this.container && this.container.tip().is(':visible')) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* called when form was submitted
|
||||
*/
|
||||
save: function(e, params) {
|
||||
var error, form;
|
||||
|
||||
//if sent to server, call success callback. if it return string --> show error
|
||||
if((params.response !== undefined) && (error = this.options.success.call(this, params.response, params.newValue))) {
|
||||
form = this.container.tip().find('form').parent().data('editableform');
|
||||
form.error(error);
|
||||
form.showForm();
|
||||
return;
|
||||
}
|
||||
|
||||
//if value was not sent to server and value changed --> mark element with unsaved css
|
||||
if(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.setValue(params.newValue);
|
||||
},
|
||||
|
||||
validate: function () {
|
||||
if (typeof this.options.validate === 'function') {
|
||||
return this.options.validate.call(this, this.value);
|
||||
}
|
||||
},
|
||||
|
||||
setValue: function(v, convertStr) {
|
||||
if(convertStr) {
|
||||
this.value = this.input.str2value(v);
|
||||
} else {
|
||||
this.value = v;
|
||||
}
|
||||
if(this.container) {
|
||||
this.container.option('value', this.value);
|
||||
}
|
||||
$.when(this.input.value2html(this.value, this.$element))
|
||||
.then($.proxy(function() {
|
||||
this.handleEmpty();
|
||||
this.$element.triggerHandler('render', this);
|
||||
}, this));
|
||||
}
|
||||
};
|
||||
|
||||
/* EDITABLE PLUGIN DEFINITION
|
||||
* ======================= */
|
||||
|
||||
$.fn.editable = function (option) {
|
||||
//special methods returning non-jquery object
|
||||
var result = {}, args = arguments, datakey = 'editable';
|
||||
switch (option) {
|
||||
case 'validate':
|
||||
this.each(function () {
|
||||
var $this = $(this), data = $this.data(datakey), error;
|
||||
if (data && (error = data.validate())) {
|
||||
result[data.options.name] = error;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
||||
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.value2str(data.value);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
||||
case 'submit': //collects value, validate and submit to server for creating new record
|
||||
var config = arguments[1] || {},
|
||||
$elems = this,
|
||||
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',
|
||||
url: config.url,
|
||||
data: values,
|
||||
dataType: 'json'
|
||||
}).success(function(response) {
|
||||
if(typeof response === 'object' && response.id) {
|
||||
$elems.editable('option', 'pk', response.id);
|
||||
$elems.removeClass('editable-unsaved');
|
||||
if(typeof config.success === 'function') {
|
||||
config.success.apply($elems, arguments);
|
||||
}
|
||||
} else { //server-side validation error
|
||||
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});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
//return jquery object
|
||||
return this.each(function () {
|
||||
var $this = $(this),
|
||||
data = $this.data(datakey),
|
||||
options = typeof option === 'object' && option;
|
||||
|
||||
if (!data) {
|
||||
$this.data(datakey, (data = new Editable(this, options)));
|
||||
}
|
||||
|
||||
if (typeof option === 'string') { //call method
|
||||
data[option].apply(data, Array.prototype.slice.call(args, 1));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$.fn.editable.defaults = {
|
||||
type:'text',
|
||||
disabled: false,
|
||||
toggle: 'click',
|
||||
trigger: 'manual',
|
||||
emptytext: 'Empty',
|
||||
autotext: 'auto',
|
||||
enablefocus: false,
|
||||
success: function(response, newValue) {} //value successfully sent on server and response status = 200
|
||||
};
|
||||
|
||||
}(window.jQuery));
|
Reference in New Issue
Block a user