drupal-civicrm/sites/all/modules/civicrm/js/jquery/jquery.crmEditable.js
2018-01-14 13:10:16 +00:00

245 lines
8.2 KiB
JavaScript

// https://civicrm.org/licensing
(function($, _) {
"use strict";
/* jshint validthis: true */
// TODO: We'll need a way to clear this cache if options are edited.
// Maybe it should be stored in the CRM object so other parts of the app can use it.
// Note that if we do move it, we should also change the format of option lists to our standard sequential arrays
var optionsCache = {};
/**
* Helper fn to retrieve semantic data from markup
*/
$.fn.crmEditableEntity = function() {
var
el = this[0],
ret = {},
$row = this.first().closest('.crm-entity');
ret.entity = $row.data('entity') || $row[0].id.split('-')[0];
ret.id = $row.data('id') || $row[0].id.split('-')[1];
ret.action = $row.data('action') || 'create';
if (!ret.entity || !ret.id) {
return false;
}
$('.crm-editable, [data-field]', $row).each(function() {
var fieldName = $(this).data('field') || this.className.match(/crmf-(\S*)/)[1];
if (fieldName) {
ret[fieldName] = $(this).text();
if (this === el) {
ret.field = fieldName;
}
}
});
return ret;
};
/**
* @see http://wiki.civicrm.org/confluence/display/CRMDOC/Structure+convention+for+automagic+edit+in+place
*/
$.fn.crmEditable = function(options) {
function checkable() {
$(this).off('.crmEditable').on('change.crmEditable', function() {
var $el = $(this),
info = $el.crmEditableEntity();
if (!info.field) {
return false;
}
var params = {
sequential: 1,
id: info.id,
field: info.field,
value: $el.is(':checked') ? 1 : 0
};
CRM.api3(info.entity, info.action, params, true);
});
}
return this.each(function() {
var $i,
fieldName = "",
defaults = {
error: function(entity, field, value, data) {
restoreContainer();
$(this).html(originalValue || settings.placeholder).click();
var msg = $.isPlainObject(data) && data.error_message;
errorMsg = $(':input', this).first().crmError(msg || ts('Sorry an error occurred and your information was not saved'), ts('Error'));
},
success: function(entity, field, value, data, settings) {
restoreContainer();
if ($i.data('refresh')) {
CRM.refreshParent($i);
} else {
value = value === '' ? settings.placeholder : _.escape(value);
$i.html(value);
}
}
},
originalValue = '',
errorMsg,
editableSettings = $.extend({}, defaults, options);
if ($(this).hasClass('crm-editable-enabled')) {
return;
}
if (this.nodeName == "INPUT" && this.type == "checkbox") {
checkable.call(this, this);
return;
}
// Table cell needs something inside it to look right
if ($(this).is('td')) {
$(this)
.removeClass('crm-editable')
.wrapInner('<div class="crm-editable" />');
$i = $('div.crm-editable', this)
.data($(this).data());
var field = this.className.match(/crmf-(\S*)/);
if (field) {
$i.data('field', field[1]);
}
}
else {
$i = $(this);
}
var settings = {
tooltip: $i.data('tooltip') || ts('Click to edit'),
placeholder: $i.data('placeholder') || '<i class="crm-i fa-pencil crm-editable-placeholder"></i>',
onblur: 'cancel',
cancel: '<button type="cancel"><i class="crm-i fa-times"></i></button>',
submit: '<button type="submit"><i class="crm-i fa-check"></i></button>',
cssclass: 'crm-editable-form',
data: getData,
onreset: restoreContainer
};
if ($i.data('type')) {
settings.type = $i.data('type');
if (settings.type == 'boolean') {
settings.type = 'select';
$i.data('options', {'0': ts('No'), '1': ts('Yes')});
}
}
if (settings.type == 'textarea') {
$i.addClass('crm-editable-textarea-enabled');
}
$i.addClass('crm-editable-enabled');
function callback(value, settings) {
$i.addClass('crm-editable-saving');
var
info = $i.crmEditableEntity(),
$el = $($i),
params = {},
action = $i.data('action') || info.action;
if (!info.field) {
return false;
}
if (info.id && info.id !== 'new') {
params.id = info.id;
}
if (action === 'setvalue') {
params.field = info.field;
params.value = value;
}
else {
params[info.field] = value;
}
CRM.api3(info.entity, action, params, {error: null})
.done(function(data) {
if (data.is_error) {
return editableSettings.error.call($el[0], info.entity, info.field, value, data);
}
if ($el.data('options')) {
value = $el.data('options')[value] || '';
}
else if ($el.data('optionsHashKey')) {
var options = optionsCache[$el.data('optionsHashKey')];
value = options && options[value] ? options[value] : '';
}
$el.trigger('crmFormSuccess', [value]);
editableSettings.success.call($el[0], info.entity, info.field, value, data, settings);
})
.fail(function(data) {
editableSettings.error.call($el[0], info.entity, info.field, value, data);
});
}
CRM.loadScript(CRM.config.resourceBase + 'packages/jquery/plugins/jquery.jeditable.min.js').done(function() {
$i.editable(callback, settings);
});
// CRM-15759 - Workaround broken textarea handling in jeditable 1.7.1
$i.click(function() {
$('textarea', this).off()
// Fix cancel-on-blur
.on('blur', function(e) {
if (!e.relatedTarget || !$(e.relatedTarget).is('.crm-editable-form button')) {
$i.find('button[type=cancel]').click();
}
})
// Add support for ctrl-enter shortcut key
.on('keydown', function (e) {
if (e.ctrlKey && e.keyCode == 13) {
$i.find('button[type=submit]').click();
e.preventDefault();
}
});
});
function getData(value, settings) {
// Add css class to wrapper
// FIXME: This should be a response to an event instead of coupled with this function but jeditable 1.7.1 doesn't trigger any events :(
$i.addClass('crm-editable-editing');
originalValue = value;
if ($i.data('type') == 'select' || $i.data('type') == 'boolean') {
if ($i.data('options')) {
return formatOptions($i.data('options'));
}
var result,
info = $i.crmEditableEntity(),
// Strip extra id from multivalued custom fields
custom = info.field.match(/(custom_\d+)_\d+/),
field = custom ? custom[1] : info.field,
hash = info.entity + '.' + field,
params = {
field: field,
context: 'create'
};
$i.data('optionsHashKey', hash);
if (!optionsCache[hash]) {
$.ajax({
url: CRM.url('civicrm/ajax/rest'),
data: {entity: info.entity, action: 'getoptions', json: JSON.stringify(params)},
async: false, // jeditable lacks support for async options lookup
success: function(data) {optionsCache[hash] = data.values;}
});
}
return formatOptions(optionsCache[hash]);
}
// Unwrap contents then replace html special characters with plain text
return _.unescape(value.replace(/<(?:.|\n)*?>/gm, ''));
}
function formatOptions(options) {
if (typeof $i.data('emptyOption') === 'string') {
// Using 'null' because '' is broken in jeditable 1.7.1
return $.extend({'null': $i.data('emptyOption')}, options);
}
return options;
}
function restoreContainer() {
if (errorMsg && errorMsg.close) errorMsg.close();
$i.removeClass('crm-editable-saving crm-editable-editing');
}
});
};
})(jQuery, CRM._);