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