1 (function(angular
, $, _
) {
4 angular
.module('crmSearchAdmin').component('crmSearchClause', {
16 templateUrl
: '~/crmSearchAdmin/crmSearchClause.html',
17 controller: function ($scope
, $element
, $timeout
, searchMeta
) {
18 var ts
= $scope
.ts
= CRM
.ts('org.civicrm.search_kit'),
21 this.conjunctions
= {AND
: ts('And'), OR
: ts('Or'), NOT
: ts('Not')};
25 connectWith
: '.api4-clause-group-sortable',
26 containment
: $element
.closest('.api4-clause-fieldset'),
32 this.$onInit = function() {
33 ctrl
.hasParent
= !!$element
.attr('delete-group');
34 _
.each(ctrl
.clauses
, updateOperators
);
37 // Return a list of operators allowed for the field in a given clause
38 this.getOperators = function(clause
) {
39 var field
= ctrl
.getField(clause
[0]);
40 if (!field
|| !field
.operators
) {
41 return CRM
.crmSearchAdmin
.operators
;
43 var opKey
= field
.operators
.join();
44 if (!ctrl
.operators
[opKey
]) {
45 ctrl
.operators
[opKey
] = _
.filter(CRM
.crmSearchAdmin
.operators
, function(operator
) {
46 return _
.includes(field
.operators
, operator
.key
);
49 return ctrl
.operators
[opKey
];
52 // Ensures a clause is using an operator that is allowed for the field
53 function updateOperators(clause
) {
54 // Recurse into AND/OR/NOT groups
55 if (ctrl
.conjunctions
[clause
[0]]) {
56 _
.each(clause
[1], updateOperators
);
58 else if (!ctrl
.skip
&& (!clause
[1] || !_
.includes(_
.pluck(ctrl
.getOperators(clause
), 'key'), clause
[1]))) {
59 clause
[1] = ctrl
.getOperators(clause
)[0].key
;
60 ctrl
.changeClauseOperator(clause
);
64 this.getField = function(expr
) {
66 meta
[expr
] = searchMeta
.parseExpr(expr
);
68 return meta
[expr
].field
;
71 this.getOptionKey = function(expr
) {
73 meta
[expr
] = searchMeta
.parseExpr(expr
);
75 return meta
[expr
].suffix
? meta
[expr
].suffix
.slice(1) : 'id';
78 this.addGroup = function(op
) {
79 ctrl
.clauses
.push([op
, []]);
82 function onSort(event
, ui
) {
83 $($element
).closest('.api4-clause-fieldset').toggleClass('api4-sorting', event
.type
=== 'sortstart');
84 $('.api4-input.form-inline').css('margin-left', '');
87 // Indent clause while dragging between nested groups
88 function onSortOver(event
, ui
) {
91 offset
= $(ui
.placeholder
).offset().left
- $(ui
.sender
).offset().left
;
93 $('.api4-input.form-inline.ui-sortable-helper').css('margin-left', '' + offset
+ 'px');
96 this.addClause = function() {
99 var newIndex
= ctrl
.clauses
.length
;
100 ctrl
.clauses
.push([ctrl
.newClause
, '=', '']);
101 ctrl
.newClause
= null;
102 updateOperators(ctrl
.clauses
[newIndex
]);
107 this.deleteRow = function(index
) {
108 ctrl
.clauses
.splice(index
, 1);
111 // Remove empty values
112 this.changeClauseField = function(clause
, index
) {
113 if (clause
[0] === '') {
114 ctrl
.deleteRow(index
);
116 updateOperators(clause
);
120 // Returns false for 'IS NULL', 'IS EMPTY', etc. true otherwise.
121 this.operatorTakesInput = function(operator
) {
122 return operator
.indexOf('IS ') !== 0;
125 this.changeClauseOperator = function(clause
) {
126 // Add/remove value depending on whether operator allows for one
127 if (!ctrl
.operatorTakesInput(clause
[1])) {
130 if (clause
.length
=== 2) {
133 // Change multi/single value to/from an array
134 var shouldBeArray
= (clause
[1] === 'IN' || clause
[1] === 'NOT IN' || clause
[1] === 'BETWEEN' || clause
[1] === 'NOT BETWEEN');
135 if (!_
.isArray(clause
[2]) && shouldBeArray
) {
137 } else if (_
.isArray(clause
[2]) && !shouldBeArray
) {
140 if (clause
[1] === 'BETWEEN' || clause
[1] === 'NOT BETWEEN') {
141 clause
[2].length
= 2;
149 })(angular
, CRM
.$, CRM
._
);