019add712d87a59ba286716c87d1c6edfd4cb178
1 // http://civicrm.org/licensing
5 /* jshint validthis: true */
7 * Handle user input - field or operator selection.
9 * Decide whether to display select drop down, regular text or date
10 * field for the given field and row.
12 function handleUserInputField() {
13 var row
= $(this).closest('tr');
14 var field
= $('select[id^=mapper][id$="_1"]', row
).val();
15 field
= (field
=== 'world_region') ? 'worldregion_id': field
;
16 var operator
= $('select[id^=operator]', row
);
17 var op
= operator
.val();
19 var patt
= /_1$/; // pattern to check if the change event came from field name
20 if (field
!== null && patt
.test(this.id
)) {
21 // based on data type remove invalid operators e.g. IS EMPTY doesn't work with Boolean type column
22 if ((field
in CRM
.searchBuilder
.fieldTypes
) === true) {
23 if (CRM
.searchBuilder
.fieldTypes
[field
] == 'Boolean') {
24 CRM
.searchBuilder
.generalOperators
= _
.omit(CRM
.searchBuilder
.generalOperators
, ['IS NOT EMPTY', 'IS EMPTY']);
26 else if (CRM
.searchBuilder
.fieldTypes
[field
] == 'String') {
27 CRM
.searchBuilder
.generalOperators
= _
.omit(CRM
.searchBuilder
.generalOperators
, ['>', '<', '>=', '<=']);
30 buildOperator(operator
, CRM
.searchBuilder
.generalOperators
);
33 // These Ops don't get any input field.
34 var noFieldOps
= ['', 'IS EMPTY', 'IS NOT EMPTY', 'IS NULL', 'IS NOT NULL'];
36 if ($.inArray(op
, noFieldOps
) > -1) {
37 // Hide the fields and return.
38 $('.crm-search-value', row
).hide().find('input, select').val('');
41 $('.crm-search-value', row
).show();
43 if (!CRM
.searchBuilder
.fieldOptions
[field
]) {
47 buildSelect(row
, field
, op
, false);
50 if ((field
in CRM
.searchBuilder
.fieldTypes
) === true &&
51 CRM
.searchBuilder
.fieldTypes
[field
] == 'Date'
61 * Add appropriate operator to selected field
62 * @param operator: jQuery object
63 * @param options: array
65 function buildOperator(operator
, options
) {
66 var selected
= operator
.val();
68 $.each(options
, function(value
, label
) {
69 operator
.append('<option value="' + value
+ '">' + label
+ '</option>');
71 operator
.val(selected
);
75 * Add select list if appropriate for this operation
76 * @param row: jQuery object
77 * @param field: string
78 * @param skip_fetch: boolean
80 function buildSelect(row
, field
, op
, skip_fetch
) {
82 // Operators that will get a single drop down list of choices.
83 var dropDownSingleOps
= ['=', '!='];
84 // Multiple select drop down list.
85 var dropDownMultipleOps
= ['IN', 'NOT IN'];
87 if ($.inArray(op
, dropDownMultipleOps
) > -1) {
88 multiSelect
= 'multiple="multiple"';
90 else if ($.inArray(op
, dropDownSingleOps
) < 0) {
91 // If this op is neither supported by single or multiple selects, then we should not render a select list.
96 $('.crm-search-value select', row
).remove();
97 $('input[id^=value]', row
)
99 .after('<select class="crm-form-' + multiSelect
.substr(0, 5) + 'select required" ' + multiSelect
+ '><option value="">' + ts('Loading') + '...</option></select>');
101 // Avoid reloading state/county options IF already built, identified by skip_fetch
103 buildOptions(row
, field
);
106 fetchOptions(row
, field
);
111 * Retrieve option list for given row
112 * @param row: jQuery object
113 * @param field: string
115 function fetchOptions(row
, field
) {
116 if (CRM
.searchBuilder
.fieldOptions
[field
] === 'yesno') {
117 CRM
.searchBuilder
.fieldOptions
[field
] = [{key
: 1, value
: ts('Yes')}, {key
: 0, value
: ts('No')}];
119 if (typeof(CRM
.searchBuilder
.fieldOptions
[field
]) == 'string') {
120 CRM
.api(CRM
.searchBuilder
.fieldOptions
[field
], 'getoptions', {field
: field
, sequential
: 1}, {
121 success: function(result
, settings
) {
122 var field
= settings
.field
;
124 CRM
.searchBuilder
.fieldOptions
[field
] = result
.values
;
125 buildOptions(settings
.row
, field
);
128 removeSelect(settings
.row
);
131 error: function(result
, settings
) {
132 removeSelect(settings
.row
);
139 buildOptions(row
, field
);
144 * Populate option list for given row
145 * @param row: jQuery object
146 * @param field: string
148 function buildOptions(row
, field
) {
149 var select
= $('.crm-search-value select', row
);
150 var value
= $('input[id^=value]', row
).val();
151 if (value
.length
&& value
.charAt(0) == '(' && value
.charAt(value
.length
- 1) == ')') {
152 value
= value
.slice(1, -1);
154 var options
= value
.split(',');
155 if (select
.attr('multiple') == 'multiple') {
156 select
.find('option').remove();
159 select
.find('option').text(ts('- select -'));
160 if (options
.length
> 1) {
161 options
= [options
[0]];
164 $.each(CRM
.searchBuilder
.fieldOptions
[field
], function(key
, option
) {
165 var optionKey
= option
.key
;
166 if ($.inArray(field
, CRM
.searchBuilder
.searchByLabelFields
) >= 0) {
167 optionKey
= option
.value
;
169 var selected
= ($.inArray(''+optionKey
, options
) > -1) ? 'selected="selected"' : '';
170 select
.append('<option value="' + optionKey
+ '"' + selected
+ '>' + option
.value
+ '</option>');
176 * Remove select options and restore input to a plain textfield
177 * @param row: jQuery object
179 function removeSelect(row
) {
180 $('.crm-search-value input', row
).show();
181 $('.crm-search-value select', row
).remove();
185 * Add a datepicker if appropriate for this operation
186 * @param row: jQuery object
188 function buildDate(row
, op
) {
189 var input
= $('.crm-search-value input', row
);
190 // These are operations that should not get a datepicker
191 var datePickerOp
= ($.inArray(op
, ['IN', 'NOT IN', 'LIKE', 'RLIKE']) < 0);
195 else if (!input
.hasClass('hasDatepicker')) {
196 input
.addClass('dateplugin').datepicker({
197 dateFormat
: 'yymmdd',
200 yearRange
: '-100:+20'
207 * @param row: jQuery object
209 function removeDate(row
) {
210 var input
= $('.crm-search-value input', row
);
211 if (input
.hasClass('hasDatepicker')) {
212 input
.removeClass('dateplugin').val('').datepicker('destroy');
217 * Load and build select options for state IF country is chosen OR county options if state is chosen
218 * @param mapper: string
219 * @param value: integer
220 * @param location_type: integer
222 function chainSelect(mapper
, value
, location_type
) {
225 field
: (mapper
== 'country_id') ? 'state_province' : 'county',
227 apiParams
[mapper
] = value
;
228 var fieldName
= apiParams
.field
;
229 CRM
.api3('address', 'getoptions', apiParams
, {
230 success: function(result
) {
231 CRM
.searchBuilder
.fieldOptions
[fieldName
] = result
.count
? result
.values
: [];
232 $('select[id^=mapper][id$="_1"]').each(function() {
233 var row
= $(this).closest('tr');
234 var op
= $('select[id^=operator]', row
).val();
235 if ($(this).val() === fieldName
&& location_type
=== $('select[id^=mapper][id$="_2"]', row
).val()) {
236 buildSelect(row
, fieldName
, op
, true);
243 // Initialize display: Hide empty blocks & fields
244 var newBlock
= CRM
.searchBuilder
&& CRM
.searchBuilder
.newBlock
|| 0;
245 function initialize() {
246 $('.crm-search-block', '#Builder').each(function(blockNo
) {
248 var empty
= blockNo
+ 1 > newBlock
;
249 var skippedRow
= false;
250 $('tr:not(.crm-search-builder-add-row)', block
).each(function(rowNo
) {
252 if ($('select:first', row
).val() === '') {
253 if (!skippedRow
&& (rowNo
=== 0 || blockNo
+ 1 == newBlock
)) {
271 $('#crm-main-content-wrapper')
272 // Reset and hide row
273 .on('click', '.crm-reset-builder-row', function() {
274 var row
= $(this).closest('tr');
275 $('input, select', row
).val('').change();
277 // Hide entire block if this is the only visible row
278 if (row
.siblings(':visible').length
< 2) {
279 row
.closest('.crm-search-block').hide();
283 // Add new field - if there's a hidden one, show it
284 // Otherwise allow form to submit and fetch more from the server
285 .on('click', 'input[name^=addMore]', function() {
286 var table
= $(this).closest('table');
287 if ($('tr:hidden', table
).length
) {
288 $('tr:hidden', table
).first().show();
292 // Add new block - if there's a hidden one, show it
293 // Otherwise allow form to submit and fetch more from the server
294 .on('click', '#addBlock', function() {
295 if ($('.crm-search-block:hidden', '#Builder').length
) {
296 var block
= $('.crm-search-block:hidden', '#Builder').first();
298 $('tr:first-child, tr.crm-search-builder-add-row', block
).show();
302 // Handle field and operator selection
303 .on('change', 'select[id^=mapper][id$="_1"], select[id^=operator]', handleUserInputField
)
304 // Handle option selection - update hidden value field
305 .on('change', '.crm-search-value select', function() {
306 var value
= $(this).val() || '';
307 if ($(this).attr('multiple') == 'multiple' && value
.length
) {
308 value
= value
.join(',');
310 $(this).siblings('input').val(value
);
312 var mapper
= $('#' + $(this).siblings('input').attr('id').replace('value_', 'mapper_') + '_1').val();
313 var location_type
= $('#' + $(this).siblings('input').attr('id').replace('value_', 'mapper_') + '_2').val();
314 if ($.inArray(mapper
, ['state_province', 'country']) > -1) {
315 chainSelect(mapper
+ '_id', value
, location_type
);
319 .on('crmLoad', function() {
321 $('select[id^=mapper][id$="_1"]', '#Builder').each(handleUserInputField
);
326 // Fetch initial options during page refresh - it's more efficient to bundle them in a single ajax request
327 var initialFields
= {}, fetchFields
= false;
328 $('select[id^=mapper][id$="_1"] option:selected', '#Builder').each(function() {
329 var field
= $(this).attr('value');
330 if (typeof(CRM
.searchBuilder
.fieldOptions
[field
]) == 'string' && CRM
.searchBuilder
.fieldOptions
[field
] !== 'yesno') {
331 initialFields
[field
] = [CRM
.searchBuilder
.fieldOptions
[field
], 'getoptions', {field
: field
, sequential
: 1}];
336 CRM
.api3(initialFields
).done(function(data
) {
337 $.each(data
, function(field
, result
) {
338 CRM
.searchBuilder
.fieldOptions
[field
] = result
.values
;
340 $('select[id^=mapper][id$="_1"]', '#Builder').each(handleUserInputField
);
343 $('select[id^=mapper][id$="_1"]', '#Builder').each(handleUserInputField
);