From 62f6f446419926488178977963cc79eefa1e4275 Mon Sep 17 00:00:00 2001
From: Ruud
Date: Wed, 1 Feb 2017 16:34:56 +0100
Subject: [PATCH 001/104] Merge pull request #428 Fix #426 add "skip_empty"
flag to "getRules"
---
examples/index.html | 5 ++++-
src/public.js | 35 ++++++++++++++++++++++++------
tests/data.module.js | 51 +++++++++++++++++++++++++++++++++++++-------
3 files changed, 76 insertions(+), 15 deletions(-)
diff --git a/examples/index.html b/examples/index.html
index 58cab1fe..6704b236 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -595,7 +595,10 @@ Output
$('.parse-json').on('click', function() {
$('#result').removeClass('hide')
.find('pre').html(JSON.stringify(
- $('#builder').queryBuilder('getRules', { get_flags: true }),
+ $('#builder').queryBuilder('getRules', {
+ get_flags: true,
+ skip_empty: true
+ }),
undefined, 2
));
});
diff --git a/src/public.js b/src/public.js
index 70db106d..7d85e666 100644
--- a/src/public.js
+++ b/src/public.js
@@ -72,9 +72,15 @@ QueryBuilder.prototype.getModel = function(target) {
/**
* Validate the whole builder
+ * @param {object} options
+ * - skip_empty: false[default] | true(skips validating rules that have no filter selected)
* @return {boolean}
*/
-QueryBuilder.prototype.validate = function() {
+QueryBuilder.prototype.validate = function(options) {
+ options = $.extend({
+ skip_empty: false
+ }, options);
+
this.clearErrors();
var self = this;
@@ -84,6 +90,10 @@ QueryBuilder.prototype.validate = function() {
var errors = 0;
group.each(function(rule) {
+ if (!rule.filter && options.skip_empty) {
+ return;
+ }
+
if (!rule.filter) {
self.triggerValidationError(rule, 'no_filter', null);
errors++;
@@ -109,10 +119,11 @@ QueryBuilder.prototype.validate = function() {
done++;
}, function(group) {
- if (parse(group)) {
+ var res = parse(group);
+ if (res === true) {
done++;
}
- else {
+ else if (res === false) {
errors++;
}
});
@@ -120,6 +131,9 @@ QueryBuilder.prototype.validate = function() {
if (errors > 0) {
return false;
}
+ else if (done === 0 && !group.isRoot() && options.skip_empty) {
+ return null;
+ }
else if (done === 0 && (!self.settings.allow_empty || !group.isRoot())) {
self.triggerValidationError(group, 'empty_group', null);
return false;
@@ -137,15 +151,17 @@ QueryBuilder.prototype.validate = function() {
* @param {object} options
* - get_flags: false[default] | true(only changes from default flags) | 'all'
* - allow_invalid: false[default] | true(returns rules even if they are invalid)
+ * - skip_empty: false[default] | true(remove rules that have no filter selected)
* @return {object}
*/
QueryBuilder.prototype.getRules = function(options) {
options = $.extend({
get_flags: false,
- allow_invalid: false
+ allow_invalid: false,
+ skip_empty: false
}, options);
- var valid = this.validate();
+ var valid = this.validate(options);
if (!valid && !options.allow_invalid) {
return null;
}
@@ -170,6 +186,10 @@ QueryBuilder.prototype.getRules = function(options) {
}
group.each(function(rule) {
+ if (!rule.filter && options.skip_empty) {
+ return;
+ }
+
var value = null;
if (!rule.operator || rule.operator.nb_inputs !== 0) {
value = rule.value;
@@ -198,7 +218,10 @@ QueryBuilder.prototype.getRules = function(options) {
groupData.rules.push(self.change('ruleToJson', ruleData, rule));
}, function(model) {
- groupData.rules.push(parse(model));
+ var data = parse(model);
+ if (data.rules.length !== 0 || !options.skip_empty) {
+ groupData.rules.push(data);
+ }
}, this);
return self.change('groupToJson', groupData, group);
diff --git a/tests/data.module.js b/tests/data.module.js
index a83c48cd..358df511 100644
--- a/tests/data.module.js
+++ b/tests/data.module.js
@@ -1,4 +1,4 @@
-$(function(){
+$(function() {
var $b = $('#builder');
QUnit.module('data', {
@@ -31,9 +31,9 @@ $(function(){
type: 'string',
input: 'select',
values: [
- {one: 'One'},
- {two: 'Two'},
- {three: 'Three'}
+ { one: 'One' },
+ { two: 'Two' },
+ { three: 'Three' }
]
}],
rules: {
@@ -207,7 +207,7 @@ $(function(){
QUnit.test('custom data', function(assert) {
var rules = {
condition: 'AND',
- data: [1,2,3],
+ data: [1, 2, 3],
rules: [{
id: 'name',
value: 'Mistic',
@@ -361,13 +361,13 @@ $(function(){
};
assert.rulesMatch(
- $b.queryBuilder('getRules', {get_flags: true}),
+ $b.queryBuilder('getRules', { get_flags: true }),
rules_changed_flags,
'Should export rules with changed flags'
);
assert.rulesMatch(
- $b.queryBuilder('getRules', {get_flags: 'all'}),
+ $b.queryBuilder('getRules', { get_flags: 'all' }),
rules_all_flags,
'Should export rules with all flags'
);
@@ -455,13 +455,48 @@ $(function(){
);
});
+ /**
+ * Test skip_empty option
+ */
+ QUnit.test('skip empty', function(assert) {
+ $b.queryBuilder({
+ filters: basic_filters
+ });
+
+ $b.queryBuilder('setRules', {
+ condition: 'AND',
+ rules: [{
+ id: 'name',
+ operator: 'equal',
+ value: 'Mistic'
+ }, {
+ empty: true
+ }]
+ });
+
+ assert.rulesMatch(
+ $b.queryBuilder('getRules', {
+ skip_empty: true
+ }),
+ {
+ condition: 'AND',
+ rules: [{
+ id: 'name',
+ operator: 'equal',
+ value: 'Mistic'
+ }]
+ },
+ 'Should skip empty rules for getRules'
+ );
+ });
+
/**
* Test allow_empty_value option
*/
QUnit.test('allow empty value', function(assert) {
var filters = $.extend(true, [], basic_filters);
filters.forEach(function(filter) {
- filter.validation = $.extend({allow_empty_value: true}, filter.validation);
+ filter.validation = $.extend({ allow_empty_value: true }, filter.validation);
});
$b.queryBuilder({
From 2aa23bc47328d61c5b7cfd80324bda0111eca672 Mon Sep 17 00:00:00 2001
From: mreishus
Date: Thu, 9 Feb 2017 09:22:35 -0600
Subject: [PATCH 002/104] Add beforeReset / beforeClear events
---
src/public.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/public.js b/src/public.js
index 7d85e666..a2c1325f 100644
--- a/src/public.js
+++ b/src/public.js
@@ -23,6 +23,8 @@ QueryBuilder.prototype.destroy = function() {
* Reset the plugin
*/
QueryBuilder.prototype.reset = function() {
+ this.trigger('beforeReset');
+
this.status.group_id = 1;
this.status.rule_id = 0;
@@ -37,6 +39,8 @@ QueryBuilder.prototype.reset = function() {
* Clear the plugin
*/
QueryBuilder.prototype.clear = function() {
+ this.trigger('beforeClear');
+
this.status.group_id = 0;
this.status.rule_id = 0;
From 1f73bd8193e770b9dcc12c5ca466c7b6f90b72f0 Mon Sep 17 00:00:00 2001
From: mistic100
Date: Sun, 12 Feb 2017 13:10:30 +0100
Subject: [PATCH 003/104] Fix #431 re-create input when operators are from
different optgroups
---
src/core.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/core.js b/src/core.js
index 3ffe3763..22149887 100644
--- a/src/core.js
+++ b/src/core.js
@@ -658,7 +658,10 @@ QueryBuilder.prototype.updateRuleOperator = function(rule, previousOperator) {
else {
$valueContainer.show();
- if ($valueContainer.is(':empty') || !previousOperator || rule.operator.nb_inputs !== previousOperator.nb_inputs) {
+ if ($valueContainer.is(':empty') || !previousOperator ||
+ rule.operator.nb_inputs !== previousOperator.nb_inputs ||
+ rule.operator.optgroup !== previousOperator.optgroup
+ ) {
this.createRuleInput(rule);
}
}
From cba0b70669aae07d0d2fb223d8928e2055f84509 Mon Sep 17 00:00:00 2001
From: mistic100
Date: Sun, 12 Feb 2017 18:52:10 +0100
Subject: [PATCH 004/104] a LOT of jsDoc
---
.gitignore | 1 +
Gruntfile.js | 13 +
README.md | 2 +-
package.json | 2 +
src/core.js | 332 +++++++++++++-----
src/data.js | 144 +++++---
src/defaults.js | 24 +-
src/main.js | 138 ++++----
src/model.js | 421 +++++++++++++++--------
src/plugins/bt-checkbox/plugin.js | 23 +-
src/plugins/bt-checkbox/plugins.scss | 12 +
src/plugins/bt-selectpicker/plugin.js | 16 +-
src/plugins/bt-tooltip-errors/plugin.js | 15 +-
src/plugins/change-filters/plugin.js | 101 +++---
src/plugins/filter-description/plugin.js | 16 +-
src/plugins/invert/plugin.js | 27 +-
src/plugins/mongodb-support/plugin.js | 159 ++++++---
src/plugins/not-group/plugin.js | 38 +-
src/plugins/sortable/plugin.js | 40 ++-
src/plugins/sql-support/plugin.js | 225 ++++++++----
src/plugins/unique-filter/plugin.js | 28 +-
src/public.js | 136 ++++++--
src/template.js | 136 +++++---
src/utils.js | 76 ++--
24 files changed, 1450 insertions(+), 675 deletions(-)
create mode 100644 src/plugins/bt-checkbox/plugins.scss
diff --git a/.gitignore b/.gitignore
index f4b28947..2b7141a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
bower_components
node_modules
dist
+doc
.sass-cache
.coverage-results
.idea
diff --git a/Gruntfile.js b/Gruntfile.js
index 3725a9be..208f6f2c 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -300,6 +300,19 @@ module.exports = function(grunt) {
}
},
+ // jsDoc generation
+ jsdoc: {
+ lib: {
+ src: ['src/**/*.js', '!src/**/.wrapper.js'],
+ dest: 'doc',
+ options: {
+ private: false,
+ template: 'node_modules/docdash',
+ readme: 'README.md'
+ }
+ }
+ },
+
// inject sources files and tests modules in demo and test
injector: {
options: {
diff --git a/README.md b/README.md
index 4aa83f81..708b26c4 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ jQuery plugin offering an simple interface to create complex queries.
[](http://querybuilder.js.org)
## Documentation
-http://querybuilder.js.org
+[querybuilder.js.org](http://querybuilder.js.org)
### Dependencies
* jQuery >= 1.10
diff --git a/package.json b/package.json
index f4c96d12..2e57f8bf 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
},
"devDependencies": {
"deepmerge": "^0.2.0",
+ "docdash": "^0.4.0",
"grunt": "^1.0.0",
"grunt-banner": "^0.6.0",
"grunt-contrib-clean": "^1.0.0",
@@ -31,6 +32,7 @@
"grunt-coveralls": "^1.0.0",
"grunt-injector": "^1.1.0",
"grunt-jscs": "^2.8.0",
+ "grunt-jsdoc": "^2.1.0",
"grunt-open": "^0.2.3",
"grunt-qunit-blanket-lcov": "^0.3.0",
"grunt-sass-injection": "^1.0.3",
diff --git a/src/core.js b/src/core.js
index 22149887..7e82498a 100644
--- a/src/core.js
+++ b/src/core.js
@@ -1,5 +1,9 @@
/**
- * Init the builder
+ * Inits the builder
+ * @param {jQuery} $el
+ * @param {object} options
+ * @fires QueryBuilder#afterInit
+ * @private
*/
QueryBuilder.prototype.init = function($el, options) {
$el[0].queryBuilder = this;
@@ -63,6 +67,9 @@ QueryBuilder.prototype.init = function($el, options) {
this.bindEvents();
this.initPlugins();
+ /**
+ * @event QueryBuilder#afterInit
+ */
this.trigger('afterInit');
if (options.rules) {
@@ -74,8 +81,43 @@ QueryBuilder.prototype.init = function($el, options) {
}
};
+/**
+ * Initializes plugins for an instance
+ * @throws ConfigError
+ * @private
+ */
+QueryBuilder.prototype.initPlugins = function() {
+ if (!this.plugins) {
+ return;
+ }
+
+ if ($.isArray(this.plugins)) {
+ var tmp = {};
+ this.plugins.forEach(function(plugin) {
+ tmp[plugin] = null;
+ });
+ this.plugins = tmp;
+ }
+
+ Object.keys(this.plugins).forEach(function(plugin) {
+ if (plugin in QueryBuilder.plugins) {
+ this.plugins[plugin] = $.extend(true, {},
+ QueryBuilder.plugins[plugin].def,
+ this.plugins[plugin] || {}
+ );
+
+ QueryBuilder.plugins[plugin].fct.call(this, this.plugins[plugin]);
+ }
+ else {
+ Utils.error('Config', 'Unable to find plugin "{0}"', plugin);
+ }
+ }, this);
+};
+
/**
* Checks the configuration of each filter
+ * @param {object[]} filters
+ * @returns {object[]}
* @throws ConfigError
*/
QueryBuilder.prototype.checkFilters = function(filters) {
@@ -136,7 +178,8 @@ QueryBuilder.prototype.checkFilters = function(filters) {
}
switch (filter.input) {
- case 'radio': case 'checkbox':
+ case 'radio':
+ case 'checkbox':
if (!filter.values || filter.values.length < 1) {
Utils.error('Config', 'Missing filter "{0}" values', filter.id);
}
@@ -164,7 +207,7 @@ QueryBuilder.prototype.checkFilters = function(filters) {
else {
var self = this;
filters.sort(function(a, b) {
- return self.translateLabel(a.label).localeCompare(self.translateLabel(b.label));
+ return self.getTranslatedLabel(a.label).localeCompare(self.getTranslatedLabel(b.label));
});
}
}
@@ -178,6 +221,8 @@ QueryBuilder.prototype.checkFilters = function(filters) {
/**
* Checks the configuration of each operator
+ * @param {object[]} operators
+ * @returns {object[]}
* @throws ConfigError
*/
QueryBuilder.prototype.checkOperators = function(operators) {
@@ -231,54 +276,56 @@ QueryBuilder.prototype.checkOperators = function(operators) {
};
/**
- * Add all events listeners
+ * Adds all events listeners to the builder
+ * @private
*/
QueryBuilder.prototype.bindEvents = function() {
var self = this;
+ var Selectors = QueryBuilder.selectors;
// group condition change
this.$el.on('change.queryBuilder', Selectors.group_condition, function() {
if ($(this).is(':checked')) {
var $group = $(this).closest(Selectors.group_container);
- Model($group).condition = $(this).val();
+ self.getModel($group).condition = $(this).val();
}
});
// rule filter change
this.$el.on('change.queryBuilder', Selectors.rule_filter, function() {
var $rule = $(this).closest(Selectors.rule_container);
- Model($rule).filter = self.getFilterById($(this).val());
+ self.getModel($rule).filter = self.getFilterById($(this).val());
});
// rule operator change
this.$el.on('change.queryBuilder', Selectors.rule_operator, function() {
var $rule = $(this).closest(Selectors.rule_container);
- Model($rule).operator = self.getOperatorByType($(this).val());
+ self.getModel($rule).operator = self.getOperatorByType($(this).val());
});
// add rule button
this.$el.on('click.queryBuilder', Selectors.add_rule, function() {
var $group = $(this).closest(Selectors.group_container);
- self.addRule(Model($group));
+ self.addRule(self.getModel($group));
});
// delete rule button
this.$el.on('click.queryBuilder', Selectors.delete_rule, function() {
var $rule = $(this).closest(Selectors.rule_container);
- self.deleteRule(Model($rule));
+ self.deleteRule(self.getModel($rule));
});
if (this.settings.allow_groups !== 0) {
// add group button
this.$el.on('click.queryBuilder', Selectors.add_group, function() {
var $group = $(this).closest(Selectors.group_container);
- self.addGroup(Model($group));
+ self.addGroup(self.getModel($group));
});
// delete group button
this.$el.on('click.queryBuilder', Selectors.delete_group, function() {
var $group = $(this).closest(Selectors.group_container);
- self.deleteGroup(Model($group));
+ self.deleteGroup(self.getModel($group));
});
}
@@ -288,12 +335,12 @@ QueryBuilder.prototype.bindEvents = function() {
node.$el.remove();
self.refreshGroupsConditions();
},
- 'add': function(e, node, index) {
+ 'add': function(e, parent, node, index) {
if (index === 0) {
- node.$el.prependTo(node.parent.$el.find('>' + Selectors.rules_list));
+ node.$el.prependTo(parent.$el.find('>' + QueryBuilder.selectors.rules_list));
}
else {
- node.$el.insertAfter(node.parent.rules[index - 1].$el);
+ node.$el.insertAfter(parent.rules[index - 1].$el);
}
self.refreshGroupsConditions();
},
@@ -301,7 +348,7 @@ QueryBuilder.prototype.bindEvents = function() {
node.$el.detach();
if (index === 0) {
- node.$el.prependTo(group.$el.find('>' + Selectors.rules_list));
+ node.$el.prependTo(group.$el.find('>' + QueryBuilder.selectors.rules_list));
}
else {
node.$el.insertAfter(group.rules[index - 1].$el);
@@ -312,7 +359,7 @@ QueryBuilder.prototype.bindEvents = function() {
if (node instanceof Rule) {
switch (field) {
case 'error':
- self.displayError(node);
+ self.updateError(node);
break;
case 'flags':
@@ -335,7 +382,7 @@ QueryBuilder.prototype.bindEvents = function() {
else {
switch (field) {
case 'error':
- self.displayError(node);
+ self.updateError(node);
break;
case 'flags':
@@ -352,11 +399,12 @@ QueryBuilder.prototype.bindEvents = function() {
};
/**
- * Create the root group
- * @param addRule {bool,optional} add a default empty rule
- * @param data {mixed,optional} group custom data
- * @param flags {object,optional} flags to apply to the group
- * @return group {Root}
+ * Creates the root group
+ * @param {boolean} [addRule=true] - adds a default empty rule
+ * @param {object} [data] - group custom data
+ * @param {object} [flags] - flags to apply to the group
+ * @returns {Group} root group
+ * @fires QueryBuilder#afterAddGroup
*/
QueryBuilder.prototype.setRoot = function(addRule, data, flags) {
addRule = (addRule === undefined || addRule === true);
@@ -383,18 +431,27 @@ QueryBuilder.prototype.setRoot = function(addRule, data, flags) {
};
/**
- * Add a new group
- * @param parent {Group}
- * @param addRule {bool,optional} add a default empty rule
- * @param data {mixed,optional} group custom data
- * @param flags {object,optional} flags to apply to the group
- * @return group {Group}
+ * Adds a new group
+ * @param {Group} parent
+ * @param {boolean} [addRule=true] - adds a default empty rule
+ * @param {object} [data] - group custom data
+ * @param {object} [flags] - flags to apply to the group
+ * @returns {Group}
+ * @fires QueryBuilder#beforeAddGroup
+ * @fires QueryBuilder#afterAddGroup
*/
QueryBuilder.prototype.addGroup = function(parent, addRule, data, flags) {
addRule = (addRule === undefined || addRule === true);
var level = parent.level + 1;
+ /**
+ * Preventable
+ * @event QueryBuilder#beforeAddGroup
+ * @param {Group} parent
+ * @param {boolean} addRule
+ * @param {int} level
+ */
var e = this.trigger('beforeAddGroup', parent, addRule, level);
if (e.isDefaultPrevented()) {
return null;
@@ -407,6 +464,10 @@ QueryBuilder.prototype.addGroup = function(parent, addRule, data, flags) {
model.data = data;
model.__.flags = $.extend({}, this.settings.default_group_flags, flags);
+ /**
+ * @event QueryBuilder#afterAddGroup
+ * @param {Group} group
+ */
this.trigger('afterAddGroup', model);
model.condition = this.settings.default_condition;
@@ -419,15 +480,22 @@ QueryBuilder.prototype.addGroup = function(parent, addRule, data, flags) {
};
/**
- * Tries to delete a group. The group is not deleted if at least one rule is no_delete.
- * @param group {Group}
- * @return {boolean} true if the group has been deleted
+ * Tries to delete a group. The group is not deleted if at least one rule is flagged `no_delete`.
+ * @param {Group} group
+ * @returns {boolean} if the group has been deleted
+ * @fires QueryBuilder#beforeDeleteGroup
+ * @fires QueryBuilder#afterDeleteGroup
*/
QueryBuilder.prototype.deleteGroup = function(group) {
if (group.isRoot()) {
return false;
}
+ /**
+ * Preventable
+ * @event QueryBuilder#beforeDeleteGroup
+ * @param {Group} parent
+ */
var e = this.trigger('beforeDeleteGroup', group);
if (e.isDefaultPrevented()) {
return false;
@@ -436,13 +504,17 @@ QueryBuilder.prototype.deleteGroup = function(group) {
var del = true;
group.each('reverse', function(rule) {
- del&= this.deleteRule(rule);
+ del &= this.deleteRule(rule);
}, function(group) {
- del&= this.deleteGroup(group);
+ del &= this.deleteGroup(group);
}, this);
if (del) {
group.drop();
+
+ /**
+ * @event QueryBuilder#afterDeleteGroup
+ */
this.trigger('afterDeleteGroup');
}
@@ -450,43 +522,57 @@ QueryBuilder.prototype.deleteGroup = function(group) {
};
/**
- * Changes the condition of a group
- * @param group {Group}
+ * Performs actions when a group's condition changes
+ * @param {Group} group
+ * @fires QueryBuilder#afterUpdateGroupCondition
+ * @private
*/
QueryBuilder.prototype.updateGroupCondition = function(group) {
- group.$el.find('>' + Selectors.group_condition).each(function() {
+ group.$el.find('>' + QueryBuilder.selectors.group_condition).each(function() {
var $this = $(this);
$this.prop('checked', $this.val() === group.condition);
$this.parent().toggleClass('active', $this.val() === group.condition);
});
+ /**
+ * @event QueryBuilder#afterUpdateGroupCondition
+ * @param {Group} group
+ */
this.trigger('afterUpdateGroupCondition', group);
};
/**
- * Update visibility of conditions based on number of rules inside each group
+ * Updates the visibility of conditions based on number of rules inside each group
+ * @private
*/
QueryBuilder.prototype.refreshGroupsConditions = function() {
(function walk(group) {
if (!group.flags || (group.flags && !group.flags.condition_readonly)) {
- group.$el.find('>' + Selectors.group_condition).prop('disabled', group.rules.length <= 1)
+ group.$el.find('>' + QueryBuilder.selectors.group_condition).prop('disabled', group.rules.length <= 1)
.parent().toggleClass('disabled', group.rules.length <= 1);
}
- group.each(function(rule) {}, function(group) {
+ group.each(null, function(group) {
walk(group);
}, this);
}(this.model.root));
};
/**
- * Add a new rule
- * @param parent {Group}
- * @param data {mixed,optional} rule custom data
- * @param flags {object,optional} flags to apply to the rule
- * @return rule {Rule}
+ * Adds a new rule
+ * @param {Group} parent
+ * @param {object} [data] - rule custom data
+ * @param {object} [flags] - flags to apply to the rule
+ * @returns {Rule}
+ * @fires QueryBuilder#beforeAddRule
+ * @fires QueryBuilder#afterAddRule
*/
QueryBuilder.prototype.addRule = function(parent, data, flags) {
+ /**
+ * Preventable
+ * @event QueryBuilder#beforeAddRule
+ * @param {Group} parent
+ */
var e = this.trigger('beforeAddRule', parent);
if (e.isDefaultPrevented()) {
return null;
@@ -502,6 +588,10 @@ QueryBuilder.prototype.addRule = function(parent, data, flags) {
model.__.flags = $.extend({}, this.settings.default_rule_flags, flags);
+ /**
+ * @event QueryBuilder#afterAddRule
+ * @param {Rule} rule
+ */
this.trigger('afterAddRule', model);
this.createRuleFilters(model);
@@ -517,15 +607,22 @@ QueryBuilder.prototype.addRule = function(parent, data, flags) {
};
/**
- * Delete a rule.
- * @param rule {Rule}
- * @return {boolean} true if the rule has been deleted
+ * Tries to delete a rule
+ * @param {Rule} rule
+ * @returns {boolean} if the rule has been deleted
+ * @fires QueryBuilder#beforeDeleteRule
+ * @fires QueryBuilder#afterDeleteRule
*/
QueryBuilder.prototype.deleteRule = function(rule) {
if (rule.flags.no_delete) {
return false;
}
+ /**
+ * Preventable
+ * @event QueryBuilder#beforeDeleteRule
+ * @param {Rule} rule
+ */
var e = this.trigger('beforeDeleteRule', rule);
if (e.isDefaultPrevented()) {
return false;
@@ -533,30 +630,41 @@ QueryBuilder.prototype.deleteRule = function(rule) {
rule.drop();
+ /**
+ * @event QueryBuilder#afterDeleteRule
+ */
this.trigger('afterDeleteRule');
return true;
};
/**
- * Create the filters