From 5e3934e2989daa154807735168e3e1ed393f3966 Mon Sep 17 00:00:00 2001
From: Damien SOREL
Date: Thu, 22 Dec 2016 17:55:20 +0100
Subject: [PATCH 001/116] Fix #397 add "allow_invalid" flag
---
src/core.js | 2 +-
src/data.js | 20 +++++++++----
src/public.js | 70 ++++++++++++++++++++++++++++++++------------
src/utils.js | 26 ++++++++++------
tests/common.js | 4 +++
tests/core.module.js | 6 ++--
tests/data.module.js | 44 ++++++++++++++++++++++++++++
7 files changed, 135 insertions(+), 37 deletions(-)
diff --git a/src/core.js b/src/core.js
index 33138413..900d1890 100644
--- a/src/core.js
+++ b/src/core.js
@@ -657,7 +657,7 @@ QueryBuilder.prototype.updateRuleOperator = function(rule, previousOperator) {
else {
$valueContainer.show();
- if ($valueContainer.is(':empty') || rule.operator.nb_inputs !== previousOperator.nb_inputs) {
+ if ($valueContainer.is(':empty') || !previousOperator || rule.operator.nb_inputs !== previousOperator.nb_inputs) {
this.createRuleInput(rule);
}
}
diff --git a/src/data.js b/src/data.js
index 6cb5a28e..56099f5b 100644
--- a/src/data.js
+++ b/src/data.js
@@ -252,10 +252,11 @@ QueryBuilder.prototype.getOperators = function(filter) {
/**
* Returns a particular filter by its id
* @throws UndefinedFilterError
- * @param filterId {string}
+ * @param id {string}
+ * @param [doThrow=true] {boolean}
* @return {object|null}
*/
-QueryBuilder.prototype.getFilterById = function(id) {
+QueryBuilder.prototype.getFilterById = function(id, doThrow) {
if (id == '-1') {
return null;
}
@@ -266,16 +267,19 @@ QueryBuilder.prototype.getFilterById = function(id) {
}
}
- Utils.error('UndefinedFilter', 'Undefined filter "{0}"', id);
+ Utils.error(doThrow !== false, 'UndefinedFilter', 'Undefined filter "{0}"', id);
+
+ return null;
};
/**
* Return a particular operator by its type
* @throws UndefinedOperatorError
* @param type {string}
+ * @param [doThrow=true] {boolean}
* @return {object|null}
*/
-QueryBuilder.prototype.getOperatorByType = function(type) {
+QueryBuilder.prototype.getOperatorByType = function(type, doThrow) {
if (type == '-1') {
return null;
}
@@ -286,7 +290,9 @@ QueryBuilder.prototype.getOperatorByType = function(type) {
}
}
- Utils.error('UndefinedOperator', 'Undefined operator "{0}"', type);
+ Utils.error(doThrow !== false, 'UndefinedOperator', 'Undefined operator "{0}"', type);
+
+ return null;
};
/**
@@ -368,6 +374,10 @@ QueryBuilder.prototype.setRuleValue = function(rule, value) {
var filter = rule.filter;
var operator = rule.operator;
+ if (!filter || !operator) {
+ return;
+ }
+
if (filter.valueSetter) {
filter.valueSetter.call(this, rule, value);
}
diff --git a/src/public.js b/src/public.js
index 9c503043..b9db5d70 100644
--- a/src/public.js
+++ b/src/public.js
@@ -90,6 +90,12 @@ QueryBuilder.prototype.validate = function() {
return;
}
+ if (!rule.operator) {
+ self.triggerValidationError(rule, 'no_operator', null);
+ errors++;
+ return;
+ }
+
if (rule.operator.nb_inputs !== 0) {
var valid = self.validateValue(rule, rule.value);
@@ -130,15 +136,18 @@ QueryBuilder.prototype.validate = function() {
* Get an object representing current rules
* @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)
* @return {object}
*/
QueryBuilder.prototype.getRules = function(options) {
options = $.extend({
- get_flags: false
+ get_flags: false,
+ allow_invalid: false
}, options);
- if (!this.validate()) {
- return {};
+ var valid = this.validate();
+ if (!valid && !options.allow_invalid) {
+ return null;
}
var self = this;
@@ -162,20 +171,20 @@ QueryBuilder.prototype.getRules = function(options) {
group.each(function(rule) {
var value = null;
- if (rule.operator.nb_inputs !== 0) {
+ if (!rule.operator || rule.operator.nb_inputs !== 0) {
value = rule.value;
}
var ruleData = {
- id: rule.filter.id,
- field: rule.filter.field,
- type: rule.filter.type,
- input: rule.filter.input,
- operator: rule.operator.type,
+ id: rule.filter ? rule.filter.id : null,
+ field: rule.filter ? rule.filter.field : null,
+ type: rule.filter ? rule.filter.type : null,
+ input: rule.filter ? rule.filter.input : null,
+ operator: rule.operator ? rule.operator.type : null,
value: value
};
- if (rule.filter.data || rule.data) {
+ if (rule.filter && rule.filter.data || rule.data) {
ruleData.data = $.extendext(true, 'replace', {}, rule.filter.data, rule.data);
}
@@ -196,6 +205,8 @@ QueryBuilder.prototype.getRules = function(options) {
}(this.model.root));
+ out.valid = valid;
+
return this.change('getRules', out);
};
@@ -203,8 +214,14 @@ QueryBuilder.prototype.getRules = function(options) {
* Set rules from object
* @throws RulesError, UndefinedConditionError
* @param data {object}
+ * @param {object} options
+ * - allow_invalid: false[default] | true(silent-fail if the data are invalid)
*/
-QueryBuilder.prototype.setRules = function(data) {
+QueryBuilder.prototype.setRules = function(data, options) {
+ options = $.extend({
+ allow_invalid: false
+ }, options);
+
if ($.isArray(data)) {
data = {
condition: this.settings.default_condition,
@@ -232,7 +249,10 @@ QueryBuilder.prototype.setRules = function(data) {
data.condition = self.settings.default_condition;
}
else if (self.settings.conditions.indexOf(data.condition) == -1) {
- Utils.error('UndefinedCondition', 'Invalid condition "{0}"', data.condition);
+ if (options.allow_invalid) {
+ data.condition = self.settings.default_condition;
+ }
+ Utils.error(!options.allow_invalid, 'UndefinedCondition', 'Invalid condition "{0}"', data.condition);
}
group.condition = data.condition;
@@ -242,8 +262,10 @@ QueryBuilder.prototype.setRules = function(data) {
if (item.rules !== undefined) {
if (self.settings.allow_groups !== -1 && self.settings.allow_groups < group.level) {
- self.reset();
- Utils.error('RulesParse', 'No more than {0} groups are allowed', self.settings.allow_groups);
+ if (!options.allow_invalid) {
+ self.reset();
+ }
+ Utils.error(!options.allow_invalid, 'RulesParse', 'No more than {0} groups are allowed', self.settings.allow_groups);
}
else {
model = self.addGroup(group, false, item.data, self.parseGroupFlags(item));
@@ -257,7 +279,10 @@ QueryBuilder.prototype.setRules = function(data) {
else {
if (!item.empty) {
if (item.id === undefined) {
- Utils.error('RulesParse', 'Missing rule field id');
+ if (options.allow_invalid) {
+ item.empty = true;
+ }
+ Utils.error(!options.allow_invalid, 'RulesParse', 'Missing rule field id');
}
if (item.operator === undefined) {
item.operator = 'equal';
@@ -270,11 +295,18 @@ QueryBuilder.prototype.setRules = function(data) {
}
if (!item.empty) {
- model.filter = self.getFilterById(item.id);
- model.operator = self.getOperatorByType(item.operator);
+ model.filter = self.getFilterById(item.id, !options.allow_invalid);
+
+ if (model.filter) {
+ model.operator = self.getOperatorByType(item.operator, !options.allow_invalid);
+
+ if (!model.operator) {
+ model.operator = self.getOperators(model.filter)[0];
+ }
- if (model.operator.nb_inputs !== 0 && item.value !== undefined) {
- model.value = item.value;
+ if (model.operator && model.operator.nb_inputs !== 0 && item.value !== undefined) {
+ model.value = item.value;
+ }
}
}
diff --git a/src/utils.js b/src/utils.js
index f97cf86d..bd0daf87 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -50,20 +50,28 @@ Utils.fmt = function(str, args) {
};
/**
- * Throw an Error object with custom name
+ * Throw an Error object with custom name or logs an error
+ * @param [doThrow=true] {boolean}
* @param type {string}
* @param message {string}
* @param args,... {Array|*}
*/
-Utils.error = function(type, message, args) {
- if (!Array.isArray(args)) {
- args = Array.prototype.slice.call(arguments, 2);
+Utils.error = function() {
+ var i = 0;
+ var doThrow = typeof arguments[i] === 'boolean' ? arguments[i++] : true;
+ var type = arguments[i++];
+ var message = arguments[i++];
+ var args = Array.isArray(arguments[i]) ? arguments[i] : Array.prototype.slice.call(arguments, i);
+
+ if (doThrow) {
+ var err = new Error(Utils.fmt(message, args));
+ err.name = type + 'Error';
+ err.args = args;
+ throw err;
+ }
+ else {
+ console.error(type + 'Error: ' + Utils.fmt(message, args));
}
-
- var err = new Error(Utils.fmt(message, args));
- err.name = type + 'Error';
- err.args = args;
- throw err;
};
/**
diff --git a/tests/common.js b/tests/common.js
index 26111ecb..74d21375 100644
--- a/tests/common.js
+++ b/tests/common.js
@@ -43,6 +43,10 @@ QUnit.assert.rulesMatch = function(actual, expected, message) {
var ok = (function match(a, b){
var ok = true;
+ if (a.hasOwnProperty('valid') && b.hasOwnProperty('valid')) {
+ ok = QUnit.equiv(a.valid, b.valid);
+ }
+
if (b.hasOwnProperty('data')) {
if (!a.hasOwnProperty('data')) {
ok = false;
diff --git a/tests/core.module.js b/tests/core.module.js
index 8780a654..8edab9f6 100644
--- a/tests/core.module.js
+++ b/tests/core.module.js
@@ -86,9 +86,9 @@ $(function(){
);
});
- assert.deepEqual(
+ assert.equal(
$b.queryBuilder('getRules'),
- {},
+ null,
'Should return empty object'
);
@@ -98,7 +98,7 @@ $(function(){
assert.deepEqual(
$b.queryBuilder('getRules'),
- { condition: 'AND', rules: [] },
+ { condition: 'AND', rules: [], valid: true},
'Should return object with no rules'
);
});
diff --git a/tests/data.module.js b/tests/data.module.js
index b5fa3b71..1f855489 100644
--- a/tests/data.module.js
+++ b/tests/data.module.js
@@ -387,6 +387,50 @@ $(function(){
);
});
+ /**
+ * Test allow_invalid option
+ */
+ QUnit.test('allow invalid', function(assert) {
+ $b.queryBuilder({
+ filters: basic_filters
+ });
+
+ $b.queryBuilder('setRules', {
+ condition: 'XOR',
+ rules: [{
+ id: 'name',
+ operator: 'unkown_ope',
+ value: 'Mistic'
+ }, {
+ id: 'unknown_id',
+ operator: 'equal',
+ value: 123
+ }]
+ }, {
+ allow_invalid: true
+ });
+
+ assert.rulesMatch(
+ $b.queryBuilder('getRules', {
+ allow_invalid: true
+ }),
+ {
+ valid: false,
+ condition: 'AND',
+ rules: [{
+ id: 'name',
+ operator: 'equal',
+ value: 'Mistic'
+ }, {
+ id: null,
+ operator: null,
+ value: null
+ }]
+ },
+ 'Should allow invalid rules for setRules and getRules'
+ );
+ });
+
var validation_filters = [{
id: 'radio',
From add6df65a36664754543187cf6f25d13c97ec0a1 Mon Sep 17 00:00:00 2001
From: mistic100
Date: Tue, 27 Dec 2016 17:18:50 +0100
Subject: [PATCH 002/116] Fix #405 add allow_empty_value filter property
---
examples/index.html | 1 +
src/data.js | 52 ++++++++++++++++++++++++++++----------------
src/public.js | 12 +++-------
tests/data.module.js | 47 ++++++++++++++++++++++++++++++++++-----
4 files changed, 79 insertions(+), 33 deletions(-)
diff --git a/examples/index.html b/examples/index.html
index 205d2a06..128f96a5 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -180,6 +180,7 @@ Output
type: 'string',
optgroup: 'core',
default_value: 'Mistic',
+ allow_empty_value: true,
size: 30,
unique: true
},
diff --git a/src/data.js b/src/data.js
index 56099f5b..c05e885c 100644
--- a/src/data.js
+++ b/src/data.js
@@ -41,15 +41,19 @@ QueryBuilder.prototype.validateValueInternal = function(rule, value) {
for (var i = 0; i < operator.nb_inputs; i++) {
switch (filter.input) {
case 'radio':
- if (value[i] === undefined) {
- result = ['radio_empty'];
+ if (value[i] === undefined || value[i].length === 0) {
+ if (!filter.allow_empty_value) {
+ result = ['radio_empty'];
+ }
break;
}
break;
case 'checkbox':
if (value[i] === undefined || value[i].length === 0) {
- result = ['checkbox_empty'];
+ if (!filter.allow_empty_value) {
+ result = ['checkbox_empty'];
+ }
break;
}
else if (!operator.multiple && value[i].length > 1) {
@@ -59,21 +63,15 @@ QueryBuilder.prototype.validateValueInternal = function(rule, value) {
break;
case 'select':
- if (filter.multiple) {
- if (value[i] === undefined || value[i].length === 0 || (filter.placeholder && value[i] == filter.placeholder_value)) {
+ if (value[i] === undefined || value[i].length === 0 || (filter.placeholder && value[i] == filter.placeholder_value)) {
+ if (!filter.allow_empty_value) {
result = ['select_empty'];
- break;
- }
- else if (!operator.multiple && value[i].length > 1) {
- result = ['operator_not_multiple', operator.type];
- break;
}
+ break;
}
- else {
- if (value[i] === undefined || (filter.placeholder && value[i] == filter.placeholder_value)) {
- result = ['select_empty'];
- break;
- }
+ if (filter.multiple && !operator.multiple && value[i].length > 1) {
+ result = ['operator_not_multiple', operator.type];
+ break;
}
break;
@@ -81,7 +79,9 @@ QueryBuilder.prototype.validateValueInternal = function(rule, value) {
switch (QueryBuilder.types[filter.type]) {
case 'string':
if (value[i] === undefined || value[i].length === 0) {
- result = ['string_empty'];
+ if (!filter.allow_empty_value) {
+ result = ['string_empty'];
+ }
break;
}
if (validation.min !== undefined) {
@@ -108,7 +108,13 @@ QueryBuilder.prototype.validateValueInternal = function(rule, value) {
break;
case 'number':
- if (value[i] === undefined || isNaN(value[i])) {
+ if (value[i] === undefined || value[i].length === 0) {
+ if (!filter.allow_empty_value) {
+ result = ['number_nan'];
+ }
+ break;
+ }
+ if (isNaN(value[i])) {
result = ['number_nan'];
break;
}
@@ -147,7 +153,9 @@ QueryBuilder.prototype.validateValueInternal = function(rule, value) {
case 'datetime':
if (value[i] === undefined || value[i].length === 0) {
- result = ['datetime_empty'];
+ if (!filter.allow_empty_value) {
+ result = ['datetime_empty'];
+ }
break;
}
@@ -180,7 +188,13 @@ QueryBuilder.prototype.validateValueInternal = function(rule, value) {
break;
case 'boolean':
- tmp = value[i].trim().toLowerCase();
+ if (value[i] === undefined || value[i].length === 0) {
+ if (!filter.allow_empty_value) {
+ result = ['boolean_not_valid'];
+ }
+ break;
+ }
+ tmp = ('' + value[i]).trim().toLowerCase();
if (tmp !== 'true' && tmp !== 'false' && tmp !== '1' && tmp !== '0' && value[i] !== 1 && value[i] !== 0) {
result = ['boolean_not_valid'];
break;
diff --git a/src/public.js b/src/public.js
index b9db5d70..bef0915f 100644
--- a/src/public.js
+++ b/src/public.js
@@ -249,10 +249,8 @@ QueryBuilder.prototype.setRules = function(data, options) {
data.condition = self.settings.default_condition;
}
else if (self.settings.conditions.indexOf(data.condition) == -1) {
- if (options.allow_invalid) {
- data.condition = self.settings.default_condition;
- }
Utils.error(!options.allow_invalid, 'UndefinedCondition', 'Invalid condition "{0}"', data.condition);
+ data.condition = self.settings.default_condition;
}
group.condition = data.condition;
@@ -262,10 +260,8 @@ QueryBuilder.prototype.setRules = function(data, options) {
if (item.rules !== undefined) {
if (self.settings.allow_groups !== -1 && self.settings.allow_groups < group.level) {
- if (!options.allow_invalid) {
- self.reset();
- }
Utils.error(!options.allow_invalid, 'RulesParse', 'No more than {0} groups are allowed', self.settings.allow_groups);
+ self.reset();
}
else {
model = self.addGroup(group, false, item.data, self.parseGroupFlags(item));
@@ -279,10 +275,8 @@ QueryBuilder.prototype.setRules = function(data, options) {
else {
if (!item.empty) {
if (item.id === undefined) {
- if (options.allow_invalid) {
- item.empty = true;
- }
Utils.error(!options.allow_invalid, 'RulesParse', 'Missing rule field id');
+ item.empty = true;
}
if (item.operator === undefined) {
item.operator = 'equal';
diff --git a/tests/data.module.js b/tests/data.module.js
index 1f855489..6782e132 100644
--- a/tests/data.module.js
+++ b/tests/data.module.js
@@ -155,11 +155,6 @@ $(function(){
/number_not_integer/
);
- assert.validationError($b,
- { id: 'double', value: 'abc' },
- /number_not_double/
- );
-
assert.validationError($b,
{ id: 'integer', value: -15 },
/number_exceed_min/
@@ -431,6 +426,27 @@ $(function(){
);
});
+ /**
+ * Test allow_empty_value option
+ */
+ QUnit.test('allow empty value', function(assert) {
+ var filters = $.extend(true, [], basic_filters);
+ filters.forEach(function(filter) {
+ filter.allow_empty_value = true;
+ });
+
+ $b.queryBuilder({
+ filters: filters,
+ rules: empty_rules
+ });
+
+ assert.rulesMatch(
+ $b.queryBuilder('getRules'),
+ empty_rules,
+ 'Should allow empty value for all filters'
+ );
+ });
+
var validation_filters = [{
id: 'radio',
@@ -502,4 +518,25 @@ $(function(){
}
}
}];
+
+ var empty_rules = {
+ condition: 'AND',
+ rules: [{
+ id: 'name',
+ operator: 'equal',
+ value: ''
+ }, {
+ id: 'category',
+ operator: 'equal',
+ value: []
+ }, {
+ id: 'in_stock',
+ operator: 'equal',
+ value: undefined
+ }, {
+ id: 'price',
+ operator: 'equal',
+ value: ''
+ }]
+ };
});
From f9f1cc8be85590b6b6fa91b459f6811e1433d974 Mon Sep 17 00:00:00 2001
From: mistic100
Date: Tue, 27 Dec 2016 17:21:31 +0100
Subject: [PATCH 003/116] Fix #406 restore afterMove event
---
src/plugins/sortable/plugin.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/plugins/sortable/plugin.js b/src/plugins/sortable/plugin.js
index 6b2818ea..4b62a871 100644
--- a/src/plugins/sortable/plugin.js
+++ b/src/plugins/sortable/plugin.js
@@ -29,6 +29,8 @@ QueryBuilder.define('sortable', function(options) {
return;
}
+ var self = e.builder;
+
/**
* Configure drag
*/
@@ -59,7 +61,7 @@ QueryBuilder.define('sortable', function(options) {
ghost[0].style.top = event.clientY - 15 + 'px';
ghost[0].style.left = event.clientX - 15 + 'px';
},
- onend: function(event) {
+ onend: function() {
// remove ghost
ghost.remove();
ghost = undefined;
@@ -70,6 +72,8 @@ QueryBuilder.define('sortable', function(options) {
// show element
src.$el.show();
+
+ self.trigger('afterMove', src);
}
});
From 6ad46cdaaf5605df6769de0681aff05f54c73d96 Mon Sep 17 00:00:00 2001
From: Ruud
Date: Thu, 29 Dec 2016 15:11:41 +0100
Subject: [PATCH 004/116] added not_between translation for NL
added not_between translation for NL
---
src/i18n/nl.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/i18n/nl.json b/src/i18n/nl.json
index f2390971..06199640 100644
--- a/src/i18n/nl.json
+++ b/src/i18n/nl.json
@@ -22,6 +22,7 @@
"greater": "groter",
"greater_or_equal": "groter of gelijk",
"between": "tussen",
+ "not_between": "niet tussen",
"begins_with": "begint met",
"not_begins_with": "begint niet met",
"contains": "bevat",
@@ -54,4 +55,4 @@
"datetime_exceed_min": "Dient na {0}",
"datetime_exceed_max": "Dient voor {0}"
}
-}
\ No newline at end of file
+}
From ec1a831c805abc05a2dc5ec448f6e5b3ff89e29e Mon Sep 17 00:00:00 2001
From: mistic100
Date: Thu, 29 Dec 2016 15:56:44 +0100
Subject: [PATCH 005/116] Fix #399 sortable: add "no_drop" flag and inherit
flags
---
examples/index.html | 4 +-
src/core.js | 6 +-
src/plugins/sortable/plugin.js | 180 +++++++++++++++++----------------
src/public.js | 7 +-
tests/core.module.js | 4 +-
tests/data.module.js | 24 ++++-
6 files changed, 131 insertions(+), 94 deletions(-)
diff --git a/examples/index.html b/examples/index.html
index 128f96a5..fef065e1 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -472,7 +472,9 @@ Output
condition: 'OR',
not: true,
flags: {
- no_delete: true
+ no_delete: true,
+ no_drop: true,
+ no_sortable: true
},
rules: [{
id: 'category',
diff --git a/src/core.js b/src/core.js
index 900d1890..5a9c31a9 100644
--- a/src/core.js
+++ b/src/core.js
@@ -370,7 +370,7 @@ QueryBuilder.prototype.setRoot = function(addRule, data, flags) {
this.model.root.model = this.model;
this.model.root.data = data;
- this.model.root.flags = $.extend({}, this.settings.default_group_flags, flags);
+ this.model.root.__.flags = $.extend({}, this.settings.default_group_flags, flags);
this.trigger('afterAddGroup', this.model.root);
@@ -406,7 +406,7 @@ QueryBuilder.prototype.addGroup = function(parent, addRule, data, flags) {
var model = parent.addGroup($group);
model.data = data;
- model.flags = $.extend({}, this.settings.default_group_flags, flags);
+ model.__.flags = $.extend({}, this.settings.default_group_flags, flags);
this.trigger('afterAddGroup', model);
@@ -501,7 +501,7 @@ QueryBuilder.prototype.addRule = function(parent, data, flags) {
model.data = data;
}
- model.flags = $.extend({}, this.settings.default_rule_flags, flags);
+ model.__.flags = $.extend({}, this.settings.default_rule_flags, flags);
this.trigger('afterAddRule', model);
diff --git a/src/plugins/sortable/plugin.js b/src/plugins/sortable/plugin.js
index 4b62a871..d9fd30ef 100644
--- a/src/plugins/sortable/plugin.js
+++ b/src/plugins/sortable/plugin.js
@@ -6,11 +6,27 @@
Selectors.rule_and_group_containers = Selectors.rule_container + ', ' + Selectors.group_container;
Selectors.drag_handle = '.drag-handle';
+QueryBuilder.defaults({
+ default_rule_flags: {
+ no_sortable: false,
+ no_drop: false
+ },
+ default_group_flags: {
+ no_sortable: false,
+ no_drop: false
+ }
+});
+
QueryBuilder.define('sortable', function(options) {
if (!('interact' in window)) {
Utils.error('MissingLibrary', 'interact.js is required to use "sortable" plugin. Get it here: http://interactjs.io');
}
+ if (options.default_no_sortable !== undefined) {
+ Utils.error(false, 'Config', 'Sortable plugin : "default_no_sortable" options is deprecated, use standard "default_rule_flags" and "default_group_flags" instead');
+ this.settings.default_rule_flags.no_sortable = this.settings.default_group_flags.no_sortable = options.default_no_sortable;
+ }
+
// recompute drop-zones during drag (when a rule is hidden)
interact.dynamicDrop(true);
@@ -32,70 +48,68 @@ QueryBuilder.define('sortable', function(options) {
var self = e.builder;
/**
- * Configure drag
+ * Inherit flags
*/
- interact(node.$el[0])
- .allowFrom(Selectors.drag_handle)
- .draggable({
- onstart: function(event) {
- // get model of dragged element
- src = Model(event.target);
-
- // create ghost
- ghost = src.$el.clone()
- .appendTo(src.$el.parent())
- .width(src.$el.outerWidth())
- .addClass('dragging');
-
- // create drop placeholder
- var ph = $('
')
- .height(src.$el.outerHeight());
-
- placeholder = src.parent.addRule(ph, src.getPos());
-
- // hide dragged element
- src.$el.hide();
- },
- onmove: function(event) {
- // make the ghost follow the cursor
- ghost[0].style.top = event.clientY - 15 + 'px';
- ghost[0].style.left = event.clientX - 15 + 'px';
- },
- onend: function() {
- // remove ghost
- ghost.remove();
- ghost = undefined;
-
- // remove placeholder
- placeholder.drop();
- placeholder = undefined;
-
- // show element
- src.$el.show();
-
- self.trigger('afterMove', src);
- }
- });
+ if (options.inherit_no_sortable && node.parent && node.parent.flags.no_sortable) {
+ node.flags.no_sortable = true;
+ }
+ if (options.inherit_no_drop && node.parent && node.parent.flags.no_drop) {
+ node.flags.no_drop = true;
+ }
/**
- * Configure drop on groups and rules
+ * Configure drag
*/
- interact(node.$el[0])
- .dropzone({
- accept: Selectors.rule_and_group_containers,
- ondragenter: function(event) {
- moveSortableToTarget(placeholder, $(event.target));
- },
- ondrop: function(event) {
- moveSortableToTarget(src, $(event.target));
- }
- });
+ if (!node.flags.no_sortable) {
+ interact(node.$el[0])
+ .allowFrom(Selectors.drag_handle)
+ .draggable({
+ onstart: function(event) {
+ // get model of dragged element
+ src = Model(event.target);
+
+ // create ghost
+ ghost = src.$el.clone()
+ .appendTo(src.$el.parent())
+ .width(src.$el.outerWidth())
+ .addClass('dragging');
+
+ // create drop placeholder
+ var ph = $('
')
+ .height(src.$el.outerHeight());
+
+ placeholder = src.parent.addRule(ph, src.getPos());
+
+ // hide dragged element
+ src.$el.hide();
+ },
+ onmove: function(event) {
+ // make the ghost follow the cursor
+ ghost[0].style.top = event.clientY - 15 + 'px';
+ ghost[0].style.left = event.clientX - 15 + 'px';
+ },
+ onend: function() {
+ // remove ghost
+ ghost.remove();
+ ghost = undefined;
- /**
- * Configure drop on group headers
- */
- if (node instanceof Group) {
- interact(node.$el.find(Selectors.group_header)[0])
+ // remove placeholder
+ placeholder.drop();
+ placeholder = undefined;
+
+ // show element
+ src.$el.show();
+
+ self.trigger('afterMove', src);
+ }
+ });
+ }
+
+ if (!node.flags.no_drop) {
+ /**
+ * Configure drop on groups and rules
+ */
+ interact(node.$el[0])
.dropzone({
accept: Selectors.rule_and_group_containers,
ondragenter: function(event) {
@@ -105,6 +119,22 @@ QueryBuilder.define('sortable', function(options) {
moveSortableToTarget(src, $(event.target));
}
});
+
+ /**
+ * Configure drop on group headers
+ */
+ if (node instanceof Group) {
+ interact(node.$el.find(Selectors.group_header)[0])
+ .dropzone({
+ accept: Selectors.rule_and_group_containers,
+ ondragenter: function(event) {
+ moveSortableToTarget(placeholder, $(event.target));
+ },
+ ondrop: function(event) {
+ moveSortableToTarget(src, $(event.target));
+ }
+ });
+ }
}
});
@@ -122,32 +152,11 @@ QueryBuilder.define('sortable', function(options) {
});
/**
- * Remove drag handle from non-sortable rules
- */
- this.on('parseRuleFlags.filter', function(flags) {
- if (flags.value.no_sortable === undefined) {
- flags.value.no_sortable = options.default_no_sortable;
- }
- });
-
- this.on('afterApplyRuleFlags', function(e, rule) {
- if (rule.flags.no_sortable) {
- rule.$el.find('.drag-handle').remove();
- }
- });
-
- /**
- * Remove drag handle from non-sortable groups
+ * Remove drag handle from non-sortable items
*/
- this.on('parseGroupFlags.filter', function(flags) {
- if (flags.value.no_sortable === undefined) {
- flags.value.no_sortable = options.default_no_sortable;
- }
- });
-
- this.on('afterApplyGroupFlags', function(e, group) {
- if (group.flags.no_sortable) {
- group.$el.find('.drag-handle').remove();
+ this.on('afterApplyRuleFlags afterApplyGroupFlags', function(e, node) {
+ if (node.flags.no_sortable) {
+ node.$el.find('.drag-handle').remove();
}
});
@@ -168,7 +177,8 @@ QueryBuilder.define('sortable', function(options) {
h.value = $h.prop('outerHTML');
});
}, {
- default_no_sortable: false,
+ inherit_no_sortable: true,
+ inherit_no_drop: true,
icon: 'glyphicon glyphicon-sort'
});
diff --git a/src/public.js b/src/public.js
index bef0915f..70db106d 100644
--- a/src/public.js
+++ b/src/public.js
@@ -235,6 +235,7 @@ QueryBuilder.prototype.setRules = function(data, options) {
this.clear();
this.setRoot(false, data.data, this.parseGroupFlags(data));
+ this.applyGroupFlags(this.model.root);
data = this.change('setRules', data);
@@ -269,6 +270,8 @@ QueryBuilder.prototype.setRules = function(data, options) {
return;
}
+ self.applyGroupFlags(model);
+
add(item, model);
}
}
@@ -283,7 +286,7 @@ QueryBuilder.prototype.setRules = function(data, options) {
}
}
- model = self.addRule(group, item.data);
+ model = self.addRule(group, item.data, self.parseRuleFlags(item));
if (model === null) {
return;
}
@@ -304,7 +307,7 @@ QueryBuilder.prototype.setRules = function(data, options) {
}
}
- model.flags = self.parseRuleFlags(item);
+ self.applyRuleFlags(model);
if (self.change('jsonToRule', model, item) != model) {
Utils.error('RulesParse', 'Plugin tried to change rule reference');
diff --git a/tests/core.module.js b/tests/core.module.js
index 8edab9f6..84ee916b 100644
--- a/tests/core.module.js
+++ b/tests/core.module.js
@@ -762,7 +762,9 @@ $(function(){
filter_readonly: true,
operator_readonly: false,
value_readonly: true,
- no_delete: false
+ no_delete: false,
+ no_sortable: true,
+ no_drop: false
};
QueryBuilder.defaults({ default_rule_flags: flags });
diff --git a/tests/data.module.js b/tests/data.module.js
index 6782e132..a6a2b184 100644
--- a/tests/data.module.js
+++ b/tests/data.module.js
@@ -331,13 +331,33 @@ $(function(){
condition_readonly: true,
no_add_rule: false,
no_add_group: false,
- no_delete: false
+ no_delete: false,
+ no_sortable: false,
+ no_drop: false
};
rules_all_flags.rules[0].flags = {
filter_readonly: false,
operator_readonly: false,
value_readonly: false,
- no_delete: true
+ no_delete: true,
+ no_sortable: false,
+ no_drop: false
+ };
+ rules_all_flags.rules[1].flags = {
+ condition_readonly: true,
+ no_add_rule: true,
+ no_add_group: true,
+ no_delete: true,
+ no_sortable: false,
+ no_drop: false
+ };
+ rules_all_flags.rules[1].rules[0].flags = {
+ filter_readonly: true,
+ operator_readonly: true,
+ value_readonly: true,
+ no_delete: true,
+ no_sortable: false,
+ no_drop: false
};
assert.rulesMatch(
From 358f5d04d12cee8b0b80f374c615fe09d31c109b Mon Sep 17 00:00:00 2001
From: kfirstri
Date: Thu, 29 Dec 2016 18:32:23 +0200
Subject: [PATCH 006/116] added hebrew translation file (#409)
---
examples/index.html | 1 +
src/i18n/he.json | 61 ++++++++++++++++++++++++++++++
src/plugins/invert/i18n/he.json | 3 ++
src/plugins/not-group/i18n/he.json | 3 ++
4 files changed, 68 insertions(+)
create mode 100644 src/i18n/he.json
create mode 100644 src/plugins/invert/i18n/he.json
create mode 100644 src/plugins/not-group/i18n/he.json
diff --git a/examples/index.html b/examples/index.html
index fef065e1..c3294a8d 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -58,6 +58,7 @@ jQuery QueryBuilder Example
+
diff --git a/src/i18n/he.json b/src/i18n/he.json
new file mode 100644
index 00000000..99f2acf9
--- /dev/null
+++ b/src/i18n/he.json
@@ -0,0 +1,61 @@
+{
+ "__locale": "Hebrew (he)",
+ "__author": "Kfir Stri https://github.com/kfirstri",
+
+ "add_rule": "הוסף כלל",
+ "add_group": "הוסף קבוצה",
+ "delete_rule": "מחק",
+ "delete_group": "מחק",
+
+ "conditions": {
+ "AND": "וגם",
+ "OR": "או"
+ },
+
+ "operators": {
+ "equal": "שווה ל",
+ "not_equal": "שונה מ",
+ "in": "חלק מ",
+ "not_in": "לא חלק מ",
+ "less": "פחות מ",
+ "less_or_equal": "פחות או שווה ל",
+ "greater": "גדול מ",
+ "greater_or_equal": "גדול או שווה ל",
+ "between": "בין",
+ "not_between": "לא בין",
+ "begins_with": "מתחיל ב",
+ "not_begins_with": "לא מתחיל ב",
+ "contains": "מכיל",
+ "not_contains": "לא מכיל",
+ "ends_with": "מסתיים ב",
+ "not_ends_with": "לא מסתיים ב",
+ "is_empty": "ריק",
+ "is_not_empty": "לא ריק",
+ "is_null": "חסר ערך",
+ "is_not_null": "לא חסר ערך"
+ },
+
+ "errors": {
+ "no_filter": "לא נבחרו מסננים",
+ "empty_group": "הקבוצה רירקה",
+ "radio_empty": "לא נבחר אף ערך",
+ "checkbox_empty": "לא נבחר אף ערך",
+ "select_empty": "לא נבחר אף ערך",
+ "string_empty": "חסר ערך",
+ "string_exceed_min_length": "המחרוזת חייבת להכיל לפחות {0} תווים",
+ "string_exceed_max_length": "המחרוזת לא יכולה להכיל יותר מ{0} תווים",
+ "string_invalid_format": "המחרוזת בפורמט שגוי ({0})",
+ "number_nan": "זהו לא מספר",
+ "number_not_integer": "המספר אינו מספר שלם",
+ "number_not_double": "המספר אינו מספר עשרוני",
+ "number_exceed_min": "המספר צריך להיות גדול מ {0}",
+ "number_exceed_max": "המספר צריך להיות קטן מ{0}",
+ "number_wrong_step": "המספר צריך להיות כפולה של {0}",
+ "datetime_empty": "תאריך ריק",
+ "datetime_invalid": "פורמט תאריך שגוי ({0})",
+ "datetime_exceed_min": "התאריך חייב להיות אחרי {0}",
+ "datetime_exceed_max": "התאריך חייב להיות לפני {0}",
+ "boolean_not_valid": "זהו לא בוליאני",
+ "operator_not_multiple": "האופרטור {0} לא יכול לקבל ערכים מרובים"
+ }
+}
\ No newline at end of file
diff --git a/src/plugins/invert/i18n/he.json b/src/plugins/invert/i18n/he.json
new file mode 100644
index 00000000..c279ee3d
--- /dev/null
+++ b/src/plugins/invert/i18n/he.json
@@ -0,0 +1,3 @@
+{
+ "invert": "הפוך שאילתא"
+}
diff --git a/src/plugins/not-group/i18n/he.json b/src/plugins/not-group/i18n/he.json
new file mode 100644
index 00000000..ac76d192
--- /dev/null
+++ b/src/plugins/not-group/i18n/he.json
@@ -0,0 +1,3 @@
+{
+ "NOT": "לא"
+}
From f0fa29c2cdf616199e70af70d050dd454ecd8c4f Mon Sep 17 00:00:00 2001
From: Damien SOREL
Date: Wed, 4 Jan 2017 13:53:35 +0100
Subject: [PATCH 007/116] Fix #404 radio state lost on drag-and-drop
---
src/core.js | 21 ++++++++---------
src/data.js | 15 +++++++------
src/model.js | 3 +++
src/plugins/sortable/plugin.js | 41 +++++++++++++++++++++-------------
4 files changed, 48 insertions(+), 32 deletions(-)
diff --git a/src/core.js b/src/core.js
index 5a9c31a9..d829bd67 100644
--- a/src/core.js
+++ b/src/core.js
@@ -14,8 +14,7 @@ QueryBuilder.prototype.init = function($el, options) {
generated_id: false,
has_optgroup: false,
has_operator_oprgroup: false,
- id: null,
- updating_value: false
+ id: null
};
// "allow_groups" can be boolean or int
@@ -601,9 +600,11 @@ QueryBuilder.prototype.createRuleInput = function(rule) {
$valueContainer.show();
$inputs.on('change ' + (filter.input_event || ''), function() {
- self.status.updating_value = true;
- rule.value = self.getRuleValue(rule);
- self.status.updating_value = false;
+ if (!this._updating_input) {
+ rule._updating_value = true;
+ rule.value = self.getRuleInputValue(rule);
+ rule._updating_value = false;
+ }
});
if (filter.plugin) {
@@ -616,9 +617,9 @@ QueryBuilder.prototype.createRuleInput = function(rule) {
rule.value = filter.default_value;
}
else {
- self.status.updating_value = true;
- rule.value = self.getRuleValue(rule);
- self.status.updating_value = false;
+ rule._updating_value = true;
+ rule.value = self.getRuleInputValue(rule);
+ rule._updating_value = false;
}
};
@@ -676,8 +677,8 @@ QueryBuilder.prototype.updateRuleOperator = function(rule, previousOperator) {
* @param rule {Rule}
*/
QueryBuilder.prototype.updateRuleValue = function(rule) {
- if (!this.status.updating_value) {
- this.setRuleValue(rule, rule.value);
+ if (!rule._updating_value) {
+ this.setRuleInputValue(rule, rule.value);
}
this.trigger('afterUpdateRuleValue', rule);
diff --git a/src/data.js b/src/data.js
index c05e885c..537c4bed 100644
--- a/src/data.js
+++ b/src/data.js
@@ -310,11 +310,11 @@ QueryBuilder.prototype.getOperatorByType = function(type, doThrow) {
};
/**
- * Returns rule value
+ * Returns rule's input value
* @param rule {Rule}
* @return {mixed}
*/
-QueryBuilder.prototype.getRuleValue = function(rule) {
+QueryBuilder.prototype.getRuleInputValue = function(rule) {
var filter = rule.filter;
var operator = rule.operator;
var value = [];
@@ -380,11 +380,11 @@ QueryBuilder.prototype.getRuleValue = function(rule) {
};
/**
- * Sets the value of a rule.
+ * Sets the value of a rule's input.
* @param rule {Rule}
* @param value {mixed}
*/
-QueryBuilder.prototype.setRuleValue = function(rule, value) {
+QueryBuilder.prototype.setRuleInputValue = function(rule, value) {
var filter = rule.filter;
var operator = rule.operator;
@@ -392,6 +392,8 @@ QueryBuilder.prototype.setRuleValue = function(rule, value) {
return;
}
+ this._updating_input = true;
+
if (filter.valueSetter) {
filter.valueSetter.call(this, rule, value);
}
@@ -401,9 +403,6 @@ QueryBuilder.prototype.setRuleValue = function(rule, value) {
if (operator.nb_inputs == 1) {
value = [value];
}
- else {
- value = value;
- }
for (var i = 0; i < operator.nb_inputs; i++) {
var name = Utils.escapeElementId(rule.id + '_value_' + i);
@@ -431,6 +430,8 @@ QueryBuilder.prototype.setRuleValue = function(rule, value) {
}
}
}
+
+ this._updating_input = false;
};
/**
diff --git a/src/model.js b/src/model.js
index 39db5988..e67538e2 100644
--- a/src/model.js
+++ b/src/model.js
@@ -410,6 +410,9 @@ var Rule = function(parent, $el) {
Node.call(this, parent, $el);
+ this._updating_value = false;
+ this._updating_input = false;
+
this.__.filter = null;
this.__.operator = null;
this.__.flags = {};
diff --git a/src/plugins/sortable/plugin.js b/src/plugins/sortable/plugin.js
index d9fd30ef..ec94fe81 100644
--- a/src/plugins/sortable/plugin.js
+++ b/src/plugins/sortable/plugin.js
@@ -116,7 +116,7 @@ QueryBuilder.define('sortable', function(options) {
moveSortableToTarget(placeholder, $(event.target));
},
ondrop: function(event) {
- moveSortableToTarget(src, $(event.target));
+ moveSortableToTarget(src, $(event.target), self);
}
});
@@ -131,7 +131,7 @@ QueryBuilder.define('sortable', function(options) {
moveSortableToTarget(placeholder, $(event.target));
},
ondrop: function(event) {
- moveSortableToTarget(src, $(event.target));
+ moveSortableToTarget(src, $(event.target), self);
}
});
}
@@ -186,29 +186,40 @@ QueryBuilder.define('sortable', function(options) {
* Move an element (placeholder or actual object) depending on active target
* @param {Node} node
* @param {jQuery} target
+ * @param {QueryBuilder} [builder]
*/
-function moveSortableToTarget(node, target) {
- var parent;
+function moveSortableToTarget(node, target, builder) {
+ var parent, method;
// on rule
parent = target.closest(Selectors.rule_container);
if (parent.length) {
- node.moveAfter(Model(parent));
- return;
+ method = 'moveAfter';
}
// on group header
- parent = target.closest(Selectors.group_header);
- if (parent.length) {
- parent = target.closest(Selectors.group_container);
- node.moveAtBegin(Model(parent));
- return;
+ if (!method) {
+ parent = target.closest(Selectors.group_header);
+ if (parent.length) {
+ parent = target.closest(Selectors.group_container);
+ method = 'moveAtBegin';
+ }
}
// on group
- parent = target.closest(Selectors.group_container);
- if (parent.length) {
- node.moveAtEnd(Model(parent));
- return;
+ if (!method) {
+ parent = target.closest(Selectors.group_container);
+ if (parent.length) {
+ method = 'moveAtEnd';
+ }
+ }
+
+ if (method) {
+ node[method](Model(parent));
+
+ // refresh radio value
+ if (builder && node instanceof Rule) {
+ builder.setRuleInputValue(node, node.value);
+ }
}
}
From 92403331fddcb22551edd0f193a9db5ed56eaa98 Mon Sep 17 00:00:00 2001
From: mistic100
Date: Thu, 5 Jan 2017 19:33:11 +0100
Subject: [PATCH 008/116] Fix #416 coordinates demo
---
examples/index.html | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/examples/index.html b/examples/index.html
index c3294a8d..b19b5cc8 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -365,10 +365,10 @@ Output
validation: {
format: /^[A-C]{1}.[1-6]{1}$/
},
- input: function(rule) {
+ input: function(rule, name) {
var $container = rule.$el.find('.rule-value-container');
- $container.on('change', '[name=coord_1]', function(){
+ $container.on('change', '[name='+ name +'_1]', function(){
var h = '';
switch ($(this).val()) {
@@ -383,27 +383,30 @@ Output
break;
}
- $container.find('[name=coord_2]').html(h).toggle(h!='');
+ $container.find('[name$=_2]')
+ .html(h).toggle(!!h)
+ .val('-1').trigger('change');
});
return '\
-