2 * Copyright (C) 2012 Xavier Dutoit
3 * Licensed to CiviCRM under the Academic Free License version 3.0.
6 * This offers two features:
7 * - crmEditable() edit in place of a single field
8 * (mostly a wrapper that binds jeditable features with the ajax api and replies on crm-entity crmf-{field} html conventions)
9 * if you want to add an edit in place on a template:
10 * - add a class crm-entity and id {EntityName}-{Entityid} higher in the dom
11 * - add a class crm-editable and crmf-{FieldName} around the field (you can add a span if needed)
12 * - add data-action=create if you need to specify the api action to call (default setvalue)
13 * crmf- stands for crm field
15 * this embed a civicrm form and make it in place (load+ajaxForm)
16 * to make it easier to customize the form (eg. hide a button...) it triggers a 'load' event on the form. you can then catch the load on your code (using the $('#id_of_the_form').on(function(){//do something
21 $.fn
.crmEditableEntity = function() {
25 $row
= this.first().closest('.crm-entity');
26 ret
.entity
= $row
.data('entity') || $row
[0].id
.split('-')[0];
27 ret
.id
= $row
.data('id') || $row
[0].id
.split('-')[1];
28 if (!ret
.entity
|| !ret
.id
) {
31 $('.crm-editable, [data-field]', $row
).each(function() {
32 var fieldName
= $(this).data('field') || this.className
.match(/crmf-(\S*)/)[1];
34 ret
[fieldName
] = $(this).text();
36 ret
.field
= fieldName
;
43 $.fn
.crmEditable = function (options
) {
44 var checkable = function () {
45 $(this).change (function() {
46 var info
= $(this).crmEditableEntity();
50 var checked
= $(this).is(':checked');
55 value
: checked
? 1 : 0
57 CRM
.api(info
.entity
, 'setvalue', params
, {
59 error: function (data
) {
60 editableSettings
.error
.call(this, info
.entity
, info
.field
, checked
, data
);
62 success: function (data
) {
63 editableSettings
.success
.call(this, info
.entity
, info
.field
, checked
, data
);
71 callBack:function(data
){
73 editableSettings
.error
.call (this,data
);
75 return editableSettings
.success
.call (this,data
);
78 error: function(entity
,field
,value
,data
) {
79 $(this).crmError(data
.error_message
, ts('Error'));
80 $(this).removeClass('crm-editable-saving');
82 success: function(entity
,field
,value
,data
) {
84 CRM
.alert('', ts('Saved'), 'success');
85 $i
.removeClass ('crm-editable-saving crm-error');
90 var editableSettings
= $.extend({}, defaults
, options
);
91 return this.each(function() {
95 if (this.nodeName
== "INPUT" && this.type
=="checkbox") {
96 checkable
.call(this,this);
100 if (this.nodeName
== 'A') {
101 if (this.className
.indexOf('crmf-') == -1) { // it isn't a jeditable field
102 var formSettings
= $.extend({}, editableSettings
.form
,
103 {source
: $i
.attr('href')
104 ,success: function (result
) {
105 if ($i
.hasClass('crm-dialog')) {
106 $('.ui-dialog').dialog('close').remove();
108 $i
.next().slideUp().remove();
109 $i
.trigger('success',result
);
112 var id
= $i
.closest('.crm-entity').attr('id');
114 var e
=id
.match(/(\S*)-(\S*)/);
116 console
&& console
.log
&& console
.log("Couldn't get the entity id. You need to set class='crm-entity' id='{entityName}-{id}'");
117 formSettings
.entity
=e
[1];
118 formSettings
.id
=e
[2];
120 if ($i
.hasClass('crm-dialog')) {
121 $i
.click (function () {
122 var $n
=$('<div>Loading</div>').appendTo('body');
123 $n
.dialog ({modal
:true,width
:500});
124 $n
.crmForm (formSettings
);
128 $i
.click (function () {
130 if (!$n
.hasClass('crm-target')) {
131 $n
=$i
.after('<div class="crm-target"></div>').next();
136 $n
.crmForm (formSettings
);
146 tooltip
: 'Click to edit...',
147 placeholder
: '<span class="crm-editable-placeholder">Click to edit</span>',
148 data: function(value
, settings
) {
149 return value
.replace(/<(?:.|\n)*?>/gm, '');
152 if ($i
.data('placeholder')) {
153 settings
.placeholder
= $i
.data('placeholder');
155 settings
.placeholder
= '<span class="crm-editable-placeholder">Click to edit</span>';
157 if ($i
.data('tooltip')) {
158 settings
.placeholder
= $i
.data('tooltip')
160 settings
.tooltip
= 'Click to edit...';
162 if ($i
.data('type')) {
163 settings
.type
= $i
.data('type');
164 settings
.onblur
= 'submit';
166 if ($i
.data('options')){
167 settings
.data
= $i
.data('options');
169 if(settings
.type
== 'textarea'){
170 $i
.addClass ('crm-editable-textarea-enabled');
173 $i
.addClass ('crm-editable-enabled');
176 $i
.editable(function(value
,settings
) {
177 $i
.addClass ('crm-editable-saving');
179 info
= $i
.crmEditableEntity(),
181 action
= $i
.data('action') || 'setvalue';
185 if (info
.id
&& info
.id
!== 'new') {
188 if (action
=== 'setvalue') {
189 params
.field
= info
.field
;
190 params
.value
= value
;
193 params
[info
.field
] = value
;
195 CRM
.api(info
.entity
, action
, params
, {
197 error: function (data
) {
198 editableSettings
.error
.call(this, info
.entity
, info
.field
, value
, data
);
200 success: function (data
) {
201 if ($i
.data('options')){
202 value
= $i
.data('options')[value
];
204 editableSettings
.success
.call(this, info
.entity
, info
.field
, value
, data
);
211 $.fn
.crmForm = function (options
) {
212 var settings
= $.extend( {
219 'load' : function (target
){},
220 'success' : function (result
) {
221 $(this).html(ts('Saved'));
226 return this.each(function() {
227 var formLoaded = function (target
) {
228 var $this =$(target
);
229 var destination
="<input type='hidden' name='civicrmDestination' value='"+CRM
.url('civicrm/ajax/rest',{
230 'sequential':settings
.sequential
,
232 'entity':settings
.entity
,
233 'action':settings
.action
,
236 $this.find('form').ajaxForm({
237 beforeSubmit :function () {
238 $this.html("<div class='crm-editable-saving'>Saving...</div>");
241 success:function(response
) {
242 if (response
.indexOf('crm-error') >= 0) { // we got an error, re-display the page
243 $this.html(response
);
246 if (response
[0] == '{')
247 settings
.success($.parseJSON (response
));
249 settings
.success(response
);
252 }).append('<input type="hidden" name="snippet" value="1"/>'+destination
).trigger('load');
254 settings
.load(target
);
258 if (settings
.source
&& settings
.source
.indexOf('snippet') == -1) {
259 if (settings
.source
.indexOf('?') >= 0)
260 settings
.source
= settings
.source
+ "&snippet=1";
262 settings
.source
= settings
.source
+ "?snippet=1";
266 $this.html ("Loading...");
268 $this.dialog({width
:'auto',minWidth
:600});
269 $this.load (settings
.source
,function (){formLoaded(this)});