/**
* @class Invert
* @memberof module:plugins
* @description Allows to invert a rule operator, a group condition or the entire builder.
* @param {object} [options]
* @param {string} [options.icon='glyphicon glyphicon-random']
* @param {boolean} [options.recursive=true]
* @param {boolean} [options.invert_rules=true]
* @param {boolean} [options.display_rules_button=false]
* @param {boolean} [options.silent_fail=false]
*/
QueryBuilder.define('invert', function(options) {
var self = this;
var Selectors = QueryBuilder.selectors;
// Bind events
this.on('afterInit', function() {
self.$el.on('click.queryBuilder', '[data-invert=group]', function() {
var $group = $(this).closest(Selectors.group_container);
self.invert(self.getModel($group), options);
});
if (options.display_rules_button && options.invert_rules) {
self.$el.on('click.queryBuilder', '[data-invert=rule]', function() {
var $rule = $(this).closest(Selectors.rule_container);
self.invert(self.getModel($rule), options);
});
}
});
// Modify templates
if (!options.disable_template) {
this.on('getGroupTemplate.filter', function(h) {
var $h = $(h.value);
$h.find(Selectors.condition_container).after(
'<button type="button" class="btn btn-xs btn-default" data-invert="group">' +
'<i class="' + options.icon + '"></i> ' + self.translate('invert') +
'</button>'
);
h.value = $h.prop('outerHTML');
});
if (options.display_rules_button && options.invert_rules) {
this.on('getRuleTemplate.filter', function(h) {
var $h = $(h.value);
$h.find(Selectors.rule_actions).prepend(
'<button type="button" class="btn btn-xs btn-default" data-invert="rule">' +
'<i class="' + options.icon + '"></i> ' + self.translate('invert') +
'</button>'
);
h.value = $h.prop('outerHTML');
});
}
}
}, {
icon: 'glyphicon glyphicon-random',
recursive: true,
invert_rules: true,
display_rules_button: false,
silent_fail: false,
disable_template: false
});
QueryBuilder.defaults({
operatorOpposites: {
'equal': 'not_equal',
'not_equal': 'equal',
'in': 'not_in',
'not_in': 'in',
'less': 'greater_or_equal',
'less_or_equal': 'greater',
'greater': 'less_or_equal',
'greater_or_equal': 'less',
'between': 'not_between',
'not_between': 'between',
'begins_with': 'not_begins_with',
'not_begins_with': 'begins_with',
'contains': 'not_contains',
'not_contains': 'contains',
'ends_with': 'not_ends_with',
'not_ends_with': 'ends_with',
'is_empty': 'is_not_empty',
'is_not_empty': 'is_empty',
'is_null': 'is_not_null',
'is_not_null': 'is_null'
},
conditionOpposites: {
'AND': 'OR',
'OR': 'AND'
}
});
QueryBuilder.extend(/** @lends module:plugins.Invert.prototype */ {
/**
* Invert a Group, a Rule or the whole builder
* @param {Node} [node]
* @param {object} [options] {@link module:plugins.Invert}
* @fires module:plugins.Invert.afterInvert
* @throws InvertConditionError, InvertOperatorError
*/
invert: function(node, options) {
if (!(node instanceof Node)) {
if (!this.model.root) return;
options = node;
node = this.model.root;
}
if (typeof options != 'object') options = {};
if (options.recursive === undefined) options.recursive = true;
if (options.invert_rules === undefined) options.invert_rules = true;
if (options.silent_fail === undefined) options.silent_fail = false;
if (options.trigger === undefined) options.trigger = true;
if (node instanceof Group) {
// invert group condition
if (this.settings.conditionOpposites[node.condition]) {
node.condition = this.settings.conditionOpposites[node.condition];
}
else if (!options.silent_fail) {
Utils.error('InvertCondition', 'Unknown inverse of condition "{0}"', node.condition);
}
// recursive call
if (options.recursive) {
var tempOpts = $.extend({}, options, { trigger: false });
node.each(function(rule) {
if (options.invert_rules) {
this.invert(rule, tempOpts);
}
}, function(group) {
this.invert(group, tempOpts);
}, this);
}
}
else if (node instanceof Rule) {
if (node.operator && !node.filter.no_invert) {
// invert rule operator
if (this.settings.operatorOpposites[node.operator.type]) {
var invert = this.settings.operatorOpposites[node.operator.type];
// check if the invert is "authorized"
if (!node.filter.operators || node.filter.operators.indexOf(invert) != -1) {
node.operator = this.getOperatorByType(invert);
}
}
else if (!options.silent_fail) {
Utils.error('InvertOperator', 'Unknown inverse of operator "{0}"', node.operator.type);
}
}
}
if (options.trigger) {
/**
* After {@link module:plugins.Invert.invert} method
* @event afterInvert
* @memberof module:plugins.Invert
* @param {Node} node - the main group or rule that has been modified
* @param {object} options
*/
this.trigger('afterInvert', node, options);
this.trigger('rulesChanged');
}
}
});