{literal}
<script type="text/javascript">
  CRM.$(function($, _) {
    var $el = $('#{/literal}{$inExcludeFieldId}{literal}');
    $('#{/literal}{$inExcludeFieldId}{literal}').addClass('crm-form-entityref');
    $('#{/literal}{$inExcludeFieldId}{literal}').crmEntityRef({
      'select': {
        formatResult: function (row) {
          var labelStyle = 'color: #060';
          if (row.mode && row.mode == 'exclude') {
            labelStyle = 'color: #600; text-decoration: line-through;';
          }

          var markup = '<div class="crm-select2-row">';
          if (row.image !== undefined) {
            markup += '<div class="crm-select2-image"><img src="' + row.image + '"/></div>';
          }
          else if (row.icon_class) {
            markup += '<div class="crm-select2-icon"><div class="crm-icon ' + row.icon_class + '-icon"></div></div>';
          }
          markup += '<div><div class="crm-select2-row-label '+(row.label_class || '')+'">' +
            (row.color ? '<span class="crm-select-item-color" style="background-color: ' + row.color + '"></span> ' : '') +
            (row.icon ? '<i class="crm-i ' + row.icon + '" aria-hidden="true"></i> ' : '') +
            '<span style="' + labelStyle + '">' +
            CRM._.escape((row.prefix !== undefined ? row.prefix + ' ' : '') + row.label + (row.suffix !== undefined ? ' ' + row.suffix : '')) +
            '</span>' +
            '</div>' +
            '<div class="crm-select2-row-description">';
          $.each(row.description || [], function(k, text) {
            markup += '<p>' + CRM._.escape(text) + '</p> ';
          });
          markup += '</div></div></div>';
          return markup;
        },
        formatSelection: function(row) {
          var labelStyle = 'color: #060';
          if (row.mode && row.mode == 'exclude') {
            labelStyle = 'color: #600; text-decoration: line-through;';
          }
          return (row.color ? '<span class="crm-select-item-color" style="background-color: ' + row.color + '"></span> ' : '') +
            '<span style="' + labelStyle + '">' +
            CRM._.escape((row.prefix !== undefined ? row.prefix + ' ' : '') + row.label + (row.suffix !== undefined ? ' ' + row.suffix : '')) +
            '</span>';
        },
        ajax: {
          url: CRM.url('civicrm/ajax/rest'),
          quietMillis: 300,
          data: function (input, page_num) {
            var
              params = $.extend({params: {}}, $el.data('api-params') || {}),
              // Prevent original data from being modified - $.extend and _.clone don't cut it, they pass nested objects by reference!
              combined = CRM._.cloneDeep(params),
              filter = $.extend({}, $el.data('user-filter') || {});
            if (filter.key && filter.value) {
              // Fieldname may be prefixed with joins
              var fieldName = CRM._.last(filter.key.split('.'));
              // Special case for contact type/sub-type combo
              if (fieldName === 'contact_type' && (filter.value.indexOf('__') > 0)) {
                combined.params[filter.key] = filter.value.split('__')[0];
                combined.params[filter.key.replace('contact_type', 'contact_sub_type')] = filter.value.split('__')[1];
              } else {
                // Allow json-encoded api filters e.g. {"BETWEEN":[123,456]}
                combined.params[filter.key] = filter.value.charAt(0) === '{' ? $.parseJSON(filter.value) : filter.value;
              }
            }
            combined.input = input;
            combined.page_num = page_num;
            return {
              entity: $el.data('api-entity'),
              action: 'getlist',
              json: JSON.stringify(combined)
            };
          },
          results: function(data) {
            var r = [];
            if (data.values) {
              data.values.forEach(function(v) {
                var include = CRM._.cloneDeep(v);
                var exclude = CRM._.cloneDeep(v);
                exclude.id = 'exclude_' + include.id;
                include.mode = 'include';
                exclude.mode = 'exclude';
                r.push(include, exclude);
              })
            }
            return {more: data.more_results, results: r || []};
          }
        },
        initSelection: function(e, callback) {
          var
            multiple = !!e.data('select-params').multiple,
            val = e.val(),
            stored = e.data('entity-value') || [];
          if (val === '') {
            return;
          }
          var idsNeeded = CRM._.difference(val.split(','), CRM._.pluck(stored, 'id'));
          var existing = CRM._.remove(stored, function(item) {
            return CRM._.includes(val.split(','), item.id);
          });
          // If we already have this data, just return it
          if (!idsNeeded.length) {
            callback(multiple ? existing : existing[0]);
          } else {
            var newArrayVal = [];
            idsNeeded.forEach(function(v) {
              if (v.startsWith('exclude_')) {
                v = v.substring(8);
              }
              if (newArrayVal.indexOf(v) < 0) {
                newArrayVal.push(v);
              }
            });

            var params = $.extend({}, e.data('api-params') || {}, {id: newArrayVal.join(',')});
            CRM.api3(e.data('api-entity'), 'getlist', params).done(function(data) {
              var r = [];
              if (data.values) {
                data.values.forEach(function(v) {
                  var include = CRM._.cloneDeep(v);
                  var exclude = CRM._.cloneDeep(v);
                  exclude.id = 'exclude_' + include.id;
                  include.mode = 'include';
                  exclude.mode = 'exclude';
                  if (idsNeeded.indexOf(exclude.id) >= 0) {
                    r.push(exclude);
                  } else {
                    r.push(include);
                  }
                })
              }
              callback(multiple ? r.concat(existing) : r[0]);
            });
          }
        }
      }
    });
  });
</script>
{/literal}
