Restore comparison operators for search builder selects
[civicrm-core.git] / templates / CRM / Contact / Form / Search / Builder.js
1 // http://civicrm.org/licensing
2 (function($, CRM) {
3 // @var: default select operator options
4 var operators, operatorCount;
5
6 /**
7 * Handle Field Selection
8 */
9 function handleFieldSelection() {
10 var field = $(this).val();
11 var row = $(this).closest('tr');
12 if (!CRM.searchBuilder.fieldOptions[field]) {
13 removeSelect(row);
14 }
15 if ($.inArray(field, CRM.searchBuilder.dateFields) < 0) {
16 removeDate(row);
17 if (CRM.searchBuilder.fieldOptions[field]) {
18 buildSelect(row, field);
19 }
20 }
21 else {
22 buildDate(row);
23 }
24 }
25
26 /**
27 * Handle Search Operator Selection
28 */
29 function handleOperatorSelection() {
30 var noValue = ['', 'IS EMPTY', 'IS NOT EMPTY', 'IS NULL', 'IS NOT NULL'];
31 var row = $(this).closest('tr');
32 if ($.inArray($(this).val(), noValue) < 0) {
33 $('.crm-search-value', row).show();
34 // Change between multiselect and select when using "IN" operator
35 var select = $('.crm-search-value select', row);
36 if (select.length) {
37 var value = select.val() || '';
38 var multi = ($(this).val() == 'IN' || $(this).val() == 'NOT IN');
39 select.attr('multiple', multi);
40 if (multi) {
41 $('option[value=""]', select).remove();
42 }
43 else if ($('option[value=""]', select).length < 1) {
44 $(select).prepend('<option value="">' + ts('- select -') + '</option>');
45 }
46 select.val(value).change();
47 }
48 }
49 // Hide value field if the operator doesn't take a value
50 else {
51 $('.crm-search-value', row).hide().find('input, select').val('');
52 }
53 }
54
55 /**
56 * Give user a list of options to choose from
57 * @param row: jQuery object
58 * @param field: string
59 */
60 function buildSelect(row, field) {
61 // Remove operators that can't be used with a select
62 removeOperators(row, ['LIKE', 'RLIKE']);
63 var op = $('select[id^=operator]', row);
64 if (op.val() == 'IN' || op.val() == 'NOT IN') {
65 var multiSelect = 'multiple="multiple">';
66 }
67 else {
68 var multiSelect = '><option value="">' + ts('- select -') + '</option>';
69 }
70 $('.crm-search-value select', row).remove();
71 $('input[id^=value]', row).hide().after('<select class="form-select required" ' + multiSelect + '</select>');
72 fetchOptions(row, field);
73 }
74
75 /**
76 * Retrieve option list for given row
77 * @param row: jQuery object
78 * @param field: string
79 */
80 function fetchOptions(row, field) {
81 if (CRM.searchBuilder.fieldOptions[field] === 'yesno') {
82 CRM.searchBuilder.fieldOptions[field] = {1: ts('Yes'), 0: ts('No')};
83 }
84 if (typeof(CRM.searchBuilder.fieldOptions[field]) == 'string') {
85 CRM.api(CRM.searchBuilder.fieldOptions[field], 'getoptions', {field: field}, {
86 success: function(result, settings) {
87 var field = settings.field;
88 if (result.count) {
89 CRM.searchBuilder.fieldOptions[field] = result.values;
90 buildOptions(settings.row, field);
91 }
92 else {
93 removeSelect(settings.row);
94 }
95 },
96 error: function(result, settings) {
97 removeSelect(settings.row);
98 },
99 row: row,
100 field: field
101 });
102 }
103 else {
104 buildOptions(row, field);
105 }
106 }
107
108 /**
109 * Populate option list for given row
110 * @param row: jQuery object
111 * @param field: string
112 */
113 function buildOptions(row, field) {
114 var select = $('.crm-search-value select', row);
115 var value = $('input[id^=value]', row).val();
116 if (value.length && value.charAt(0) == '(' && value.charAt(value.length - 1) == ')') {
117 value = value.slice(1, -1);
118 }
119 var options = value.split(',');
120 var op = $('select[id^=operator]', row);
121 if (op.val() != 'IN' && op.val() != 'NOT IN' && options.length > 1) {
122 options = [options[0]];
123 }
124 $.each(CRM.searchBuilder.fieldOptions[field], function(value, label) {
125 var selected = ($.inArray(value, options) > -1) ? 'selected="selected"' : '';
126 select.append('<option value="' + value + '"' + selected + '>' + label + '</option>');
127 });
128 select.change();
129 }
130
131 /**
132 * Remove select options and restore input to a plain textfield
133 * @param row: jQuery object
134 */
135 function removeSelect(row) {
136 $('.crm-search-value input', row).show();
137 $('.crm-search-value select', row).remove();
138 restoreOperators(row);
139 }
140
141 /**
142 * Add a datepicker
143 * @param row: jQuery object
144 */
145 function buildDate(row) {
146 var input = $('.crm-search-value input', row);
147 if (!input.hasClass('hasDatepicker')) {
148 // Remove operators that can't be used with a date
149 removeOperators(row, ['IN', 'NOT IN', 'LIKE', 'RLIKE', 'IS EMPTY', 'IS NOT EMPTY']);
150 input.addClass('dateplugin').datepicker({
151 dateFormat: 'yymmdd',
152 changeMonth: true,
153 changeYear: true,
154 yearRange: '-100:+20'
155 });
156 }
157 }
158
159 /**
160 * Remove datepicker
161 * @param row: jQuery object
162 */
163 function removeDate(row) {
164 var input = $('.crm-search-value input', row);
165 if (input.hasClass('hasDatepicker')) {
166 restoreOperators(row);
167 input.removeClass('dateplugin').val('').datepicker('destroy');
168 }
169 }
170
171 /**
172 * Remove operators from a row
173 * @param row: jQuery object
174 * @param illegal: array
175 */
176 function removeOperators(row, illegal) {
177 var value = $('select[id^=operator]').val();
178 $('select[id^=operator] option', row).each(function() {
179 if ($.inArray($(this).attr('value'), illegal) > -1) {
180 $(this).remove();
181 }
182 });
183 if (value !== $('select[id^=operator]').val()) {
184 $('select[id^=operator]').change();
185 }
186 }
187
188 /**
189 * Restore operators to the default
190 * @param row: jQuery object
191 */
192 function restoreOperators(row) {
193 var op = $('select[id^=operator]', row);
194 if ($('option', op).length != operatorCount) {
195 var value = op.val();
196 op.html(operators).val(value).change();
197 }
198 }
199
200 $('document').ready(function() {
201 operators = $('#operator_1_0').html();
202 operatorCount = $('#operator_1_0 option').length;
203
204 // Hide empty blocks & fields
205 var newBlock = CRM.searchBuilder && CRM.searchBuilder.newBlock || 0;
206 $('#Builder .crm-search-block').each(function(blockNo) {
207 var block = $(this);
208 var empty = blockNo + 1 > newBlock;
209 var skippedRow = false;
210 $('tr:not(.crm-search-builder-add-row)', block).each(function(rowNo) {
211 var row = $(this);
212 if ($('select:first', row).val() === '') {
213 if (!skippedRow && (rowNo == 0 || blockNo + 1 == newBlock)) {
214 skippedRow = true;
215 }
216 else {
217 row.hide();
218 }
219 }
220 else {
221 empty = false;
222 }
223 });
224 if (empty) {
225 block.hide();
226 }
227 });
228
229 $('#Builder')
230 // Reset and hide row
231 .on('click', '.crm-reset-builder-row', function() {
232 var row = $(this).closest('tr');
233 $('input, select', row).val('').change();
234 row.hide();
235 // Hide entire block if this is the only visible row
236 if (row.siblings(':visible').length < 2) {
237 row.closest('.crm-search-block').hide();
238 }
239 return false;
240 })
241 // Add new field - if there's a hidden one, show it
242 // Otherwise we submit form to fetch more from the server
243 .on('click', 'input[name^=addMore]', function() {
244 var table = $(this).closest('table');
245 if ($('tr:hidden', table).length) {
246 $('tr:hidden', table).first().show();
247 return false;
248 }
249 })
250 // Add new block - if there's a hidden one, show it
251 // Otherwise we submit form to fetch more from the server
252 .on('click', '#addBlock', function() {
253 if ($('.crm-search-block:hidden', '#Builder').length) {
254 var block = $('.crm-search-block:hidden', '#Builder').first();
255 block.show();
256 $('tr:first-child, tr.crm-search-builder-add-row', block).show();
257 return false;
258 }
259 })
260 // Handle field selection
261 .on('change', 'select[id^=mapper][id$="_1"]', handleFieldSelection)
262 // Handle operator selection
263 .on('change', 'select[id^=operator]', handleOperatorSelection)
264 // Handle option selection - update hidden value field
265 .on('change', '.crm-search-value select', function() {
266 var value = $(this).val() || '';
267 if ($(this).attr('multiple') == 'multiple' && value.length) {
268 value = '(' + value.join(',') + ')';
269 }
270 $(this).siblings('input').val(value);
271 })
272 ;
273 $('select[id^=operator]', '#Builder').each(handleOperatorSelection);
274 $().crmAccordions();
275 $('select[id^=mapper][id$="_1"] option[selected=selected]:not([value=""])', '#Builder').parent().each(handleFieldSelection);
276 });
277 })(cj, CRM);