Skip to content

Commit d9c8018

Browse files
committed
new "invert" plugin
1 parent d811944 commit d9c8018

File tree

7 files changed

+194
-17
lines changed

7 files changed

+194
-17
lines changed

examples/index.html

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<link rel="stylesheet" href="../dist/css/query-builder.default.css" id="qb-theme">
1616

1717
<link rel="stylesheet" href="http://mistic100.github.io/jQuery-QueryBuilder/assets/flags/flags.css">
18+
<style>
19+
.flag { display: inline-block; }
20+
</style>
1821
</head>
1922

2023
<body>
@@ -39,22 +42,20 @@ <h1>jQuery QueryBuilder <small>Example</small></h1>
3942

4043
<label>Language:</label>
4144
<select name="language" class="selectpicker show-tick show-menu-arrow" data-width="auto">
42-
<optgroup label="Complete">
43-
<option value="de" data-icon="flag flag-de">German</option>
44-
<option value="en" data-icon="flag flag-gb" selected>English</option>
45-
<option value="es" data-icon="flag flag-es">Spanish</option>
46-
<option value="fr" data-icon="flag flag-fr">French</option>
47-
<option value="nl" data-icon="flag flag-nl">Dutch</option>
48-
<option value="pl" data-icon="flag flag-pl">Polish</option>
49-
<option value="pt-BR" data-icon="flag flag-pt">Portuguese</option>
50-
<option value="ru" data-icon="flag flag-ru">Russian</option>
51-
</optgroup>
52-
<optgroup label="Partial">
53-
<option value="da" data-icon="flag flag-dk">Danish</option>
54-
<option value="it" data-icon="flag flag-it">Italian</option>
55-
<option value="no" data-icon="flag flag-no">Norwegian</option>
56-
<option value="ro" data-icon="flag flag-ro">Romanian</option>
57-
</optgroup>
45+
<option value="sq" data-icon="flag flag-al">Albanian</option>
46+
<option value="de" data-icon="flag flag-de">German</option>
47+
<option value="da" data-icon="flag flag-dk">Danish</option>
48+
<option value="en" data-icon="flag flag-gb" selected>English</option>
49+
<option value="es" data-icon="flag flag-es">Spanish</option>
50+
<option value="fr" data-icon="flag flag-fr">French</option>
51+
<option value="it" data-icon="flag flag-it">Italian</option>
52+
<option value="fa-IR" data-icon="flag flag-ir">Farsi</option>
53+
<option value="nl" data-icon="flag flag-nl">Dutch</option>
54+
<option value="no" data-icon="flag flag-no">Norwegian</option>
55+
<option value="pl" data-icon="flag flag-pl">Polish</option>
56+
<option value="pt-BR" data-icon="flag flag-pt">Portuguese</option>
57+
<option value="ro" data-icon="flag flag-ro">Romanian</option>
58+
<option value="ru" data-icon="flag flag-ru">Russian</option>
5859
</select>
5960
</div>
6061

@@ -106,7 +107,8 @@ <h3>Output</h3>
106107
'filter-description': { mode: 'bootbox' },
107108
'bt-selectpicker': null,
108109
'unique-filter': null,
109-
'bt-checkbox': { color: 'primary' }
110+
'bt-checkbox': { color: 'primary' },
111+
'invert': null
110112
},
111113

112114
filters: [

src/plugins/invert/i18n/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"invert": "Invert"
3+
}

src/plugins/invert/i18n/fr.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"invert": "Inverser"
3+
}

