Merge pull request #17836 from seamuslee001/dev_core_1874
[civicrm-core.git] / js / crm.optionEdit.js
1 // https://civicrm.org/licensing
2 jQuery(function($) {
3 $('body')
4 // Enable administrators to edit option lists in a dialog
5 .on('click', 'a.crm-option-edit-link', CRM.popup)
6 .on('crmPopupFormSuccess', 'a.crm-option-edit-link', function() {
7 $(this).trigger('crmOptionsEdited');
8 var optionEditPath = $(this).data('option-edit-path');
9 var $selects = $('select[data-option-edit-path="' + optionEditPath + '"]');
10 var $inputs = $('input[data-option-edit-path="' + optionEditPath + '"]');
11 var $radios = $inputs.filter('[type=radio]');
12 var $checkboxes = $inputs.filter('[type=checkbox]');
13
14 if ($selects.length > 0) {
15 rebuildOptions($selects, CRM.utils.setOptions);
16 }
17 else if ($radios.length > 0) {
18 rebuildOptions($radios, rebuildRadioOptions);
19 }
20 else if ($checkboxes.length > 0) {
21 rebuildOptions($checkboxes, rebuildCheckboxOptions);
22 }
23 });
24
25 /**
26 * Fetches options using metadata from the existing ones and calls the
27 * function to rebuild them
28 * @param $existing {object} The existing options, used as metadata store
29 * @param rebuilder {function} Function to be called to rebuild the options
30 */
31 function rebuildOptions($existing, rebuilder) {
32 if ($existing.data('api-entity') && $existing.data('api-field')) {
33 var params = {
34 sequential: 1,
35 field: $existing.data('api-field')
36 };
37 $.extend(params, $existing.data('option-edit-context'));
38
39 CRM.api3($existing.data('api-entity'), 'getoptions', params)
40 .done(function(data) {
41 rebuilder($existing, data.values);
42 });
43 }
44 }
45
46 /**
47 * Rebuild checkbox input options, overwriting the existing options
48 *
49 * @param $existing {object} the existing checkbox options
50 * @param newOptions {array} in format returned by api.getoptions
51 */
52 function rebuildCheckboxOptions($existing, newOptions) {
53 var $parent = $existing.first().parent(),
54 $firstExisting = $existing.first(),
55 optionName = $firstExisting.attr('name'),
56 optionAttributes =
57 'data-option-edit-path =' + $firstExisting.data('option-edit-path') +
58 ' data-api-entity = ' + $firstExisting.data('api-entity') +
59 ' data-api-field = ' + $firstExisting.data('api-field');
60
61 var prefix = optionName.substr(0, optionName.lastIndexOf("["));
62
63 var checkedBoxes = [];
64 $parent.find('input:checked').each(function() {
65 checkedBoxes.push($(this).attr('id'));
66 });
67
68 // remove existing checkboxes
69 $parent.find('input[type=checkbox]').remove();
70
71 // find existing labels for the checkboxes
72 var $checkboxLabels = $parent.find('label').filter(function() {
73 var forAttr = $(this).attr('for') || '';
74
75 return forAttr.indexOf(prefix) !== -1;
76 });
77
78 // find what is used to separate the elements; spaces or linebreaks
79 var $elementAfterLabel = $checkboxLabels.first().next();
80 var separator = $elementAfterLabel.is('br') ? '<br/>' : '&nbsp;';
81
82 // remove existing labels
83 $checkboxLabels.remove();
84
85 // remove linebreaks in container
86 $parent.find('br').remove();
87
88 // remove separator whitespace in container
89 $parent.html(function (i, html) {
90 return html.replace(/&nbsp;/g, '');
91 });
92
93 var renderedOptions = '';
94 // replace missing br at start of element
95 if (separator === '<br/>') {
96 $parent.prepend(separator);
97 renderedOptions = separator;
98 }
99
100 newOptions.forEach(function(option) {
101 var optionId = prefix + '_' + option.key,
102 checked = '';
103
104 if ($.inArray(optionId, checkedBoxes) !== -1) {
105 checked = ' checked="checked"';
106 }
107
108 renderedOptions += '<input type="checkbox" ' +
109 ' value="1"' +
110 ' id="' + optionId + '"' +
111 ' name="' + prefix + '[' + option.key +']' + '"' +
112 checked +
113 ' class="crm-form-checkbox"' +
114 optionAttributes +
115 '><label for="' + optionId + '">' + option.value + '</label>' +
116 separator;
117 });
118
119 // remove final separator
120 renderedOptions = renderedOptions.substring(0, renderedOptions.lastIndexOf(separator));
121
122 var $editLink = $parent.find('.crm-option-edit-link');
123
124 // try to insert before the edit link to maintain structure
125 if ($editLink.length > 0) {
126 $(renderedOptions).insertBefore($editLink);
127 }
128 else {
129 $parent.append(renderedOptions);
130 }
131 }
132
133 /**
134 * Rebuild radio input options, overwriting the existing options
135 *
136 * @param $existing {object} the existing input options
137 * @param newOptions {array} in format returned by api.getoptions
138 */
139 function rebuildRadioOptions($existing, newOptions) {
140 var $parent = $existing.first().parent(),
141 $firstExisting = $existing.first(),
142 optionName = $firstExisting.attr('name'),
143 renderedOptions = '',
144 checkedValue = parseInt($parent.find('input:checked').attr('value')),
145 optionAttributes =
146 'data-option-edit-path =' + $firstExisting.attr('data-option-edit-path') +
147 ' data-api-entity = ' + $firstExisting.attr('data-api-entity') +
148 ' data-api-field = ' + $firstExisting.attr('data-api-field');
149
150 // remove existing radio inputs and labels
151 $parent.find('input, label').remove();
152
153 newOptions.forEach(function(option) {
154 var optionId = 'CIVICRM_QFID_' + option.key + '_' + optionName,
155 checked = (option.key === checkedValue) ? ' checked="checked"' : '';
156
157 renderedOptions += '<input type="radio" ' +
158 ' value=' + option.key +
159 ' id="' + optionId +'"' +
160 ' name="' + optionName + '"' +
161 checked +
162 ' class="crm-form-radio"' +
163 optionAttributes +
164 '><label for="' + optionId + '">' + option.value + '</label> ';
165 });
166
167 $parent.prepend(renderedOptions);
168 }
169 });