commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / js / view / crm.designer.js
1 (function($, _) {
2 if (!CRM.Designer) CRM.Designer = {};
3
4 /**
5 * When rendering a template with 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
7 * variable.
8 *
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").
13 *
14 * @return {*}
15 */
16 var extendedSerializeData = function() {
17 var result = Marionette.ItemView.prototype.serializeData.apply(this);
18 result._view = this;
19 result._model = this.model;
20 result._collection = this.collection;
21 result._options = this.options;
22 return result;
23 };
24
25 /**
26 * Display a dialog window with an editable form for a UFGroupModel
27 *
28 * The implementation here is very "jQuery-style" and not "Backbone-style";
29 * it's been extracted
30 *
31 * options:
32 * - model: CRM.UF.UFGroupModel
33 */
34 CRM.Designer.DesignerDialog = Backbone.Marionette.Layout.extend({
35 serializeData: extendedSerializeData,
36 template: '#designer_dialog_template',
37 className: 'crm-designer-dialog',
38 regions: {
39 designerRegion: '.crm-designer'
40 },
41 /** @var bool whether this dialog is currently open */
42 isDialogOpen: false,
43 /** @var bool whether any changes have been made */
44 isUfUnsaved: false,
45 /** @var obj handle for the CRM.alert containing undo link */
46 undoAlert: null,
47 /** @var bool whether this dialog is being re-opened by the undo link */
48 undoState: false,
49
50 initialize: function(options) {
51 CRM.designerApp.vent.on('ufUnsaved', this.onUfChanged, this);
52 CRM.designerApp.vent.on('ufSaved', this.onUfSaved, this);
53 },
54 onClose: function() {
55 if (this.undoAlert && this.undoAlert.close) this.undoAlert.close();
56 CRM.designerApp.vent.off('ufUnsaved', this.onUfChanged, this);
57 },
58 onUfChanged: function(isUfUnsaved) {
59 this.isUfUnsaved = isUfUnsaved;
60 },
61 onUfSaved: function() {
62 CRM.designerApp.vent.off('ufUnsaved', this.onUfChanged, this);
63 this.isUfUnsaved = false;
64 },
65 onRender: function() {
66 var designerDialog = this;
67 designerDialog.$el.dialog({
68 autoOpen: true, // note: affects accordion height
69 title: ts('Edit Profile'),
70 modal: true,
71 width: '75%',
72 height: parseInt($(window).height() * 0.8, 10),
73 minWidth: 500,
74 minHeight: 600, // to allow dropping in big whitespace, coordinate with min-height of .crm-designer-fields
75 open: function() {
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.");
82 }
83 if (designerDialog.oldOnBeforeUnload) {
84 return designerDialog.oldOnBeforeUnload.apply(arguments);
85 }
86 };
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({
97 model: ufGroupModel,
98 el: '<div class="full-height"></div>'
99 });
100 designerDialog.$el.unblock();
101 designerDialog.designerRegion.show(designerLayout);
102 CRM.designerApp.vent.trigger('resize');
103 designerDialog.isUfUnsaved = false;
104 }
105 });
106 }
107 designerDialog.undoState = false;
108 // CRM-12188
109 CRM.designerApp.DetachedProfiles = [];
110 },
111 close: function() {
112 window.onbeforeunload = designerDialog.oldOnBeforeUnload;
113 designerDialog.isDialogOpen = false;
114
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: 'ui-icon-arrowreturnthick-1-w'}}).click(function(e) {
119 e.preventDefault();
120 designerDialog.undoState = true;
121 designerDialog.$el.dialog('open');
122 });
123 }
124 // CRM-12188
125 CRM.designerApp.restorePreviewArea();
126 },
127 resize: function() {
128 CRM.designerApp.vent.trigger('resize');
129 }
130 });
131 }
132 });
133
134 /**
135 * Display a complete form-editing UI, including canvas, palette, and
136 * buttons.
137 *
138 * options:
139 * - model: CRM.UF.UFGroupModel
140 */
141 CRM.Designer.DesignerLayout = Backbone.Marionette.Layout.extend({
142 serializeData: extendedSerializeData,
143 template: '#designer_template',
144 regions: {
145 buttons: '.crm-designer-buttonset-region',
146 palette: '.crm-designer-palette-region',
147 form: '.crm-designer-form-region',
148 fields: '.crm-designer-fields-region'
149 },
150 initialize: function() {
151 CRM.designerApp.vent.on('resize', this.onResize, this);
152 },
153 onClose: function() {
154 CRM.designerApp.vent.off('resize', this.onResize, this);
155 },
156 onRender: function() {
157 this.buttons.show(new CRM.Designer.ToolbarView({
158 model: this.model
159 }));
160 this.palette.show(new CRM.Designer.PaletteView({
161 model: this.model
162 }));
163 this.form.show(new CRM.Designer.UFGroupView({
164 model: this.model
165 }));
166 this.fields.show(new CRM.Designer.UFFieldCanvasView({
167 model: this.model
168 }));
169 },
170 onResize: function() {
171 if (! this.hasResizedBefore) {
172 this.hasResizedBefore = true;
173 this.$('.crm-designer-toolbar').resizable({
174 handles: 'w',
175 maxWidth: 400,
176 minWidth: 150,
177 resize: function(event, ui) {
178 $('.crm-designer-canvas').css('margin-right', (ui.size.width + 10) + 'px');
179 $(this).css({left: '', height: ''});
180 }
181 }).css({left: '', height: ''});
182 }
183 }
184 });
185
186 /**
187 * Display toolbar with working button
188 *
189 * options:
190 * - model: CRM.UF.UFGroupModel
191 */
192 CRM.Designer.ToolbarView = Backbone.Marionette.ItemView.extend({
193 serializeData: extendedSerializeData,
194 template: '#designer_buttons_template',
195 previewMode: false,
196 events: {
197 'click .crm-designer-save': 'doSave',
198 'click .crm-designer-preview': 'doPreview'
199 },
200 onRender: function() {
201 this.$('.crm-designer-save').button({icons: {primary: 'ui-icon-check'}}).attr({
202 disabled: 'disabled',
203 style: 'opacity:.5; cursor:default;'
204 });
205 this.$('.crm-designer-preview').button({icons: {primary: 'ui-icon-search'}});
206 },
207 initialize: function(options) {
208 CRM.designerApp.vent.on('ufUnsaved', this.onUfChanged, this);
209 },
210 onUfChanged: function(isUfUnsaved) {
211 if (isUfUnsaved) {
212 this.$('.crm-designer-save').removeAttr('style').prop('disabled', false);
213 }
214 },
215 doSave: function(e) {
216 e.preventDefault();
217 var ufGroupModel = this.model;
218 if (ufGroupModel.getRel('ufFieldCollection').hasDuplicates()) {
219 CRM.alert(ts('Please correct errors before saving.'), '', 'alert');
220 return;
221 }
222 var $dialog = this.$el.closest('.crm-designer-dialog'); // FIXME use events
223 $dialog.block();
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) {
228 $dialog.unblock();
229 var error = false;
230 if (data.is_error) {
231 CRM.alert(data.error_message);
232 error = true;
233 }
234 _.each(data.values, function(ufGroupResponse) {
235 if (ufGroupResponse['api.UFField.replace'].is_error) {
236 CRM.alert(ufGroupResponse['api.UFField.replace'].error_message);
237 error = true;
238 }
239 });
240 if (!error) {
241 if (!ufGroupModel.get('id')) {
242 ufGroupModel.set('id', data.id);
243 }
244 CRM.designerApp.vent.trigger('ufUnsaved', false);
245 CRM.designerApp.vent.trigger('ufSaved');
246 $dialog.dialog('close');
247 }
248 }
249 });
250 },
251 doPreview: function(e) {
252 e.preventDefault();
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: 'ui-icon-search'}}).find('span').text(ts('Preview'));
258 return;
259 }
260 if (this.model.getRel('ufFieldCollection').hasDuplicates()) {
261 CRM.alert(ts('Please correct errors before previewing.'), '', 'alert');
262 return;
263 }
264 var $dialog = this.$el.closest('.crm-designer-dialog'); // FIXME use events
265 $dialog.block();
266 // CRM-12188
267 CRM.designerApp.clearPreviewArea();
268 $.post(CRM.url("civicrm/ajax/inline"), {
269 'qfKey': CRM.profilePreviewKey,
270 'class_name': 'CRM_UF_Form_Inline_Preview',
271 'snippet': 1,
272 'ufData': JSON.stringify({
273 ufGroup: this.model.toStrictJSON(),
274 ufFieldCollection: this.model.getRel('ufFieldCollection').toSortedJSON()
275 })
276 }).done(function(data) {
277 $dialog.unblock();
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: 'ui-icon-pencil'}}).find('span').text(ts('Edit'));
281 });
282 }
283 });
284
285 /**
286 * Display a selection of available fields
287 *
288 * options:
289 * - model: CRM.UF.UFGroupModel
290 */
291 CRM.Designer.PaletteView = Backbone.Marionette.ItemView.extend({
292 serializeData: extendedSerializeData,
293 template: '#palette_template',
294 el: '<div class="full-height"></div>',
295 openTreeNodes: [],
296 events: {
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'
304 },
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);
312 },
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);
320 },
321 onRender: function() {
322 var paletteView = this;
323
324 // Prepare data for jstree
325 var treeData = [];
326 var paletteFieldsByEntitySection = this.model.getRel('paletteFieldCollection').getFieldsByEntitySection();
327
328 paletteView.model.getRel('ufEntityCollection').each(function(ufEntityModel){
329 _.each(ufEntityModel.getSections(), function(section, sectionKey){
330 var defaultValue = paletteView.selectedContactType;
331 if (!defaultValue) {
332 defaultValue = paletteView.model.calculateContactEntityType();
333 }
334
335 // set selected option as default, since we are rebuilding palette
336 paletteView.$('.crm-contact-types').val(defaultValue).prop('selected','selected');
337
338 var entitySection = ufEntityModel.get('entity_name') + '-' + sectionKey;
339 var items = [];
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}});
343 });
344 }
345 if (section.is_addable) {
346 items.push({data: ts('+ Add New Field'), attr: {'class': 'crm-designer-palette-add'}});
347 }
348 if (items.length > 0) {
349 treeData.push({
350 data: section.title,
351 children: items,
352 state: _.contains(paletteView.openTreeNodes, sectionKey) ? 'open' : 'closed',
353 attr: {
354 'class': 'crm-designer-palette-section',
355 'data-section': sectionKey,
356 'data-entity': ufEntityModel.get('entity_name')
357 }
358 });
359 }
360 });
361 });
362
363 this.$('.crm-designer-palette-tree').jstree({
364 'json_data': {data: treeData},
365 'search': {
366 'case_insensitive' : true,
367 'show_only_matches': true
368 },
369 themes: {
370 "theme": 'classic',
371 "dots": false,
372 "icons": false,
373 "url": CRM.config.resourceBase + 'packages/jquery/plugins/jstree/themes/classic/style.css'
374 },
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).zIndex() + 5000,
380 helper: 'clone',
381 connectToSortable: '.crm-designer-fields' // FIXME: tight canvas/palette coupling
382 });
383 paletteView.model.getRel('ufFieldCollection').each(function(ufFieldModel) {
384 paletteView.toggleActive(ufFieldModel, paletteView.model.getRel('ufFieldCollection'));
385 });
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);
392 });
393
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)"
399 });
400
401 this.onResize();
402 },
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});
407 },
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);
412 },
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();
421 }
422 newUfEntityModels.push(new CRM.UF.UFEntityModel(values));
423 });
424 this.model.getRel('ufEntityCollection').reset(newUfEntityModels);
425 },
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();
430 },
431 doNewCustomFieldDialog: function(e) {
432 e.preventDefault();
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', {
439 reset: 1,
440 action: 'add',
441 gid: sections[sectionKey].custom_group_id
442 });
443 CRM.loadForm(url).on('crmFormSuccess', function(e, data) {
444 paletteView.doRefresh('custom_' + data.id);
445 });
446 },
447 doNewCustomSetDialog: function(e) {
448 e.preventDefault();
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);
457 });
458 },
459 doRefresh: function(fieldToAdd) {
460 var ufGroupModel = this.model;
461 this.getOpenTreeNodes();
462 CRM.Schema.reloadModels()
463 .done(function(data){
464 ufGroupModel.resetEntities();
465 if (fieldToAdd) {
466 var field = ufGroupModel.getRel('paletteFieldCollection').getFieldByName(null, fieldToAdd);
467 field.addToUFCollection(ufGroupModel.getRel('ufFieldCollection'));
468 }
469 })
470 .fail(function() {
471 CRM.alert(ts('Failed to retrieve schema'), ts('Error'), 'error');
472 });
473 },
474 clearSearch: function(e) {
475 e.preventDefault();
476 $('.crm-designer-palette-search input').val('').keyup();
477 },
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);
484 }
485 },
486 toggleAll: function(e) {
487 if (_.isEmpty($('.crm-designer-palette-search input').val())) {
488 $('.crm-designer-palette-tree').jstree($(e.target).attr('rel'));
489 }
490 e.preventDefault();
491 },
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'));
497 });
498 }
499 });
500
501 /**
502 * Display all UFFieldModel objects in a UFGroupModel.
503 *
504 * options:
505 * - model: CRM.UF.UFGroupModel
506 */
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);
514 },
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);
521 },
522 render: function() {
523 var ufFieldCanvasView = this;
524 this.$el.html(_.template($('#field_canvas_view_template').html()));
525
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'));
531 });
532 _.each(ufFieldModels, function(ufFieldModel) {
533 ufFieldCanvasView.addUFFieldView(ufFieldModel, ufFieldCanvasView.model.getRel('ufFieldCollection'), {skipWeights: true});
534 });
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'),
543 {skipWeights: true}
544 );
545 if (_.isEmpty(ufFieldModel)) {
546 ufFieldCanvasView.$('.crm-designer-fields .ui-draggable').remove();
547 } else {
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);
551 }
552 // note: the sortable() update callback will call updateWeight
553 },
554 update: function() {
555 ufFieldCanvasView.updateWeights();
556 }
557 });
558 },
559 /** Determine visual order of fields and set the model values for "weight" */
560 updateWeights: function() {
561 var ufFieldCanvasView = this;
562 var weight = 1;
563 var rows = this.$('.crm-designer-row').each(function(key, row) {
564 if ($(row).hasClass('placeholder')) {
565 return;
566 }
567 var ufFieldCid = $(row).attr('data-field-cid');
568 var ufFieldModel = ufFieldCanvasView.model.getRel('ufFieldCollection').get(ufFieldCid);
569 ufFieldModel.set('weight', weight);
570 weight++;
571 });
572 },
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>"),
577 model: ufFieldModel,
578 paletteFieldModel: paletteFieldModel
579 });
580 ufFieldView.render();
581 this.$('.crm-designer-fields').append(ufFieldView.$el);
582 if (! (options && options.skipWeights)) {
583 this.updateWeights();
584 }
585 },
586 updatePlaceholder: function() {
587 if (this.model.getRel('ufFieldCollection').isEmpty()) {
588 this.$('.placeholder').css({display: 'block', border: '0 none', cursor: 'default'});
589 } else {
590 this.$('.placeholder').hide();
591 }
592 }
593 });
594
595 /**
596 * options:
597 * - model: CRM.UF.UFFieldModel
598 * - paletteFieldModel: CRM.Designer.PaletteFieldModel
599 */
600 CRM.Designer.UFFieldView = Backbone.Marionette.Layout.extend({
601 serializeData: extendedSerializeData,
602 template: '#field_row_template',
603 expanded: false,
604 regions: {
605 summary: '.crm-designer-field-summary',
606 detail: '.crm-designer-field-detail'
607 },
608 events: {
609 "click .crm-designer-action-settings": 'doToggleForm',
610 "click button.crm-designer-edit-custom": 'doEditCustomField',
611 "click .crm-designer-action-remove": 'doRemove'
612 },
613 modelEvents: {
614 "destroy": 'remove',
615 "change:is_duplicate": 'onChangeIsDuplicate'
616 },
617 onRender: function() {
618 this.summary.show(new CRM.Designer.UFFieldSummaryView({
619 model: this.model,
620 fieldSchema: this.model.getFieldSchema(),
621 paletteFieldModel: this.options.paletteFieldModel
622 }));
623 this.detail.show(new CRM.Designer.UFFieldDetailView({
624 model: this.model,
625 fieldSchema: this.model.getFieldSchema()
626 }));
627 this.onChangeIsDuplicate(this.model, this.model.get('is_duplicate'));
628 if (!this.expanded) {
629 this.detail.$el.hide();
630 }
631 var that = this;
632 CRM.designerApp.vent.on('formOpened', function(event) {
633 if (that.expanded && event != that.cid) {
634 that.doToggleForm(false);
635 }
636 });
637 },
638 doToggleForm: function(event) {
639 this.expanded = !this.expanded;
640 if (this.expanded && event !== false) {
641 CRM.designerApp.vent.trigger('formOpened', this.cid);
642 }
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();
648 }
649 else {
650 var $canvas = $('.crm-designer-canvas');
651 var top = $canvas.offset().top;
652 $detail.slideDown({
653 duration: 250,
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);
659 }
660 }
661 }
662 });
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: 'ui-icon-pencil'}}).attr('title', ts('Edit global settings for this custom field.'));
666 }
667 }
668 },
669 doEditCustomField: function(e) {
670 e.preventDefault();
671 var url = CRM.url('civicrm/admin/custom/group/field/update', {
672 action: 'update',
673 reset: 1,
674 id: this.model.get('field_name').split('_')[1]
675 });
676 var form1 = CRM.loadForm(url)
677 .on('crmFormLoad', function() {
678 $(this).prepend('<div class="messages status"><div class="icon inform-icon"></div>' + ts('Note: This will modify the field system-wide, not just in this profile form.') + '</div>');
679 });
680 },
681 onChangeIsDuplicate: function(model, value, options) {
682 this.$el.toggleClass('crm-designer-duplicate', value);
683 },
684 doRemove: function(event) {
685 var that = this;
686 this.$el.hide(250, function() {
687 that.model.destroyLocal();
688 });
689 }
690 });
691
692 /**
693 * options:
694 * - model: CRM.UF.UFFieldModel
695 * - fieldSchema: (Backbone.Form schema element)
696 * - paletteFieldModel: CRM.Designer.PaletteFieldModel
697 */
698 CRM.Designer.UFFieldSummaryView = Backbone.Marionette.ItemView.extend({
699 serializeData: extendedSerializeData,
700 template: '#field_summary_template',
701 modelEvents: {
702 'change': 'render'
703 },
704
705 /**
706 * Compose a printable string which describes the binding of this UFField to the data model
707 * @return {String}
708 */
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')];
713 }
714 if (this.options.fieldSchema.civiIsWebsite) {
715 result = result + '-' + CRM.PseudoConstant.websiteType[this.model.get('website_type_id')];
716 }
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 + ')';
720 }
721 return result;
722 },
723
724 /**
725 * Return a string marking if the field is required
726 * @return {String}
727 */
728 getRequiredMarker: function() {
729 if (this.model.get('is_required') == 1) {
730 return ' <span class="crm-marker">*</span> ';
731 }
732 return '';
733 },
734
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();
739 }
740 }
741 });
742
743 /**
744 * options:
745 * - model: CRM.UF.UFFieldModel
746 * - fieldSchema: (Backbone.Form schema element)
747 */
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');
754 }
755 if (! this.options.fieldSchema.civiIsWebsite) {
756 fields = _.without(fields, 'website_type_id');
757 }
758 if (! this.options.fieldSchema.civiIsPhone) {
759 fields = _.without(fields, 'phone_type_id');
760 }
761 if (!this.options.fieldSchema.civiIsMultiple) {
762 fields = _.without(fields, 'is_multi_summary');
763 }
764 if (this.options.fieldSchema.type == 'Markup') {
765 fields = _.without(fields, 'is_required', 'is_view', 'visibility', 'in_selector', 'is_searchable', 'help_post');
766 }
767
768 this.form = new Backbone.Form({
769 model: this.model,
770 fields: fields
771 });
772 this.form.on('change', this.onFormChange, this);
773 this.model.on('change', this.onModelChange, this);
774 },
775 render: function() {
776 this.$el.html(this.form.render().el);
777 this.onFormChange();
778 },
779 onModelChange: function() {
780 $.each(this.form.fields, function(i, field) {
781 this.form.setValue(field.key, this.model.get(field.key));
782 });
783 },
784 onFormChange: function() {
785 this.form.commit();
786 this.$('.field-is_multi_summary').toggle(this.options.fieldSchema.civiIsMultiple ? true : false);
787 this.$('.field-in_selector').toggle(this.model.isInSelectorAllowed());
788
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");
793 }
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.
797 }
798 }
799 });
800
801 /**
802 * options:
803 * - model: CRM.UF.UFGroupModel
804 */
805 CRM.Designer.UFGroupView = Backbone.Marionette.Layout.extend({
806 serializeData: extendedSerializeData,
807 template: '#form_row_template',
808 expanded: false,
809 regions: {
810 summary: '.crm-designer-form-summary',
811 detail: '.crm-designer-form-detail'
812 },
813 events: {
814 "click .crm-designer-action-settings": 'doToggleForm'
815 },
816 onRender: function() {
817 this.summary.show(new CRM.Designer.UFGroupSummaryView({
818 model: this.model
819 }));
820 this.detail.show(new CRM.Designer.UFGroupDetailView({
821 model: this.model
822 }));
823 if (!this.expanded) {
824 this.detail.$el.hide();
825 }
826 var that = this;
827 CRM.designerApp.vent.on('formOpened', function(event) {
828 if (that.expanded && event !== 0) {
829 that.doToggleForm(false);
830 }
831 });
832 },
833 doToggleForm: function(event) {
834 this.expanded = !this.expanded;
835 if (this.expanded && event !== false) {
836 CRM.designerApp.vent.trigger('formOpened', 0);
837 }
838 this.$el.toggleClass('crm-designer-open', this.expanded);
839 this.detail.$el.toggle('blind', 250);
840 }
841 });
842
843 /**
844 * options:
845 * - model: CRM.UF.UFGroupModel
846 */
847 CRM.Designer.UFGroupSummaryView = Backbone.Marionette.ItemView.extend({
848 serializeData: extendedSerializeData,
849 template: '#form_summary_template',
850 modelEvents: {
851 'change': 'render'
852 },
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();
857 }
858 }
859 });
860
861 /**
862 * options:
863 * - model: CRM.UF.UFGroupModel
864 */
865 CRM.Designer.UFGroupDetailView = Backbone.View.extend({
866 initialize: function() {
867 this.form = new Backbone.Form({
868 model: this.model,
869 fields: ['title', 'help_pre', 'help_post', 'is_active']
870 });
871 this.form.on('change', this.form.commit, this.form);
872 },
873 render: function() {
874 this.$el.html(this.form.render().el);
875 }
876 });
877
878 })(CRM.$, CRM._);