src/plugins/invert/plugin.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*!
2+
* jQuery QueryBuilder Invert
3+
* Allows to invert a rule operator, a group condition or the entire builder.
4+
* Copyright 2014-2015 Damien "Mistic" Sorel (http://www.strangeplanet.fr)
5+
*/
6+
7+
QueryBuilder.defaults({
8+
operatorOpposites: {
9+
'equal': 'not_equal',
10+
'not_equal': 'equal',
11+
'in': 'not_in',
12+
'not_in': 'in',
13+
'less': 'greater_or_equal',
14+
'less_or_equal': 'greater',
15+
'greater': 'less_or_equal',
16+
'greater_or_equal': 'less',
17+
'between': 'not_between',
18+
'not_between': 'between',
19+
'begins_with': 'not_begins_with',
20+
'not_begins_with': 'begins_with',
21+
'contains': 'not_contains',
22+
'not_contains': 'contains',
23+
'ends_with': 'not_ends_with',
24+
'not_ends_with': 'ends_with',
25+
'is_empty': 'is_not_empty',
26+
'is_not_empty': 'is_empty',
27+
'is_null': 'is_not_null',
28+
'is_not_null': 'is_null'
29+
},
30+
31+
conditionOpposites: {
32+
'AND': 'OR',
33+
'OR': 'AND'
34+
}
35+
});
36+
37+
QueryBuilder.define('invert', function(options) {
38+
var that = this;
39+
40+
/**
41+
* Bind events
42+
*/
43+
this.on('afterInit', function() {
44+
that.$el.on('click.queryBuilder', '[data-invert=group]', function() {
45+
var $group = $(this).closest('.rules-group-container');
46+
that.invert(Model($group), options.recursive, options.invert_rules);
47+
});
48+
49+
if (options.display_rules_button && options.invert_rules) {
50+
that.$el.on('click.queryBuilder', '[data-invert=rule]', function() {
51+
var $rule = $(this).closest('.rule-container');
52+
that.invert(Model($rule));
53+
});
54+
}
55+
});
56+
57+
/**
58+
* Modify templates
59+
*/
60+
this.on('getGroupTemplate.filter', function(h, level) {
61+
var $h = $(h.value);
62+
$h.find('.group-conditions').after('<button type="button" class="btn btn-xs btn-default" data-invert="group"><i class="' + options.icon + '"></i> '+ that.lang.invert +'</button>');
63+
h.value = $h.prop('outerHTML');
64+
});
65+
66+
if (options.display_rules_button && options.invert_rules) {
67+
this.on('getRuleTemplate.filter', function(h) {
68+
var $h = $(h.value);
69+
$h.find('.rule-actions').prepend('<button type="button" class="btn btn-xs btn-default" data-invert="rule"><i class="' + options.icon + '"></i> '+ that.lang.invert +'</button>');
70+
h.value = $h.prop('outerHTML');
71+
});
72+
}
73+
}, {
74+
icon: 'glyphicon glyphicon-random',
75+
recursive: true,
76+
invert_rules: true,
77+
display_rules_button: false
78+
});
79+
80+
QueryBuilder.extend({
81+
invert: function(node, recursive, invert_rules) {
82+
if (typeof node != 'object') {
83+
if (this.model.root) {
84+
this.invert(this.model.root, node, recursive);
85+
}
86+
}
87+
else if (node instanceof Group) {
88+
if (this.settings.conditionOpposites[node.condition]) {
89+
node.condition = this.settings.conditionOpposites[node.condition];
90+
}
91+
else {
92+
error('Unknown inverse of condition "{0}"', node.condition);
93+
}
94+
95+
if (recursive === true || recursive === undefined) {
96+
node.each(function(rule) {
97+
if (invert_rules === true || invert_rules === undefined) {
98+
this.invert(rule);
99+
}
100+
}, function(group) {
101+
this.invert(group, true);
102+
}, this);
103+
}
104+
}
105+
else if (node instanceof Rule) {
106+
if (node.operator) {
107+
if (this.settings.operatorOpposites[node.operator.type]) {
108+
node.operator = this.getOperatorByType(this.settings.operatorOpposites[node.operator.type]);
109+
}
110+
else {
111+
error('Unknown inverse of operator "{0}"', node.operator.type);
112+
}
113+
}
114+
}
115+
}
116+
});

src/plugins/invert/plugin.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.query-builder {
2+
.rules-group-header [data-invert] {
3+
margin-left: 5px;
4+
}
5+
}

tests/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
<script src="../src/plugins/bt-selectpicker/plugin.js" data-cover></script>
4444
<script src="../src/plugins/bt-tooltip-errors/plugin.js" data-cover></script>
4545
<script src="../src/plugins/filter-description/plugin.js" data-cover></script>
46+
<script src="../src/plugins/invert/plugin.js" data-cover></script>
4647
<script src="../src/plugins/mongodb-support/plugin.js" data-cover></script>
4748
<script src="../src/plugins/sortable/plugin.js" data-cover></script>
4849
<script src="../src/plugins/sql-support/plugin.js" data-cover></script>

tests/plugins.module.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,25 @@ $(function(){
382382
});
383383
});
384384

385+
/**
386+
* Test inversion
387+
*/
388+
QUnit.test('invert', function(assert) {
389+
$b.queryBuilder({
390+
plugins: ['invert'],
391+
filters: basic_filters,
392+
rules: basic_rules
393+
});
394+
395+
$b.queryBuilder('invert', true, true);
396+
397+
assert.rulesMatch(
398+
$b.queryBuilder('getRules'),
399+
basic_rules_invert,
400+
'Should have inverted all conditions and operators'
401+
);
402+
});
403+
385404

386405
var basic_rules_sql_raw = {
387406
sql: 'price < 10.25 AND name IS NULL AND ( category IN(\'mo\', \'mu\') OR id != \'1234-azer-5678\' ) '
@@ -793,4 +812,32 @@ $(function(){
793812

794813
var sorted_rules = $.extend(true, {}, basic_rules);
795814
sorted_rules.rules.splice(2, 0, sorted_rules.rules[2].rules.pop());
815+
816+
var basic_rules_invert = {
817+
condition: 'OR',
818+
rules: [{
819+
id: 'price',
820+
field: 'price',
821+
operator: 'greater_or_equal',
822+
value: 10.25
823+
}, {
824+
id: 'name',
825+
field: 'name',
826+
operator: 'is_not_null',
827+
value: null
828+
}, {
829+
condition: 'AND',
830+
rules: [{
831+
id: 'category',
832+
field: 'category',
833+
operator: 'not_in',
834+
value: ['mo', 'mu']
835+
}, {
836+
id: 'id',
837+
field: 'id',
838+
operator: 'equal',
839+
value: '1234-azer-5678'
840+
}]
841+
}]
842+
};
796843
});

0 commit comments

Comments
 (0)