1 (function($, _
, Backbone
) {
2 if (!CRM
.Designer
) CRM
.Designer
= {};
5 * When rendering a template with Backbone.Marionette.ItemView, the list of variables is determined by
6 * serializeData(). The normal behavior is to map each property of this.model to a template
9 * This function extends that practice by exporting variables "_view", "_model", "_collection",
10 * and "_options". This makes it easier for the template to, e.g., access computed properties of
11 * a model (by calling "_model.getComputedProperty"), or to access constructor options (by
12 * calling "_options.myoption").
16 var extendedSerializeData = function() {
17 var result
= Backbone
.Marionette
.ItemView
.prototype.serializeData
.apply(this);
19 result
._model
= this.model
;
20 result
._collection
= this.collection
;
21 result
._options
= this.options
;
26 * Display a dialog window with an editable form for a UFGroupModel
28 * The implementation here is very "jQuery-style" and not "Backbone-style";
32 * - model: CRM.UF.UFGroupModel
34 CRM
.Designer
.DesignerDialog
= Backbone
.Marionette
.Layout
.extend({
35 serializeData
: extendedSerializeData
,
36 template
: '#designer_dialog_template',
37 className
: 'crm-designer-dialog',
39 designerRegion
: '.crm-designer'
41 /** @var bool whether this dialog is currently open */
43 /** @var bool whether any changes have been made */
45 /** @var obj handle for the CRM.alert containing undo link */
47 /** @var bool whether this dialog is being re-opened by the undo link */
50 initialize: function(options
) {
51 CRM
.designerApp
.vent
.on('ufUnsaved', this.onUfChanged
, this);
52 CRM
.designerApp
.vent
.on('ufSaved', this.onUfSaved
, this);
55 if (this.undoAlert
&& this.undoAlert
.close
) this.undoAlert
.close();
56 CRM
.designerApp
.vent
.off('ufUnsaved', this.onUfChanged
, this);
58 onUfChanged: function(isUfUnsaved
) {
59 this.isUfUnsaved
= isUfUnsaved
;
61 onUfSaved: function() {
62 CRM
.designerApp
.vent
.off('ufUnsaved', this.onUfChanged
, this);
63 this.isUfUnsaved
= false;
65 onRender: function() {
66 var designerDialog
= this;
67 designerDialog
.$el
.dialog({
68 autoOpen
: true, // note: affects accordion height
69 title
: ts('Edit Profile'),
72 height
: parseInt($(window
).height() * 0.8, 10),
74 minHeight
: 600, // to allow dropping in big whitespace, coordinate with min-height of .crm-designer-fields
76 // Prevent conflicts with other onbeforeunload handlers
77 designerDialog
.oldOnBeforeUnload
= window
.onbeforeunload
;
78 // Warn of unsaved changes when navigating away from the page
79 window
.onbeforeunload = function() {
80 if (designerDialog
.isDialogOpen
&& designerDialog
.isUfUnsaved
) {
81 return ts("Your profile has not been saved.");
83 if (designerDialog
.oldOnBeforeUnload
) {
84 return designerDialog
.oldOnBeforeUnload
.apply(arguments
);
87 if (designerDialog
.undoAlert
&& designerDialog
.undoAlert
.close
) designerDialog
.undoAlert
.close();
88 designerDialog
.isDialogOpen
= true;
89 // Initialize new dialog if we are not re-opening unsaved changes
90 if (designerDialog
.undoState
=== false) {
91 if (designerDialog
.designerRegion
&& designerDialog
.designerRegion
.close
) designerDialog
.designerRegion
.close();
92 designerDialog
.$el
.block();
93 designerDialog
.options
.findCreateUfGroupModel({
94 onLoad: function(ufGroupModel
) {
95 designerDialog
.model
= ufGroupModel
;
96 var designerLayout
= new CRM
.Designer
.DesignerLayout({
98 el
: '<div class="full-height"></div>'
100 designerDialog
.$el
.unblock();
101 designerDialog
.designerRegion
.show(designerLayout
);
102 CRM
.designerApp
.vent
.trigger('resize');
103 designerDialog
.isUfUnsaved
= false;
107 designerDialog
.undoState
= false;
109 CRM
.designerApp
.DetachedProfiles
= [];
112 window
.onbeforeunload
= designerDialog
.oldOnBeforeUnload
;
113 designerDialog
.isDialogOpen
= false;
115 if (designerDialog
.undoAlert
&& designerDialog
.undoAlert
.close
) designerDialog
.undoAlert
.close();
116 if (designerDialog
.isUfUnsaved
) {
117 designerDialog
.undoAlert
= CRM
.alert('<p>' + ts('%1 has not been saved.', {1: designerDialog
.model
.get('title')}) + '</p><a href="#" class="crm-undo">' + ts('Restore') + '</a>', ts('Unsaved Changes'), 'alert', {expires
: 60000});
118 $('.ui-notify-message a.crm-undo').button({icons
: {primary
: 'fa-undo'}}).click(function(e
) {
120 designerDialog
.undoState
= true;
121 designerDialog
.$el
.dialog('open');
125 CRM
.designerApp
.restorePreviewArea();
128 CRM
.designerApp
.vent
.trigger('resize');
135 * Display a complete form-editing UI, including canvas, palette, and
139 * - model: CRM.UF.UFGroupModel
141 CRM
.Designer
.DesignerLayout
= Backbone
.Marionette
.Layout
.extend({
142 serializeData
: extendedSerializeData
,
143 template
: '#designer_template',
145 buttons
: '.crm-designer-buttonset-region',
146 palette
: '.crm-designer-palette-region',
147 form
: '.crm-designer-form-region',
148 fields
: '.crm-designer-fields-region'
150 initialize: function() {
151 CRM
.designerApp
.vent
.on('resize', this.onResize
, this);
153 onClose: function() {
154 CRM
.designerApp
.vent
.off('resize', this.onResize
, this);
156 onRender: function() {
157 this.buttons
.show(new CRM
.Designer
.ToolbarView({
160 this.palette
.show(new CRM
.Designer
.PaletteView({
163 this.form
.show(new CRM
.Designer
.UFGroupView({
166 this.fields
.show(new CRM
.Designer
.UFFieldCanvasView({
170 onResize: function() {
171 if (! this.hasResizedBefore
) {
172 this.hasResizedBefore
= true;
173 this.$('.crm-designer-toolbar').resizable({
177 resize: function(event
, ui
) {
178 $('.crm-designer-canvas').css('margin-right', (ui
.size
.width
+ 10) + 'px');
179 $(this).css({left
: '', height
: ''});
181 }).css({left
: '', height
: ''});
187 * Display toolbar with working button
190 * - model: CRM.UF.UFGroupModel
192 CRM
.Designer
.ToolbarView
= Backbone
.Marionette
.ItemView
.extend({
193 serializeData
: extendedSerializeData
,
194 template
: '#designer_buttons_template',
197 'click .crm-designer-save': 'doSave',
198 'click .crm-designer-preview': 'doPreview'
200 onRender: function() {
201 this.$('.crm-designer-save').button({icons
: {primary
: 'fa-check'}}).attr({
202 disabled
: 'disabled',
203 style
: 'opacity:.5; cursor:default;'
205 this.$('.crm-designer-preview').button({icons
: {primary
: 'fa-television'}});
207 initialize: function(options
) {
208 CRM
.designerApp
.vent
.on('ufUnsaved', this.onUfChanged
, this);
210 onUfChanged: function(isUfUnsaved
) {
212 this.$('.crm-designer-save').removeAttr('style').prop('disabled', false);
215 doSave: function(e
) {
217 var ufGroupModel
= this.model
;
218 if (ufGroupModel
.getRel('ufFieldCollection').hasDuplicates()) {
219 CRM
.alert(ts('Please correct errors before saving.'), '', 'alert');
222 var $dialog
= this.$el
.closest('.crm-designer-dialog'); // FIXME use events
224 var profile
= ufGroupModel
.toStrictJSON();
225 profile
["api.UFField.replace"] = {values
: ufGroupModel
.getRel('ufFieldCollection').toSortedJSON(), 'option.autoweight': 0};
226 CRM
.api('UFGroup', 'create', profile
, {
227 success: function(data
) {
231 CRM
.alert(data
.error_message
);
234 _
.each(data
.values
, function(ufGroupResponse
) {
235 if (ufGroupResponse
['api.UFField.replace'].is_error
) {
236 CRM
.alert(ufGroupResponse
['api.UFField.replace'].error_message
);
241 if (!ufGroupModel
.get('id')) {
242 ufGroupModel
.set('id', data
.id
);
244 CRM
.designerApp
.vent
.trigger('ufUnsaved', false);
245 CRM
.designerApp
.vent
.trigger('ufSaved');
246 $dialog
.dialog('close');
251 doPreview: function(e
) {
253 this.previewMode
= !this.previewMode
;
254 if (!this.previewMode
) {
255 $('.crm-designer-preview-canvas').html('');
256 $('.crm-designer-canvas > *, .crm-designer-palette-region').show();
257 $('.crm-designer-preview').button('option', {icons
: {primary
: 'fa-television'}}).find('span').text(ts('Preview'));
260 if (this.model
.getRel('ufFieldCollection').hasDuplicates()) {
261 CRM
.alert(ts('Please correct errors before previewing.'), '', 'alert');
264 var $dialog
= this.$el
.closest('.crm-designer-dialog'); // FIXME use events
267 CRM
.designerApp
.clearPreviewArea();
268 $.post(CRM
.url("civicrm/ajax/inline"), {
269 'qfKey': CRM
.profilePreviewKey
,
270 'class_name': 'CRM_UF_Form_Inline_Preview',
272 'ufData': JSON
.stringify({
273 ufGroup
: this.model
.toStrictJSON(),
274 ufFieldCollection
: this.model
.getRel('ufFieldCollection').toSortedJSON()
276 }).done(function(data
) {
278 $('.crm-designer-canvas > *, .crm-designer-palette-region').hide();
279 $('.crm-designer-preview-canvas').html(data
).show().trigger('crmLoad').find(':input').prop('readOnly', true);
280 $('.crm-designer-preview').button('option', {icons
: {primary
: 'fa-pencil'}}).find('span').text(ts('Edit'));
286 * Display a selection of available fields
289 * - model: CRM.UF.UFGroupModel
291 CRM
.Designer
.PaletteView
= Backbone
.Marionette
.ItemView
.extend({
292 serializeData
: extendedSerializeData
,
293 template
: '#palette_template',
294 el
: '<div class="full-height"></div>',
297 'keyup .crm-designer-palette-search input': 'doSearch',
298 'change .crm-contact-types': 'doSetPaletteEntity',
299 'click .crm-designer-palette-clear-search': 'clearSearch',
300 'click .crm-designer-palette-toggle': 'toggleAll',
301 'click .crm-designer-palette-add button': 'doNewCustomFieldDialog',
302 'click #crm-designer-add-custom-set': 'doNewCustomSetDialog',
303 'dblclick .crm-designer-palette-field': 'doAddToCanvas'
305 initialize: function() {
306 this.model
.getRel('ufFieldCollection')
307 .on('add', this.toggleActive
, this)
308 .on('remove', this.toggleActive
, this);
309 this.model
.getRel('paletteFieldCollection')
310 .on('reset', this.render
, this);
311 CRM
.designerApp
.vent
.on('resize', this.onResize
, this);
313 onClose: function() {
314 this.model
.getRel('ufFieldCollection')
315 .off('add', this.toggleActive
, this)
316 .off('remove', this.toggleActive
, this);
317 this.model
.getRel('paletteFieldCollection')
318 .off('reset', this.render
, this);
319 CRM
.designerApp
.vent
.off('resize', this.onResize
, this);
321 onRender: function() {
322 var paletteView
= this;
324 // Prepare data for jstree
326 var paletteFieldsByEntitySection
= this.model
.getRel('paletteFieldCollection').getFieldsByEntitySection();
328 paletteView
.model
.getRel('ufEntityCollection').each(function(ufEntityModel
){
329 _
.each(ufEntityModel
.getSections(), function(section
, sectionKey
){
330 var defaultValue
= paletteView
.selectedContactType
;
332 defaultValue
= paletteView
.model
.calculateContactEntityType();
335 // set selected option as default, since we are rebuilding palette
336 paletteView
.$('.crm-contact-types').val(defaultValue
).prop('selected','selected');
338 var entitySection
= ufEntityModel
.get('entity_name') + '-' + sectionKey
;
340 if (paletteFieldsByEntitySection
[entitySection
]) {
341 _
.each(paletteFieldsByEntitySection
[entitySection
], function(paletteFieldModel
, k
) {
342 items
.push({data
: paletteFieldModel
.getLabel(), attr
: {'class': 'crm-designer-palette-field', 'data-plm-cid': paletteFieldModel
.cid
}});
345 if (section
.is_addable
) {
346 items
.push({data
: ts('+ Add New Field'), attr
: {'class': 'crm-designer-palette-add'}});
348 if (items
.length
> 0) {
352 state
: _
.contains(paletteView
.openTreeNodes
, sectionKey
) ? 'open' : 'closed',
354 'class': 'crm-designer-palette-section',
355 'data-section': sectionKey
,
356 'data-entity': ufEntityModel
.get('entity_name')
363 this.$('.crm-designer-palette-tree').jstree({
364 'json_data': {data
: treeData
},
366 'case_insensitive' : true,
367 'show_only_matches': true
373 "url": CRM
.config
.packagesBase
+ 'jquery/plugins/jstree/themes/classic/style.css'
375 'plugins': ['themes', 'json_data', 'ui', 'search']
376 }).bind('loaded.jstree', function () {
377 $('.crm-designer-palette-field', this).draggable({
378 appendTo
: '.crm-designer',
379 zIndex
: $(this.$el
).css("zIndex") + 5000,
381 connectToSortable
: '.crm-designer-fields' // FIXME: tight canvas/palette coupling
383 paletteView
.model
.getRel('ufFieldCollection').each(function(ufFieldModel
) {
384 paletteView
.toggleActive(ufFieldModel
, paletteView
.model
.getRel('ufFieldCollection'));
386 paletteView
.$('.crm-designer-palette-add a').replaceWith('<button>' + $('.crm-designer-palette-add a').first().text() + '</<button>');
387 paletteView
.$('.crm-designer-palette-tree > ul').append('<li><button id="crm-designer-add-custom-set">+ ' + ts('Add Set of Custom Fields') + '</button></li>');
388 paletteView
.$('.crm-designer-palette-tree button').button();
389 }).bind("select_node.jstree", function (e
, data
) {
390 $(this).jstree("toggle_node", data
.rslt
.obj
);
391 $(this).jstree("deselect_node", data
.rslt
.obj
);
394 // FIXME: tight canvas/palette coupling
395 this.$(".crm-designer-fields").droppable({
396 activeClass
: "ui-state-default",
397 hoverClass
: "ui-state-hover",
398 accept
: ":not(.ui-sortable-helper)"
403 onResize: function() {
404 var pos
= this.$('.crm-designer-palette-tree').position();
405 var div
= this.$('.crm-designer-palette-tree').closest('.crm-container').height();
406 this.$('.crm-designer-palette-tree').css({height
: div
- pos
.top
});
408 doSearch: function(e
) {
409 var str
= $(e
.target
).val();
410 this.$('.crm-designer-palette-clear-search').css('visibility', str
? 'visible' : 'hidden');
411 this.$('.crm-designer-palette-tree').jstree("search", str
);
413 doSetPaletteEntity: function(event
) {
414 this.selectedContactType
= $('.crm-contact-types :selected').val();
415 // loop through entity collection and remove non-valid entity section's
416 var newUfEntityModels
= [];
417 this.model
.getRel('ufEntityCollection').each(function(oldUfEntityModel
){
418 var values
= oldUfEntityModel
.toJSON();
419 if (values
.entity_name
== 'contact_1') {
420 values
.entity_type
= $('.crm-contact-types :selected').val();
422 newUfEntityModels
.push(new CRM
.UF
.UFEntityModel(values
));
424 this.model
.getRel('ufEntityCollection').reset(newUfEntityModels
);
426 doAddToCanvas: function(event
) {
427 var paletteFieldModel
= this.model
.getRel('paletteFieldCollection').get($(event
.currentTarget
).attr('data-plm-cid'));
428 paletteFieldModel
.addToUFCollection(this.model
.getRel('ufFieldCollection'));
429 event
.stopPropagation();
431 doNewCustomFieldDialog: function(e
) {
433 var paletteView
= this;
434 var entityKey
= $(e
.currentTarget
).closest('.crm-designer-palette-section').attr('data-entity');
435 var sectionKey
= $(e
.currentTarget
).closest('.crm-designer-palette-section').attr('data-section');
436 var ufEntityModel
= paletteView
.model
.getRel('ufEntityCollection').getByName(entityKey
);
437 var sections
= ufEntityModel
.getSections();
438 var url
= CRM
.url('civicrm/admin/custom/group/field/add', {
441 gid
: sections
[sectionKey
].custom_group_id
443 CRM
.loadForm(url
).on('crmFormSuccess', function(e
, data
) {
444 paletteView
.doRefresh('custom_' + data
.id
);
447 doNewCustomSetDialog: function(e
) {
449 var paletteView
= this;
450 var url
= CRM
.url('civicrm/admin/custom/group', 'action=add&reset=1');
451 // Create custom field set and automatically go to next step (create fields) after save button is clicked.
452 CRM
.loadForm(url
, {refreshAction
: ['next']})
453 .on('crmFormSuccess', function(e
, data
) {
454 // When form switches to create custom field context, modify button behavior to only continue for "save and new"
455 if (data
.customField
) ($(this).data('civiCrmSnippet').options
.crmForm
.refreshAction
= ['next_new']);
456 paletteView
.doRefresh(data
.customField
? 'custom_' + data
.id
: null);
459 doRefresh: function(fieldToAdd
) {
460 var ufGroupModel
= this.model
;
461 this.getOpenTreeNodes();
462 CRM
.Schema
.reloadModels()
463 .done(function(data
){
464 ufGroupModel
.resetEntities();
466 var field
= ufGroupModel
.getRel('paletteFieldCollection').getFieldByName(null, fieldToAdd
);
467 field
.addToUFCollection(ufGroupModel
.getRel('ufFieldCollection'));
471 CRM
.alert(ts('Failed to retrieve schema'), ts('Error'), 'error');
474 clearSearch: function(e
) {
476 $('.crm-designer-palette-search input').val('').keyup();
478 toggleActive: function(ufFieldModel
, ufFieldCollection
, options
) {
479 var paletteFieldCollection
= this.model
.getRel('paletteFieldCollection');
480 var paletteFieldModel
= paletteFieldCollection
.getFieldByName(ufFieldModel
.get('entity_name'), ufFieldModel
.get('field_name'));
481 var isAddable
= ufFieldCollection
.isAddable(ufFieldModel
);
482 if (paletteFieldModel
) {
483 this.$('[data-plm-cid='+paletteFieldModel
.cid
+']').toggleClass('disabled', !isAddable
);
486 toggleAll: function(e
) {
487 if (_
.isEmpty($('.crm-designer-palette-search input').val())) {
488 $('.crm-designer-palette-tree').jstree($(e
.target
).attr('rel'));
492 getOpenTreeNodes: function() {
493 var paletteView
= this;
494 this.openTreeNodes
= [];
495 this.$('.crm-designer-palette-section.jstree-open').each(function() {
496 paletteView
.openTreeNodes
.push($(this).data('section'));
502 * Display all UFFieldModel objects in a UFGroupModel.
505 * - model: CRM.UF.UFGroupModel
507 CRM
.Designer
.UFFieldCanvasView
= Backbone
.Marionette
.View
.extend({
508 initialize: function() {
509 this.model
.getRel('ufFieldCollection')
510 .on('add', this.updatePlaceholder
, this)
511 .on('remove', this.updatePlaceholder
, this)
512 .on('add', this.addUFFieldView
, this)
513 .on('reset', this.render
, this);
515 onClose: function() {
516 this.model
.getRel('ufFieldCollection')
517 .off('add', this.updatePlaceholder
, this)
518 .off('remove', this.updatePlaceholder
, this)
519 .off('add', this.addUFFieldView
, this)
520 .off('reset', this.render
, this);
523 var ufFieldCanvasView
= this;
524 this.$el
.html(_
.template($('#field_canvas_view_template').html()));
526 // BOTTOM: Setup field-level editing
527 var $fields
= this.$('.crm-designer-fields');
528 this.updatePlaceholder();
529 var ufFieldModels
= this.model
.getRel('ufFieldCollection').sortBy(function(ufFieldModel
) {
530 return parseInt(ufFieldModel
.get('weight'));
532 _
.each(ufFieldModels
, function(ufFieldModel
) {
533 ufFieldCanvasView
.addUFFieldView(ufFieldModel
, ufFieldCanvasView
.model
.getRel('ufFieldCollection'), {skipWeights
: true});
535 this.$(".crm-designer-fields").sortable({
536 placeholder
: 'crm-designer-row-placeholder',
537 forcePlaceholderSize
: true,
538 cancel
: 'input,textarea,button,select,option,a,.crm-designer-open',
539 receive: function(event
, ui
) {
540 var paletteFieldModel
= ufFieldCanvasView
.model
.getRel('paletteFieldCollection').get(ui
.item
.attr('data-plm-cid'));
541 var ufFieldModel
= paletteFieldModel
.addToUFCollection(
542 ufFieldCanvasView
.model
.getRel('ufFieldCollection'),
545 if (_
.isEmpty(ufFieldModel
)) {
546 ufFieldCanvasView
.$('.crm-designer-fields .ui-draggable').remove();
548 // Move from end to the 'dropped' position
549 var ufFieldViewEl
= ufFieldCanvasView
.$('div[data-field-cid='+ufFieldModel
.cid
+']').parent();
550 ufFieldCanvasView
.$('.crm-designer-fields .ui-draggable').replaceWith(ufFieldViewEl
);
552 // note: the sortable() update callback will call updateWeight
555 ufFieldCanvasView
.updateWeights();
559 /** Determine visual order of fields and set the model values for "weight" */
560 updateWeights: function() {
561 var ufFieldCanvasView
= this;
563 var rows
= this.$('.crm-designer-row').each(function(key
, row
) {
564 if ($(row
).hasClass('placeholder')) {
567 var ufFieldCid
= $(row
).attr('data-field-cid');
568 var ufFieldModel
= ufFieldCanvasView
.model
.getRel('ufFieldCollection').get(ufFieldCid
);
569 ufFieldModel
.set('weight', weight
);
573 addUFFieldView: function(ufFieldModel
, ufFieldCollection
, options
) {
574 var paletteFieldModel
= this.model
.getRel('paletteFieldCollection').getFieldByName(ufFieldModel
.get('entity_name'), ufFieldModel
.get('field_name'));
575 var ufFieldView
= new CRM
.Designer
.UFFieldView({
576 el
: $("<div></div>"),
578 paletteFieldModel
: paletteFieldModel
580 ufFieldView
.render();
581 this.$('.crm-designer-fields').append(ufFieldView
.$el
);
582 if (! (options
&& options
.skipWeights
)) {
583 this.updateWeights();
586 updatePlaceholder: function() {
587 if (this.model
.getRel('ufFieldCollection').isEmpty()) {
588 this.$('.placeholder').css({display
: 'block', border
: '0 none', cursor
: 'default'});
590 this.$('.placeholder').hide();
597 * - model: CRM.UF.UFFieldModel
598 * - paletteFieldModel: CRM.Designer.PaletteFieldModel
600 CRM
.Designer
.UFFieldView
= Backbone
.Marionette
.Layout
.extend({
601 serializeData
: extendedSerializeData
,
602 template
: '#field_row_template',
605 summary
: '.crm-designer-field-summary',
606 detail
: '.crm-designer-field-detail'
609 "click .crm-designer-action-settings": 'doToggleForm',
610 "click button.crm-designer-edit-custom": 'doEditCustomField',
611 "click .crm-designer-action-remove": 'doRemove'
615 "change:is_duplicate": 'onChangeIsDuplicate'
617 onRender: function() {
618 this.summary
.show(new CRM
.Designer
.UFFieldSummaryView({
620 fieldSchema
: this.model
.getFieldSchema(),
621 paletteFieldModel
: this.options
.paletteFieldModel
623 this.detail
.show(new CRM
.Designer
.UFFieldDetailView({
625 fieldSchema
: this.model
.getFieldSchema()
627 this.onChangeIsDuplicate(this.model
, this.model
.get('is_duplicate'));
628 if (!this.expanded
) {
629 this.detail
.$el
.hide();
632 CRM
.designerApp
.vent
.on('formOpened', function(event
) {
633 if (that
.expanded
&& event
!= that
.cid
) {
634 that
.doToggleForm(false);
638 doToggleForm: function(event
) {
639 this.expanded
= !this.expanded
;
640 if (this.expanded
&& event
!== false) {
641 CRM
.designerApp
.vent
.trigger('formOpened', this.cid
);
643 this.$el
.toggleClass('crm-designer-open', this.expanded
);
644 var $detail
= this.detail
.$el
;
645 if (!this.expanded
) {
646 $detail
.toggle('blind', 250);
647 this.$('button.crm-designer-edit-custom').remove();
650 var $canvas
= $('.crm-designer-canvas');
651 var top
= $canvas
.offset().top
;
654 step: function(num
, effect
) {
655 // Scroll canvas to keep field details visible
656 if (effect
.prop
== 'height') {
657 if (effect
.now
+ $detail
.offset().top
- top
> $canvas
.height() - 9) {
658 $canvas
.scrollTop($canvas
.scrollTop() + effect
.now
+ $detail
.offset().top
- top
- $canvas
.height() + 9);
663 if (this.model
.get('field_name').split('_')[0] == 'custom') {
664 this.$('.crm-designer-field-summary > div').append('<button class="crm-designer-edit-custom">' + ts('Edit Custom Field') + '</button>');
665 this.$('button.crm-designer-edit-custom').button({icons
: {primary
: 'fa-pencil'}}).attr('title', ts('Edit global settings for this custom field.'));
669 doEditCustomField: function(e
) {
671 var url
= CRM
.url('civicrm/admin/custom/group/field/update', {
674 id
: this.model
.get('field_name').split('_')[1]
676 var form1
= CRM
.loadForm(url
)
677 .on('crmFormLoad', function() {
678 $(this).prepend('<div class="messages status"><i class="crm-i fa-info-circle" aria-hidden="true"></i> ' + ts('Note: This will modify the field system-wide, not just in this profile form.') + '</div>');
681 onChangeIsDuplicate: function(model
, value
, options
) {
682 this.$el
.toggleClass('crm-designer-duplicate', value
);
684 doRemove: function(event
) {
686 this.$el
.hide(250, function() {
687 that
.model
.destroyLocal();
694 * - model: CRM.UF.UFFieldModel
695 * - fieldSchema: (Backbone.Form schema element)
696 * - paletteFieldModel: CRM.Designer.PaletteFieldModel
698 CRM
.Designer
.UFFieldSummaryView
= Backbone
.Marionette
.ItemView
.extend({
699 serializeData
: extendedSerializeData
,
700 template
: '#field_summary_template',
706 * Compose a printable string which describes the binding of this UFField to the data model
709 getBindingLabel: function() {
710 var result
= this.options
.paletteFieldModel
.getSection().title
+ ": " + this.options
.paletteFieldModel
.getLabel();
711 if (this.options
.fieldSchema
.civiIsPhone
) {
712 result
= result
+ '-' + CRM
.PseudoConstant
.phoneType
[this.model
.get('phone_type_id')];
714 if (this.options
.fieldSchema
.civiIsWebsite
) {
715 result
= result
+ '-' + CRM
.PseudoConstant
.websiteType
[this.model
.get('website_type_id')];
717 if (this.options
.fieldSchema
.civiIsLocation
) {
718 var locType
= this.model
.get('location_type_id') ? CRM
.PseudoConstant
.locationType
[this.model
.get('location_type_id')] : ts('Primary');
719 result
= result
+ ' (' + locType
+ ')';
725 * Return a string marking if the field is required
728 getRequiredMarker: function() {
729 if (this.model
.get('is_required') == 1) {
730 return ' <span class="crm-marker">*</span> ';
735 onRender: function() {
736 this.$el
.toggleClass('disabled', this.model
.get('is_active') != 1);
737 if (this.model
.get("is_reserved") == 1) {
738 this.$('.crm-designer-buttons').hide();
745 * - model: CRM.UF.UFFieldModel
746 * - fieldSchema: (Backbone.Form schema element)
748 CRM
.Designer
.UFFieldDetailView
= Backbone
.View
.extend({
749 initialize: function() {
750 // FIXME: hide/display 'in_selector' if 'visibility' is one of the public options
751 var fields
= ['location_type_id', 'website_type_id', 'phone_type_id', 'label', 'is_multi_summary', 'is_required', 'is_view', 'visibility', 'in_selector', 'is_searchable', 'help_pre', 'help_post', 'is_active'];
752 if (! this.options
.fieldSchema
.civiIsLocation
) {
753 fields
= _
.without(fields
, 'location_type_id');
755 if (! this.options
.fieldSchema
.civiIsWebsite
) {
756 fields
= _
.without(fields
, 'website_type_id');
758 if (! this.options
.fieldSchema
.civiIsPhone
) {
759 fields
= _
.without(fields
, 'phone_type_id');
761 if (!this.options
.fieldSchema
.civiIsMultiple
) {
762 fields
= _
.without(fields
, 'is_multi_summary');
764 if (this.options
.fieldSchema
.type
== 'Markup') {
765 fields
= _
.without(fields
, 'is_required', 'is_view', 'visibility', 'in_selector', 'is_searchable', 'help_post');
768 this.form
= new Backbone
.Form({
772 this.form
.on('change', this.onFormChange
, this);
773 this.model
.on('change', this.onModelChange
, this);
776 this.$el
.html(this.form
.render().el
);
779 onModelChange: function() {
780 $.each(this.form
.fields
, function(i
, field
) {
781 this.form
.setValue(field
.key
, this.model
.get(field
.key
));
784 onFormChange: function() {
786 this.$('.field-is_multi_summary').toggle(this.options
.fieldSchema
.civiIsMultiple
? true : false);
787 this.$('.field-in_selector').toggle(this.model
.isInSelectorAllowed());
789 if (!this.model
.isInSelectorAllowed() && this.model
.get('in_selector') != "0") {
790 this.model
.set('in_selector', "0");
791 if (this.form
.fields
.in_selector
) {
792 this.form
.setValue('in_selector', "0");
794 // TODO: It might be nicer if we didn't completely discard in_selector -- e.g.
795 // if the value could be restored when the user isInSelectorAllowed becomes true
796 // again. However, I haven't found a simple way to do this.
803 * - model: CRM.UF.UFGroupModel
805 CRM
.Designer
.UFGroupView
= Backbone
.Marionette
.Layout
.extend({
806 serializeData
: extendedSerializeData
,
807 template
: '#form_row_template',
810 summary
: '.crm-designer-form-summary',
811 detail
: '.crm-designer-form-detail'
814 "click .crm-designer-action-settings": 'doToggleForm'
816 onRender: function() {
817 this.summary
.show(new CRM
.Designer
.UFGroupSummaryView({
820 this.detail
.show(new CRM
.Designer
.UFGroupDetailView({
823 if (!this.expanded
) {
824 this.detail
.$el
.hide();
827 CRM
.designerApp
.vent
.on('formOpened', function(event
) {
828 if (that
.expanded
&& event
!== 0) {
829 that
.doToggleForm(false);
833 doToggleForm: function(event
) {
834 this.expanded
= !this.expanded
;
835 if (this.expanded
&& event
!== false) {
836 CRM
.designerApp
.vent
.trigger('formOpened', 0);
838 this.$el
.toggleClass('crm-designer-open', this.expanded
);
839 this.detail
.$el
.toggle('blind', 250);
845 * - model: CRM.UF.UFGroupModel
847 CRM
.Designer
.UFGroupSummaryView
= Backbone
.Marionette
.ItemView
.extend({
848 serializeData
: extendedSerializeData
,
849 template
: '#form_summary_template',
853 onRender: function() {
854 this.$el
.toggleClass('disabled', this.model
.get('is_active') != 1);
855 if (this.model
.get("is_reserved") == 1) {
856 this.$('.crm-designer-buttons').hide();
863 * - model: CRM.UF.UFGroupModel
865 CRM
.Designer
.UFGroupDetailView
= Backbone
.View
.extend({
866 initialize: function() {
867 this.form
= new Backbone
.Form({
869 fields
: ['title', 'frontend_title', 'help_pre', 'help_post', 'is_active']
871 this.form
.on('change', this.form
.commit
, this.form
);
874 this.$el
.html(this.form
.render().el
);
878 })(CRM
.$, CRM
._
, CRM
.BB
);