diff --git a/bower.json b/bower.json index 104ba6c1..8a3d289c 100644 --- a/bower.json +++ b/bower.json @@ -1,11 +1,9 @@ { "name": "jQuery-QueryBuilder", "version": "2.3.3", - "authors": [{ - "name": "Damien \"Mistic\" Sorel", - "email": "contact@git.strangeplanet.fr", - "homepage": "http://www.strangeplanet.fr" - }], + "authors": [ + "[object Object]" + ], "description": "jQuery plugin for user friendly query/filter creator", "main": [ "dist/js/query-builder.js", @@ -47,6 +45,7 @@ "composer.json", "package.json", "Gruntfile.js", - "CONTRIBUTING.md" + "CONTRIBUTING.md", + "test" ] } diff --git a/dist/css/query-builder.dark.css b/dist/css/query-builder.dark.css index 614972c2..ce092c67 100644 --- a/dist/css/query-builder.dark.css +++ b/dist/css/query-builder.dark.css @@ -4,7 +4,7 @@ * Licensed under MIT (http://opensource.org/licenses/MIT) */ -.query-builder .rules-group-container, .query-builder .rule-container, .query-builder .rule-placeholder { +.query-builder .rules-group-container, .query-builder .rule-container { position: relative; margin: 4px 0; border-radius: 5px; @@ -15,7 +15,7 @@ .query-builder .rule-container .rule-filter-container, .query-builder .rule-container .rule-operator-container, -.query-builder .rule-container .rule-value-container, .query-builder .error-container, .query-builder .drag-handle { +.query-builder .rule-container .rule-value-container, .query-builder .error-container { display: inline-block; margin: 0 5px 0 0; vertical-align: middle; @@ -95,34 +95,3 @@ .query-builder .rules-list > *:last-child::after { display: none; } - -.query-builder .error-container + .tooltip .tooltip-inner { - color: #F22 !important; -} - -.query-builder p.filter-description { - margin: 5px 0 0 0; - background: rgba(0, 170, 255, 0.2); - border: 1px solid #346F7B; - color: #AAD1E4; - border-radius: 5px; - padding: 2.5px 5px; - font-size: .8em; -} - -.query-builder .rules-group-header [data-invert] { - margin-left: 5px; -} - -.query-builder .drag-handle { - cursor: move; - vertical-align: middle; - margin-left: 5px; -} -.query-builder .dragged { - opacity: .5; -} -.query-builder .rule-placeholder { - border: 1px dashed #BBB; - opacity: .7; -} diff --git a/dist/css/query-builder.dark.min.css b/dist/css/query-builder.dark.min.css index f81dbb9d..e928d323 100644 --- a/dist/css/query-builder.dark.min.css +++ b/dist/css/query-builder.dark.min.css @@ -4,4 +4,4 @@ * Licensed under MIT (http://opensource.org/licenses/MIT) */ -.query-builder .rule-container,.query-builder .rule-placeholder,.query-builder .rules-group-container{position:relative;margin:4px 0;border-radius:5px;padding:5px;border:1px solid #111;background:rgba(40,40,40,.9)}.query-builder .drag-handle,.query-builder .error-container,.query-builder .rule-container .rule-filter-container,.query-builder .rule-container .rule-operator-container,.query-builder .rule-container .rule-value-container{display:inline-block;margin:0 5px 0 0;vertical-align:middle}.query-builder .rules-group-container{padding:10px 10px 6px;border:1px solid #00164A;background:rgba(50,70,80,.5)}.query-builder .rules-group-header{margin-bottom:10px}.query-builder .rules-group-header .group-conditions .btn.readonly:not(.active),.query-builder .rules-group-header .group-conditions input[name$=_cond]{display:none}.query-builder .rules-group-header .group-conditions .btn.readonly{border-radius:3px}.query-builder .rules-list{list-style:none;padding:0 0 0 15px;margin:0}.query-builder .rule-value-container{border-left:1px solid #DDD;padding-left:5px}.query-builder .rule-value-container label{margin-bottom:0;font-weight:400}.query-builder .rule-value-container label.block{display:block}.query-builder .rule-value-container input[type=number],.query-builder .rule-value-container input[type=text],.query-builder .rule-value-container select{padding:1px}.query-builder .error-container{display:none;cursor:help;color:red}.query-builder .has-error{background-color:#322;border-color:#800}.query-builder .has-error .error-container{display:inline-block!important}.query-builder .rules-list>::after,.query-builder .rules-list>::before{content:'';position:absolute;left:-10px;width:10px;height:calc(50% + 4px);border-color:#222;border-style:solid}.query-builder .rules-list>::before{top:-4px;border-width:0 0 2px 2px}.query-builder .rules-list>::after{top:50%;border-width:0 0 0 2px}.query-builder .rules-list>:first-child::before{top:-12px;height:calc(50% + 14px)}.query-builder .rules-list>:last-child::before{border-radius:0 0 0 4px}.query-builder .rules-list>:last-child::after{display:none}.query-builder .error-container+.tooltip .tooltip-inner{color:#F22!important}.query-builder p.filter-description{margin:5px 0 0;background:rgba(0,170,255,.2);border:1px solid #346F7B;color:#AAD1E4;border-radius:5px;padding:2.5px 5px;font-size:.8em}.query-builder .rules-group-header [data-invert]{margin-left:5px}.query-builder .drag-handle{cursor:move;vertical-align:middle;margin-left:5px}.query-builder .dragged{opacity:.5}.query-builder .rule-placeholder{border:1px dashed #BBB;opacity:.7} \ No newline at end of file +.query-builder .rule-container,.query-builder .rules-group-container{position:relative;margin:4px 0;border-radius:5px;padding:5px;border:1px solid #111;background:rgba(40,40,40,.9)}.query-builder .error-container,.query-builder .rule-container .rule-filter-container,.query-builder .rule-container .rule-operator-container,.query-builder .rule-container .rule-value-container{display:inline-block;margin:0 5px 0 0;vertical-align:middle}.query-builder .rules-group-container{padding:10px 10px 6px;border:1px solid #00164A;background:rgba(50,70,80,.5)}.query-builder .rules-group-header{margin-bottom:10px}.query-builder .rules-group-header .group-conditions .btn.readonly:not(.active),.query-builder .rules-group-header .group-conditions input[name$=_cond]{display:none}.query-builder .rules-group-header .group-conditions .btn.readonly{border-radius:3px}.query-builder .rules-list{list-style:none;padding:0 0 0 15px;margin:0}.query-builder .rule-value-container{border-left:1px solid #DDD;padding-left:5px}.query-builder .rule-value-container label{margin-bottom:0;font-weight:400}.query-builder .rule-value-container label.block{display:block}.query-builder .rule-value-container input[type=number],.query-builder .rule-value-container input[type=text],.query-builder .rule-value-container select{padding:1px}.query-builder .error-container{display:none;cursor:help;color:red}.query-builder .has-error{background-color:#322;border-color:#800}.query-builder .has-error .error-container{display:inline-block!important}.query-builder .rules-list>::after,.query-builder .rules-list>::before{content:'';position:absolute;left:-10px;width:10px;height:calc(50% + 4px);border-color:#222;border-style:solid}.query-builder .rules-list>::before{top:-4px;border-width:0 0 2px 2px}.query-builder .rules-list>::after{top:50%;border-width:0 0 0 2px}.query-builder .rules-list>:first-child::before{top:-12px;height:calc(50% + 14px)}.query-builder .rules-list>:last-child::before{border-radius:0 0 0 4px}.query-builder .rules-list>:last-child::after{display:none} \ No newline at end of file diff --git a/dist/css/query-builder.default.css b/dist/css/query-builder.default.css index 8391656b..f8e03c2e 100644 --- a/dist/css/query-builder.default.css +++ b/dist/css/query-builder.default.css @@ -4,7 +4,7 @@ * Licensed under MIT (http://opensource.org/licenses/MIT) */ -.query-builder .rules-group-container, .query-builder .rule-container, .query-builder .rule-placeholder { +.query-builder .rules-group-container, .query-builder .rule-container { position: relative; margin: 4px 0; border-radius: 5px; @@ -15,7 +15,7 @@ .query-builder .rule-container .rule-filter-container, .query-builder .rule-container .rule-operator-container, -.query-builder .rule-container .rule-value-container, .query-builder .error-container, .query-builder .drag-handle { +.query-builder .rule-container .rule-value-container, .query-builder .error-container { display: inline-block; margin: 0 5px 0 0; vertical-align: middle; @@ -95,34 +95,3 @@ .query-builder .rules-list > *:last-child::after { display: none; } - -.query-builder .error-container + .tooltip .tooltip-inner { - color: #F99 !important; -} - -.query-builder p.filter-description { - margin: 5px 0 0 0; - background: #D9EDF7; - border: 1px solid #BCE8F1; - color: #31708F; - border-radius: 5px; - padding: 2.5px 5px; - font-size: .8em; -} - -.query-builder .rules-group-header [data-invert] { - margin-left: 5px; -} - -.query-builder .drag-handle { - cursor: move; - vertical-align: middle; - margin-left: 5px; -} -.query-builder .dragged { - opacity: .5; -} -.query-builder .rule-placeholder { - border: 1px dashed #BBB; - opacity: .7; -} diff --git a/dist/css/query-builder.default.min.css b/dist/css/query-builder.default.min.css index c5a5d29e..9a7634d1 100644 --- a/dist/css/query-builder.default.min.css +++ b/dist/css/query-builder.default.min.css @@ -4,4 +4,4 @@ * Licensed under MIT (http://opensource.org/licenses/MIT) */ -.query-builder .rule-container,.query-builder .rule-placeholder,.query-builder .rules-group-container{position:relative;margin:4px 0;border-radius:5px;padding:5px;border:1px solid #EEE;background:rgba(255,255,255,.9)}.query-builder .drag-handle,.query-builder .error-container,.query-builder .rule-container .rule-filter-container,.query-builder .rule-container .rule-operator-container,.query-builder .rule-container .rule-value-container{display:inline-block;margin:0 5px 0 0;vertical-align:middle}.query-builder .rules-group-container{padding:10px 10px 6px;border:1px solid #DCC896;background:rgba(250,240,210,.5)}.query-builder .rules-group-header{margin-bottom:10px}.query-builder .rules-group-header .group-conditions .btn.readonly:not(.active),.query-builder .rules-group-header .group-conditions input[name$=_cond]{display:none}.query-builder .rules-group-header .group-conditions .btn.readonly{border-radius:3px}.query-builder .rules-list{list-style:none;padding:0 0 0 15px;margin:0}.query-builder .rule-value-container{border-left:1px solid #DDD;padding-left:5px}.query-builder .rule-value-container label{margin-bottom:0;font-weight:400}.query-builder .rule-value-container label.block{display:block}.query-builder .rule-value-container input[type=number],.query-builder .rule-value-container input[type=text],.query-builder .rule-value-container select{padding:1px}.query-builder .error-container{display:none;cursor:help;color:red}.query-builder .has-error{background-color:#FDD;border-color:#F99}.query-builder .has-error .error-container{display:inline-block!important}.query-builder .rules-list>::after,.query-builder .rules-list>::before{content:'';position:absolute;left:-10px;width:10px;height:calc(50% + 4px);border-color:#CCC;border-style:solid}.query-builder .rules-list>::before{top:-4px;border-width:0 0 2px 2px}.query-builder .rules-list>::after{top:50%;border-width:0 0 0 2px}.query-builder .rules-list>:first-child::before{top:-12px;height:calc(50% + 14px)}.query-builder .rules-list>:last-child::before{border-radius:0 0 0 4px}.query-builder .rules-list>:last-child::after{display:none}.query-builder .error-container+.tooltip .tooltip-inner{color:#F99!important}.query-builder p.filter-description{margin:5px 0 0;background:#D9EDF7;border:1px solid #BCE8F1;color:#31708F;border-radius:5px;padding:2.5px 5px;font-size:.8em}.query-builder .rules-group-header [data-invert]{margin-left:5px}.query-builder .drag-handle{cursor:move;vertical-align:middle;margin-left:5px}.query-builder .dragged{opacity:.5}.query-builder .rule-placeholder{border:1px dashed #BBB;opacity:.7} \ No newline at end of file +.query-builder .rule-container,.query-builder .rules-group-container{position:relative;margin:4px 0;border-radius:5px;padding:5px;border:1px solid #EEE;background:rgba(255,255,255,.9)}.query-builder .error-container,.query-builder .rule-container .rule-filter-container,.query-builder .rule-container .rule-operator-container,.query-builder .rule-container .rule-value-container{display:inline-block;margin:0 5px 0 0;vertical-align:middle}.query-builder .rules-group-container{padding:10px 10px 6px;border:1px solid #DCC896;background:rgba(250,240,210,.5)}.query-builder .rules-group-header{margin-bottom:10px}.query-builder .rules-group-header .group-conditions .btn.readonly:not(.active),.query-builder .rules-group-header .group-conditions input[name$=_cond]{display:none}.query-builder .rules-group-header .group-conditions .btn.readonly{border-radius:3px}.query-builder .rules-list{list-style:none;padding:0 0 0 15px;margin:0}.query-builder .rule-value-container{border-left:1px solid #DDD;padding-left:5px}.query-builder .rule-value-container label{margin-bottom:0;font-weight:400}.query-builder .rule-value-container label.block{display:block}.query-builder .rule-value-container input[type=number],.query-builder .rule-value-container input[type=text],.query-builder .rule-value-container select{padding:1px}.query-builder .error-container{display:none;cursor:help;color:red}.query-builder .has-error{background-color:#FDD;border-color:#F99}.query-builder .has-error .error-container{display:inline-block!important}.query-builder .rules-list>::after,.query-builder .rules-list>::before{content:'';position:absolute;left:-10px;width:10px;height:calc(50% + 4px);border-color:#CCC;border-style:solid}.query-builder .rules-list>::before{top:-4px;border-width:0 0 2px 2px}.query-builder .rules-list>::after{top:50%;border-width:0 0 0 2px}.query-builder .rules-list>:first-child::before{top:-12px;height:calc(50% + 14px)}.query-builder .rules-list>:last-child::before{border-radius:0 0 0 4px}.query-builder .rules-list>:last-child::after{display:none} \ No newline at end of file diff --git a/dist/i18n/query-builder.ar.js b/dist/i18n/query-builder.ar.js index e8444c65..9b4d7ead 100644 --- a/dist/i18n/query-builder.ar.js +++ b/dist/i18n/query-builder.ar.js @@ -72,8 +72,7 @@ QueryBuilder.regional['ar'] = { "datetime_exceed_max": "التاريخ أكبر من الأقصى المسموح به", "boolean_not_valid": "ليست قيمة منطقية ثنائية", "operator_not_multiple": "العامل ليس متعدد القيَم" - }, - "invert": "قَلْبُ" + } }; QueryBuilder.defaults({ lang_code: 'ar' }); diff --git a/dist/i18n/query-builder.az.js b/dist/i18n/query-builder.az.js index 69e710f2..7fd02509 100644 --- a/dist/i18n/query-builder.az.js +++ b/dist/i18n/query-builder.az.js @@ -71,8 +71,7 @@ QueryBuilder.regional['az'] = { "datetime_exceed_max": "{0} əvvəl olmalıdır", "boolean_not_valid": "Loqik olmayan", "operator_not_multiple": "{0} operatoru çoxlu məna daşımır" - }, - "invert": "invert" + } }; QueryBuilder.defaults({ lang_code: 'az' }); diff --git a/dist/i18n/query-builder.cs.js b/dist/i18n/query-builder.cs.js index fa5b8039..d48735a1 100644 --- a/dist/i18n/query-builder.cs.js +++ b/dist/i18n/query-builder.cs.js @@ -71,8 +71,7 @@ QueryBuilder.regional['cs'] = { "datetime_exceed_max": "Musí být do {0}", "boolean_not_valid": "Nelogické", "operator_not_multiple": "Operátor {0} nepodporuje mnoho hodnot" - }, - "invert": "invertní" + } }; QueryBuilder.defaults({ lang_code: 'cs' }); diff --git a/dist/i18n/query-builder.en.js b/dist/i18n/query-builder.en.js index c77da5d5..c048967e 100644 --- a/dist/i18n/query-builder.en.js +++ b/dist/i18n/query-builder.en.js @@ -72,8 +72,7 @@ QueryBuilder.regional['en'] = { "datetime_exceed_max": "Must be before {0}", "boolean_not_valid": "Not a boolean", "operator_not_multiple": "Operator {0} cannot accept multiple values" - }, - "invert": "Invert" + } }; QueryBuilder.defaults({ lang_code: 'en' }); diff --git a/dist/i18n/query-builder.fr.js b/dist/i18n/query-builder.fr.js index e7c7e5b0..6039db97 100644 --- a/dist/i18n/query-builder.fr.js +++ b/dist/i18n/query-builder.fr.js @@ -72,8 +72,7 @@ QueryBuilder.regional['fr'] = { "datetime_exceed_max": "Doit être avant {0}", "boolean_not_valid": "N'est pas un booléen", "operator_not_multiple": "L'opérateur {0} ne peut utiliser plusieurs valeurs" - }, - "invert": "Inverser" + } }; QueryBuilder.defaults({ lang_code: 'fr' }); diff --git a/dist/i18n/query-builder.pl.js b/dist/i18n/query-builder.pl.js index 9b5608dc..0f9d31c5 100644 --- a/dist/i18n/query-builder.pl.js +++ b/dist/i18n/query-builder.pl.js @@ -72,8 +72,7 @@ QueryBuilder.regional['pl'] = { "datetime_exceed_max": "Musi być przed {0}", "boolean_not_valid": "Niepoprawna wartość logiczna", "operator_not_multiple": "Operator {0} nie przyjmuje wielu wartości" - }, - "invert": "Odwróć" + } }; QueryBuilder.defaults({ lang_code: 'pl' }); diff --git a/dist/i18n/query-builder.ru.js b/dist/i18n/query-builder.ru.js index 0ee2091d..6f123992 100644 --- a/dist/i18n/query-builder.ru.js +++ b/dist/i18n/query-builder.ru.js @@ -69,8 +69,7 @@ QueryBuilder.regional['ru'] = { "datetime_exceed_max": "Должно быть, до {0}", "boolean_not_valid": "Не логическое", "operator_not_multiple": "Оператор {0} не поддерживает много значений" - }, - "invert": "Инвертировать" + } }; QueryBuilder.defaults({ lang_code: 'ru' }); diff --git a/dist/i18n/query-builder.tr.js b/dist/i18n/query-builder.tr.js index dd9f9a2b..e2571b8a 100644 --- a/dist/i18n/query-builder.tr.js +++ b/dist/i18n/query-builder.tr.js @@ -72,8 +72,7 @@ QueryBuilder.regional['tr'] = { "datetime_exceed_max": "{0} Tarihinden daha öncesi olmalı.", "boolean_not_valid": "Değer Doğru/Yanlış(bool) olmalı", "operator_not_multiple": "Operatör {0} birden fazla değer kabul etmiyor" - }, - "invert": "Ters Çevir" + } }; QueryBuilder.defaults({ lang_code: 'tr' }); diff --git a/dist/i18n/query-builder.ua.js b/dist/i18n/query-builder.ua.js index fd060991..5ce89c93 100644 --- a/dist/i18n/query-builder.ua.js +++ b/dist/i18n/query-builder.ua.js @@ -71,8 +71,7 @@ QueryBuilder.regional['ua'] = { "datetime_exceed_max": "Повинне бути, до {0}", "boolean_not_valid": "Не логічне", "operator_not_multiple": "Оператор {0} не підтримує багато значень" - }, - "invert": "інвертувати" + } }; QueryBuilder.defaults({ lang_code: 'ua' }); diff --git a/dist/i18n/query-builder.zh-CN.js b/dist/i18n/query-builder.zh-CN.js index e7b7859c..4851349f 100644 --- a/dist/i18n/query-builder.zh-CN.js +++ b/dist/i18n/query-builder.zh-CN.js @@ -72,8 +72,7 @@ QueryBuilder.regional['zh-CN'] = { "datetime_exceed_max": "必须在{0}之前", "boolean_not_valid": "不是布尔值", "operator_not_multiple": "选项{0}无法接受多个值" - }, - "invert": "倒置" + } }; QueryBuilder.defaults({ lang_code: 'zh-CN' }); diff --git a/dist/js/query-builder.js b/dist/js/query-builder.js index f63bf7b9..cdde1f1c 100644 --- a/dist/js/query-builder.js +++ b/dist/js/query-builder.js @@ -5,7 +5,7 @@ */ // Languages: en -// Plugins: bt-checkbox, bt-selectpicker, bt-tooltip-errors, change-filters, filter-description, invert, mongodb-support, sortable, sql-support, unique-filter +// Plugins: ogc-filter, sql-support (function(root, factory) { if (typeof define == 'function' && define.amd) { define(['jquery', 'doT', 'jQuery.extendext'], factory); @@ -273,6 +273,8 @@ QueryBuilder.DEFAULTS = { default_group_flags: { condition_readonly: false, + no_add_rule: false, + no_add_group: false, no_delete: false }, @@ -829,7 +831,10 @@ QueryBuilder.prototype.addRule = function(parent, data, flags) { this.createRuleFilters(model); if (this.settings.default_filter || !this.settings.display_empty_filter) { - model.filter = this.getFilterById(this.settings.default_filter || this.filters[0].id); + model.filter = this.change('getDefaultFilter', + this.getFilterById(this.settings.default_filter || this.filters[0].id), + model + ); } return model; @@ -1027,6 +1032,12 @@ QueryBuilder.prototype.applyGroupFlags = function(group) { group.$el.find('>' + Selectors.group_condition).prop('disabled', true) .parent().addClass('readonly'); } + if (flags.no_add_rule) { + group.$el.find(Selectors.add_rule).remove(); + } + if (flags.no_add_group) { + group.$el.find(Selectors.add_group).remove(); + } if (flags.no_delete) { group.$el.find(Selectors.delete_group).remove(); } @@ -1337,7 +1348,8 @@ QueryBuilder.prototype.setRules = function(data) { data.rules.forEach(function(item) { var model; - if (item.rules && item.rules.length > 0) { + + 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); @@ -1352,11 +1364,13 @@ QueryBuilder.prototype.setRules = function(data) { } } else { - if (item.id === undefined) { - Utils.error('RulesParse', 'Missing rule field id'); - } - if (item.operator === undefined) { - item.operator = 'equal'; + if (!item.empty) { + if (item.id === undefined) { + Utils.error('RulesParse', 'Missing rule field id'); + } + if (item.operator === undefined) { + item.operator = 'equal'; + } } model = self.addRule(group, item.data); @@ -1364,13 +1378,16 @@ QueryBuilder.prototype.setRules = function(data) { return; } - model.filter = self.getFilterById(item.id); - model.operator = self.getOperatorByType(item.operator); - model.flags = self.parseRuleFlags(item); + if (!item.empty) { + model.filter = self.getFilterById(item.id); + model.operator = self.getOperatorByType(item.operator); - if (model.operator.nb_inputs !== 0 && item.value !== undefined) { - model.value = item.value; + if (model.operator.nb_inputs !== 0 && item.value !== undefined) { + model.value = item.value; + } } + + model.flags = self.parseRuleFlags(item); } }); @@ -1837,6 +1854,8 @@ QueryBuilder.prototype.parseGroupFlags = function(group) { if (group.readonly) { $.extend(flags, { condition_readonly: true, + no_add_rule: true, + no_add_group: true, no_delete: true }); } @@ -1949,8 +1968,13 @@ QueryBuilder.templates.filterSelect = '\ '; QueryBuilder.templates.operatorSelect = '\ +{{? it.operators.length === 1 }} \ + \ +{{= it.lang.operators[it.operators[0].type] || it.operators[0].type }} \ + \ +{{?}} \ {{ var optgroup = null; }} \ - \ {{~ it.operators: operator }} \ {{? optgroup !== operator.optgroup }} \ {{? optgroup !== null }}{{?}} \ @@ -2313,7 +2337,7 @@ Node.prototype.moveAtEnd = function(target) { target = this.parent; } - this._move(target, target.length() - 1); + this._move(target, target.length() === 0 ? 0 : target.length() - 1); return this; }; @@ -2735,608 +2759,74 @@ $.fn.queryBuilder.regional = QueryBuilder.regional; /*! - * jQuery QueryBuilder Awesome Bootstrap Checkbox - * Applies Awesome Bootstrap Checkbox for checkbox and radio inputs. - */ - -QueryBuilder.define('bt-checkbox', function(options) { - if (options.font == 'glyphicons') { - var injectCSS = document.createElement('style'); - injectCSS.innerHTML = '\ -.checkbox input[type=checkbox]:checked + label:after { \ - font-family: "Glyphicons Halflings"; \ - content: "\\e013"; \ -} \ -.checkbox label:after { \ - padding-left: 4px; \ - padding-top: 2px; \ - font-size: 9px; \ -}'; - document.body.appendChild(injectCSS); - } - - this.on('getRuleInput.filter', function(h, rule, name) { - var filter = rule.filter; - - if ((filter.input === 'radio' || filter.input === 'checkbox') && !filter.plugin) { - h.value = ''; - - if (!filter.colors) { - filter.colors = {}; - } - if (filter.color) { - filter.colors._def_ = filter.color; - } - - var style = filter.vertical ? ' style="display:block"' : ''; - var i = 0; - - Utils.iterateOptions(filter.values, function(key, val) { - var color = filter.colors[key] || filter.colors._def_ || options.color; - var id = name + '_' + (i++); - - h.value+= '\ - \ - \ - \ -'; - }); - } - }); -}, { - font: 'glyphicons', - color: 'default' -}); - - -/*! - * jQuery QueryBuilder Bootstrap Selectpicker - * Applies Bootstrap Select on filters and operators combo-boxes. - */ - -/** - * @throws ConfigError - */ -QueryBuilder.define('bt-selectpicker', function(options) { - if (!$.fn.selectpicker || !$.fn.selectpicker.Constructor) { - Utils.error('MissingLibrary', 'Bootstrap Select is required to use "bt-selectpicker" plugin. Get it here: http://silviomoreto.github.io/bootstrap-select'); - } - - // init selectpicker - this.on('afterCreateRuleFilters', function(e, rule) { - rule.$el.find(Selectors.rule_filter).removeClass('form-control').selectpicker(options); - }); - - this.on('afterCreateRuleOperators', function(e, rule) { - rule.$el.find(Selectors.rule_operator).removeClass('form-control').selectpicker(options); - }); - - // update selectpicker on change - this.on('afterUpdateRuleFilter', function(e, rule) { - rule.$el.find(Selectors.rule_filter).selectpicker('render'); - }); - - this.on('afterUpdateRuleOperator', function(e, rule) { - rule.$el.find(Selectors.rule_operator).selectpicker('render'); - }); -}, { - container: 'body', - style: 'btn-inverse btn-xs', - width: 'auto', - showIcon: false -}); - - -/*! - * jQuery QueryBuilder Bootstrap Tooltip errors - * Applies Bootstrap Tooltips on validation error messages. - */ - -/** - * @throws ConfigError - */ -QueryBuilder.define('bt-tooltip-errors', function(options) { - if (!$.fn.tooltip || !$.fn.tooltip.Constructor || !$.fn.tooltip.Constructor.prototype.fixTitle) { - Utils.error('MissingLibrary', 'Bootstrap Tooltip is required to use "bt-tooltip-errors" plugin. Get it here: http://getbootstrap.com'); - } - - var self = this; - - // add BT Tooltip data - this.on('getRuleTemplate.filter getGroupTemplate.filter', function(h) { - var $h = $(h.value); - $h.find(Selectors.error_container).attr('data-toggle', 'tooltip'); - h.value = $h.prop('outerHTML'); - }); - - // init/refresh tooltip when title changes - this.model.on('update', function(e, node, field) { - if (field == 'error' && self.settings.display_errors) { - node.$el.find(Selectors.error_container).eq(0) - .tooltip(options) - .tooltip('hide') - .tooltip('fixTitle'); - } - }); -}, { - placement: 'right' -}); - - -/*! - * jQuery QueryBuilder Change Filters - * Allows to change available filters after plugin initialization. - */ - -QueryBuilder.extend({ - /** - * Change the filters of the builder - * @throws ChangeFilterError - * @param {boolean,optional} delete rules using old filters - * @param {object[]} new filters - */ - setFilters: function(delete_orphans, filters) { - var self = this; - - if (filters === undefined) { - filters = delete_orphans; - delete_orphans = false; - } - - filters = this.checkFilters(filters); - filters = this.change('setFilters', filters); - - var filtersIds = filters.map(function(filter) { - return filter.id; - }); - - // check for orphans - if (!delete_orphans) { - (function checkOrphans(node) { - node.each( - function(rule) { - if (rule.filter && filtersIds.indexOf(rule.filter.id) === -1) { - Utils.error('ChangeFilter', 'A rule is using filter "{0}"', rule.filter.id); - } - }, - checkOrphans - ); - }(this.model.root)); - } - - // replace filters - this.filters = filters; - - // apply on existing DOM - (function updateBuilder(node) { - node.each(true, - function(rule) { - if (rule.filter && filtersIds.indexOf(rule.filter.id) === -1) { - rule.drop(); - } - else { - self.createRuleFilters(rule); - - rule.$el.find(Selectors.rule_filter).val(rule.filter ? rule.filter.id : '-1'); - } - }, - updateBuilder - ); - }(this.model.root)); - - // update plugins - if (this.settings.plugins) { - if (this.settings.plugins['unique-filter']) { - this.updateDisabledFilters(); - } - if (this.settings.plugins['bt-selectpicker']) { - this.$el.find(Selectors.rule_filter).selectpicker('render'); - } - } - - // reset the default_filter if does not exist anymore - if (this.settings.default_filter) { - try { - this.getFilterById(this.settings.default_filter); - } - catch (e) { - this.settings.default_filter = null; - } - } - - this.trigger('afterSetFilters', filters); - }, - - /** - * Adds a new filter to the builder - * @param {object|object[]} the new filter - * @param {mixed,optional} numeric index or '#start' or '#end' - */ - addFilter: function(new_filters, position) { - if (position === undefined || position == '#end') { - position = this.filters.length; - } - else if (position == '#start') { - position = 0; - } - - if (!$.isArray(new_filters)) { - new_filters = [new_filters]; - } - - var filters = $.extend(true, [], this.filters); - - // numeric position - if (parseInt(position) == position) { - Array.prototype.splice.apply(filters, [position, 0].concat(new_filters)); - } - else { - // after filter by its id - if (this.filters.some(function(filter, index) { - if (filter.id == position) { - position = index + 1; - return true; - } - })) { - Array.prototype.splice.apply(filters, [position, 0].concat(new_filters)); - } - // defaults to end of list - else { - Array.prototype.push.apply(filters, new_filters); - } - } - - this.setFilters(filters); - }, - - /** - * Removes a filter from the builder - * @param {string|string[]} the filter id - * @param {boolean,optional} delete rules using old filters - */ - removeFilter: function(filter_ids, delete_orphans) { - var filters = $.extend(true, [], this.filters); - if (typeof filter_ids === 'string') { - filter_ids = [filter_ids]; - } - - filters = filters.filter(function(filter) { - return filter_ids.indexOf(filter.id) === -1; - }); - - this.setFilters(delete_orphans, filters); - } -}); - - -/*! - * jQuery QueryBuilder Filter Description - * Provides three ways to display a description about a filter: inline, Bootsrap Popover or Bootbox. - */ - -/** - * @throws ConfigError - */ -QueryBuilder.define('filter-description', function(options) { - /** - * INLINE - */ - if (options.mode === 'inline') { - this.on('afterUpdateRuleFilter', function(e, rule) { - var $p = rule.$el.find('p.filter-description'); - - if (!rule.filter || !rule.filter.description) { - $p.hide(); - } - else { - if ($p.length === 0) { - $p = $('

'); - $p.appendTo(rule.$el); - } - else { - $p.show(); - } - - $p.html(' ' + rule.filter.description); - } - }); - } - /** - * POPOVER - */ - else if (options.mode === 'popover') { - if (!$.fn.popover || !$.fn.popover.Constructor || !$.fn.popover.Constructor.prototype.fixTitle) { - Utils.error('MissingLibrary', 'Bootstrap Popover is required to use "filter-description" plugin. Get it here: http://getbootstrap.com'); - } - - this.on('afterUpdateRuleFilter', function(e, rule) { - var $b = rule.$el.find('button.filter-description'); - - if (!rule.filter || !rule.filter.description) { - $b.hide(); - - if ($b.data('bs.popover')) { - $b.popover('hide'); - } - } - else { - if ($b.length === 0) { - $b = $(''); - $b.prependTo(rule.$el.find(Selectors.rule_actions)); - - $b.popover({ - placement: 'left', - container: 'body', - html: true - }); - - $b.on('mouseout', function() { - $b.popover('hide'); - }); - } - else { - $b.show(); - } - - $b.data('bs.popover').options.content = rule.filter.description; - - if ($b.attr('aria-describedby')) { - $b.popover('show'); - } - } - }); - } - /** - * BOOTBOX - */ - else if (options.mode === 'bootbox') { - if (!('bootbox' in window)) { - Utils.error('MissingLibrary', 'Bootbox is required to use "filter-description" plugin. Get it here: http://bootboxjs.com'); - } - - this.on('afterUpdateRuleFilter', function(e, rule) { - var $b = rule.$el.find('button.filter-description'); - - if (!rule.filter || !rule.filter.description) { - $b.hide(); - } - else { - if ($b.length === 0) { - $b = $(''); - $b.prependTo(rule.$el.find(Selectors.rule_actions)); - - $b.on('click', function() { - bootbox.alert($b.data('description')); - }); - } - - $b.data('description', rule.filter.description); - } - }); - } -}, { - icon: 'glyphicon glyphicon-info-sign', - mode: 'popover' -}); - - -/*! - * jQuery QueryBuilder Invert - * Allows to invert a rule operator, a group condition or the entire builder. - */ - -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.define('invert', function(options) { - var self = this; - - /** - * Bind events - */ - this.on('afterInit', function() { - self.$el.on('click.queryBuilder', '[data-invert=group]', function() { - var $group = $(this).closest(Selectors.group_container); - self.invert(Model($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(Model($rule), options); - }); - } - }); - - /** - * Modify templates - */ - this.on('getGroupTemplate.filter', function(h, level) { - var $h = $(h.value); - $h.find(Selectors.condition_container).after(''); - 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(''); - h.value = $h.prop('outerHTML'); - }); - } -}, { - icon: 'glyphicon glyphicon-random', - recursive: true, - invert_rules: true, - display_rules_button: false, - silent_fail: false -}); - -QueryBuilder.extend({ - /** - * Invert a Group, a Rule or the whole builder - * @throws InvertConditionError, InvertOperatorError - * @param {Node,optional} - * @param {object,optional} - */ - 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) { - this.trigger('afterInvert', node, options); - } - } -}); - - -/*! - * jQuery QueryBuilder MongoDB Support - * Allows to export rules as a MongoDB find object as well as populating the builder from a MongoDB object. + * jQuery QueryBuilder OGC Filter Support + * Allows to export rules as a SQL WHERE statement as well as populating the builder from an SQL query. */ // DEFAULT CONFIG // =============================== QueryBuilder.defaults({ - mongoOperators: { - equal: function(v) { return v[0]; }, - not_equal: function(v) { return { '$ne': v[0] }; }, - in: function(v) { return { '$in': v }; }, - not_in: function(v) { return { '$nin': v }; }, - less: function(v) { return { '$lt': v[0] }; }, - less_or_equal: function(v) { return { '$lte': v[0] }; }, - greater: function(v) { return { '$gt': v[0] }; }, - greater_or_equal: function(v) { return { '$gte': v[0] }; }, - between: function(v) { return { '$gte': v[0], '$lte': v[1] }; }, - not_between: function(v) { return { '$lt': v[0], '$gt': v[1] }; }, - begins_with: function(v) { return { '$regex': '^' + Utils.escapeRegExp(v[0]) }; }, - not_begins_with: function(v) { return { '$regex': '^(?!' + Utils.escapeRegExp(v[0]) + ')' }; }, - contains: function(v) { return { '$regex': Utils.escapeRegExp(v[0]) }; }, - not_contains: function(v) { return { '$regex': '^((?!' + Utils.escapeRegExp(v[0]) + ').)*$', '$options': 's' }; }, - ends_with: function(v) { return { '$regex': Utils.escapeRegExp(v[0]) + '$' }; }, - not_ends_with: function(v) { return { '$regex': '(? SQL conversion */ + filterOperators: { + equal: { op: 'ogc:PropertyIsEqualTo' }, + not_equal: { op: 'ogc:PropertyIsNotEqualTo' }, + less: { op: 'ogc:PropertyIsLessThan' }, + less_or_equal: { op: 'ogc:PropertyIsLessThanOrEqualTo' }, + greater: { op: 'ogc:PropertyIsGreaterThan' }, + greater_or_equal: { op: 'ogc:PropertyIsLessThanOrEqualTo' }, + between: { op: 'ogc:PropertyIsBetween', sep: 'And' }, + is_null: { op: 'ogc:PropertyIsNull' } + }, - mongoRuleOperators: { - $ne: function(v) { - v = v.$ne; + + /* statements for internal -> SQL conversion */ + filterStatements: { + 'question_mark': function() { + var params = []; return { - 'val': v, - 'op': v === null ? 'is_not_null' : (v === '' ? 'is_not_empty' : 'not_equal') + add: function(rule, value) { + params.push(value); + return '?'; + }, + run: function() { + return params; + } }; }, - eq: function(v) { + + 'numbered': function(char) { + if (!char || char.length > 1) char = '$'; + var index = 0; + var params = []; return { - 'val': v, - 'op': v === null ? 'is_null' : (v === '' ? 'is_empty' : 'equal') + add: function(rule, value) { + params.push(value); + index++; + return char + index; + }, + run: function() { + return params; + } }; }, - $regex: function(v) { - v = v.$regex; - if (v.slice(0, 4) == '^(?!' && v.slice(-1) == ')') { - return { 'val': v.slice(4, -1), 'op': 'not_begins_with' }; - } - else if (v.slice(0, 5) == '^((?!' && v.slice(-5) == ').)*$') { - return { 'val': v.slice(5, -5), 'op': 'not_contains' }; - } - else if (v.slice(0, 4) == '(? 1) char = ':'; + var indexes = {}; + var params = {}; + return { + add: function(rule, value) { + if (!indexes[rule.field]) indexes[rule.field] = 1; + var key = rule.field + '_' + (indexes[rule.field]++); + params[key] = value; + return char + key; + }, + run: function() { + return params; + } + }; + } } }); @@ -3345,349 +2835,96 @@ QueryBuilder.defaults({ // =============================== QueryBuilder.extend({ /** - * Get rules as MongoDB query - * @throws UndefinedMongoConditionError, UndefinedMongoOperatorError + * Get rules as Filter query + * @throws UndefinedFilterConditionError, UndefinedFilterOperatorError * @param data {object} (optional) rules * @return {object} */ - getMongo: function(data) { + getFilter: function(data) { data = (data === undefined) ? this.getRules() : data; + var nl = '\n'; var self = this; - return (function parse(data) { + var filterXML = (function parse(data) { + var parts = []; + if (!data.condition) { data.condition = self.settings.default_condition; } if (['AND', 'OR'].indexOf(data.condition.toUpperCase()) === -1) { - Utils.error('UndefinedMongoCondition', 'Unable to build MongoDB query with condition "{0}"', data.condition); + Utils.error('UndefinedSQLCondition', 'Unable to build SQL query with condition "{0}"', data.condition); } if (!data.rules) { - return {}; + return ''; } - var parts = []; - - data.rules.forEach(function(rule) { - if (rule.rules && rule.rules.length > 0) { - parts.push(parse(rule)); - } - else { - var mdb = self.settings.mongoOperators[rule.operator]; - var ope = self.getOperatorByType(rule.operator); - var values = []; - - if (mdb === undefined) { - Utils.error('UndefinedMongoOperator', 'Unknown MongoDB operation for operator "{0}"', rule.operator); - } - - if (ope.nb_inputs !== 0) { - if (!(rule.value instanceof Array)) { - rule.value = [rule.value]; - } - - rule.value.forEach(function(v) { - values.push(Utils.changeType(v, rule.type, false)); - }); - } - - var part = {}; - part[rule.field] = mdb.call(self, values); - parts.push(part); - } - }); - - var res = {}; - if (parts.length > 0) { - res['$' + data.condition.toLowerCase()] = parts; + parts.push(''); + if (data.condition.toUpperCase() === 'OR') { + parts.push(''); + } else { + parts.push(''); } - return res; - }(data)); - }, - - /** - * Convert MongoDB object to rules - * @throws MongoParseError, UndefinedMongoConditionError, UndefinedMongoOperatorError - * @param data {object} query object - * @return {object} - */ - getRulesFromMongo: function(data) { - if (data === undefined || data === null) { - return null; - } - var self = this; - var conditions = { - '$and': 'AND', - '$or': 'OR' - }; - return (function parse(data) { - var topKeys = Object.keys(data); - - if (topKeys.length > 1) { - Utils.error('MongoParse', 'Invalid MongoDB query format'); - } - if (!conditions[topKeys[0].toLowerCase()]) { - Utils.error('UndefinedMongoCondition', 'Unable to build MongoDB query with condition "{0}"', topKeys[0]); - } - - var rules = data[topKeys[0]]; - var parts = []; + data.rules.forEach(function(rule) { - rules.forEach(function(rule) { - var keys = Object.keys(rule); + var filterCmd = self.settings.filterOperators[rule.operator]; + var value = ''; - if (conditions[keys[0].toLowerCase()]) { - parts.push(parse(rule)); + if (filterCmd === undefined) { + Utils.error('UndefinedFilterOperator', 'Unknown Filter operation for operator "{0}"', rule.operator); } - else { - var field = keys[0]; - var value = rule[field]; - var operator = determineMongoOperator(value, field); - if (operator === undefined) { - Utils.error('MongoParse', 'Invalid MongoDB query format'); +/* rule.value.forEach(function(v, i) { + if (i > 0) { + value += sql.sep; } - var mdbrl = self.settings.mongoRuleOperators[operator]; - if (mdbrl === undefined) { - Utils.error('UndefinedMongoOperator', 'JSON Rule operation unknown for operator "{0}"', operator); + if (rule.type == 'integer' || rule.type == 'double' || rule.type == 'boolean') { + v = Utils.changeType(v, rule.type, true); + } + else if (!stmt) { + v = Utils.escapeString(v); } - var opVal = mdbrl.call(self, value); - parts.push({ - id: self.change('getMongoDBFieldID', field, value), - field: field, - operator: opVal.op, - value: opVal.val - }); - } - }); - - var res = {}; - if (parts.length > 0) { - res.condition = conditions[topKeys[0].toLowerCase()]; - res.rules = parts; - } - return res; - }(data)); - }, - - /** - * Set rules from MongoDB object - * @param data {object} - */ - setRulesFromMongo: function(data) { - this.setRules(this.getRulesFromMongo(data)); - } -}); - -/** - * Find which operator is used in a MongoDB sub-object - * @param {mixed} value - * @param {string} field - * @return {string|undefined} - */ -function determineMongoOperator(value, field) { - if (value !== null && typeof value == 'object') { - var subkeys = Object.keys(value); - - if (subkeys.length === 1) { - return subkeys[0]; - } - else { - if (value.$gte !== undefined && value.$lte !== undefined) { - return 'between'; - } - if (value.$lt !== undefined && value.$gt !== undefined) { - return 'not_between'; - } - else if (value.$regex !== undefined) { // optional $options - return '$regex'; - } - else { - return; - } - } - } - else { - return 'eq'; - } -} - - -/*! - * jQuery QueryBuilder Sortable - * Enables drag & drop sort of rules. - */ - -Selectors.rule_and_group_containers = Selectors.rule_container + ', ' + Selectors.group_container; - -QueryBuilder.define('sortable', function(options) { - /** - * Init HTML5 drag and drop - */ - this.on('afterInit', function(e) { - // configure jQuery to use dataTransfer - $.event.props.push('dataTransfer'); - - var placeholder; - var src; - var self = e.builder; - - // only add "draggable" attribute when hovering drag handle - // preventing text select bug in Firefox - self.$el.on('mouseover.queryBuilder', '.drag-handle', function() { - self.$el.find(Selectors.rule_and_group_containers).attr('draggable', true); - }); - self.$el.on('mouseout.queryBuilder', '.drag-handle', function() { - self.$el.find(Selectors.rule_and_group_containers).removeAttr('draggable'); - }); - - // dragstart: create placeholder and hide current element - self.$el.on('dragstart.queryBuilder', '[draggable]', function(e) { - e.stopPropagation(); - - // notify drag and drop (only dummy text) - e.dataTransfer.setData('text', 'drag'); - - src = Model(e.target); - - // Chrome glitchs - // - helper invisible if hidden immediately - // - "dragend" is called immediately if we modify the DOM directly - setTimeout(function() { - var ph = $('
 
'); - ph.css('min-height', src.$el.height()); + else { + if (typeof v == 'string') { + v = '\'' + v + '\''; + } - placeholder = src.parent.addRule(ph, src.getPos()); + value += v; + } + });*/ - src.$el.hide(); - }, 0); - }); + parts.push('<' + filterCmd.op + '>' + nl); + parts.push('' + rule.field + '' + nl); + parts.push('' + rule.value + '' + nl); + parts.push('' + nl); - // dragenter: move the placeholder - self.$el.on('dragenter.queryBuilder', '[draggable]', function(e) { - e.preventDefault(); - e.stopPropagation(); + }); - if (placeholder) { - moveSortableToTarget(placeholder, $(e.target)); + if (data.condition.toUpperCase() === 'OR') { + parts.push('
'); + } else { + parts.push(''); } - }); - - // dragover: prevent glitches - self.$el.on('dragover.queryBuilder', '[draggable]', function(e) { - e.preventDefault(); - e.stopPropagation(); - }); - - // drop: move current element - self.$el.on('drop.queryBuilder', function(e) { - e.preventDefault(); - e.stopPropagation(); - - moveSortableToTarget(src, $(e.target)); - }); + parts.push('
') - // dragend: show current element and delete placeholder - self.$el.on('dragend.queryBuilder', '[draggable]', function(e) { - e.preventDefault(); - e.stopPropagation(); - - src.$el.show(); - placeholder.drop(); - - self.$el.find(Selectors.rule_and_group_containers).removeAttr('draggable'); - - self.trigger('afterMove', src); - - src = placeholder = null; - }); - }); - - /** - * 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 - */ - 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(); - } - }); - - /** - * Modify templates - */ - this.on('getGroupTemplate.filter', function(h, level) { - if (level > 1) { - var $h = $(h.value); - $h.find(Selectors.condition_container).after('
'); - h.value = $h.prop('outerHTML'); - } - }); - - this.on('getRuleTemplate.filter', function(h) { - var $h = $(h.value); - $h.find(Selectors.rule_header).after('
'); - h.value = $h.prop('outerHTML'); - }); -}, { - default_no_sortable: false, - icon: 'glyphicon glyphicon-sort' -}); - -/** - * Move an element (placeholder or actual object) depending on active target - * @param {Node} - * @param {jQuery} - */ -function moveSortableToTarget(element, target) { - var parent; - - // on rule - parent = target.closest(Selectors.rule_container); - if (parent.length) { - element.moveAfter(Model(parent)); - return; - } + return parts.join(' '); + }(data)); - // on group header - parent = target.closest(Selectors.group_header); - if (parent.length) { - parent = target.closest(Selectors.group_container); - element.moveAtBegin(Model(parent)); - return; + return { + filter: filterXML + }; } +}); - // on group - parent = target.closest(Selectors.group_container); - if (parent.length) { - element.moveAtEnd(Model(parent)); - return; - } +function getStmtConfig(stmt) { + var config = stmt.match(/(question_mark|numbered|named)(?:\((.)\))?/); + if (!config) config = [null, 'question_mark', undefined]; + return config; } @@ -4127,86 +3364,6 @@ function getStmtConfig(stmt) { } -/*! - * jQuery QueryBuilder Unique Filter - * Allows to define some filters as "unique": ie which can be used for only one rule, globally or in the same group. - */ - -QueryBuilder.define('unique-filter', function() { - this.status.used_filters = {}; - - this.on('afterUpdateRuleFilter', this.updateDisabledFilters); - this.on('afterDeleteRule', this.updateDisabledFilters); - this.on('afterCreateRuleFilters', this.applyDisabledFilters); - this.on('afterReset', this.clearDisabledFilters); - this.on('afterClear', this.clearDisabledFilters); -}); - -QueryBuilder.extend({ - updateDisabledFilters: function(e) { - var self = e ? e.builder : this; - - self.status.used_filters = {}; - - if (!self.model) { - return; - } - - // get used filters - (function walk(group) { - group.each(function(rule) { - if (rule.filter && rule.filter.unique) { - if (!self.status.used_filters[rule.filter.id]) { - self.status.used_filters[rule.filter.id] = []; - } - if (rule.filter.unique == 'group') { - self.status.used_filters[rule.filter.id].push(rule.parent); - } - } - }, function(group) { - walk(group); - }); - }(self.model.root)); - - self.applyDisabledFilters(e); - }, - - clearDisabledFilters: function(e) { - var self = e ? e.builder : this; - - self.status.used_filters = {}; - - self.applyDisabledFilters(e); - }, - - applyDisabledFilters: function(e) { - var self = e ? e.builder : this; - - // re-enable everything - self.$el.find(Selectors.filter_container + ' option').prop('disabled', false); - - // disable some - $.each(self.status.used_filters, function(filterId, groups) { - if (groups.length === 0) { - self.$el.find(Selectors.filter_container + ' option[value="' + filterId + '"]:not(:selected)').prop('disabled', true); - } - else { - groups.forEach(function(group) { - group.each(function(rule) { - rule.$el.find(Selectors.filter_container + ' option[value="' + filterId + '"]:not(:selected)').prop('disabled', true); - }); - }); - } - }); - - // update Selectpicker - if (self.settings.plugins && self.settings.plugins['bt-selectpicker']) { - self.$el.find(Selectors.rule_filter).selectpicker('render'); - } - } -}); - - /*! * jQuery QueryBuilder 2.3.3 * Locale: English (en) @@ -4269,8 +3426,7 @@ QueryBuilder.regional['en'] = { "datetime_exceed_max": "Must be before {0}", "boolean_not_valid": "Not a boolean", "operator_not_multiple": "Operator {0} cannot accept multiple values" - }, - "invert": "Invert" + } }; QueryBuilder.defaults({ lang_code: 'en' }); diff --git a/dist/js/query-builder.min.js b/dist/js/query-builder.min.js index ad279385..df0f50eb 100644 --- a/dist/js/query-builder.min.js +++ b/dist/js/query-builder.min.js @@ -4,5 +4,5 @@ * Licensed under MIT (http://opensource.org/licenses/MIT) */ -!function(a,b){"function"==typeof define&&define.amd?define(["jquery","doT","jQuery.extendext"],b):b(a.jQuery,a.doT)}(this,function($,a){"use strict";function b(a){return this instanceof b?(this.root=null,void(this.$=$(this))):b.getModel(a)}function c(a,b){b.forEach(function(b){Object.defineProperty(a.prototype,b,{enumerable:!0,get:function(){return this.__[b]},set:function(a){var c=null!==this.__[b]&&"object"==typeof this.__[b]?$.extend({},this.__[b]):this.__[b];this.__[b]=a,null!==this.model&&this.model.trigger("update",this,b,a,c)}})})}function d(a,b){if(null!==a&&"object"==typeof a){var c=Object.keys(a);return 1===c.length?c[0]:void 0!==a.$gte&&void 0!==a.$lte?"between":void 0!==a.$lt&&void 0!==a.$gt?"not_between":void 0!==a.$regex?"$regex":void 0}return"eq"}function e(a,c){var d;return d=c.closest(h.rule_container),d.length?void a.moveAfter(b(d)):(d=c.closest(h.group_header),d.length?(d=c.closest(h.group_container),void a.moveAtBegin(b(d))):(d=c.closest(h.group_container),d.length?void a.moveAtEnd(b(d)):void 0))}function f(a){var b=a.match(/(question_mark|numbered|named)(?:\((.)\))?/);return b||(b=[null,"question_mark",void 0]),b}var g=function(a,b){this.init(a,b)};$.extend(g.prototype,{change:function(a,b){var c=new $.Event(a+".queryBuilder.filter",{builder:this,value:b});return this.$el.triggerHandler(c,Array.prototype.slice.call(arguments,2)),c.value},trigger:function(a){var b=new $.Event(a+".queryBuilder",{builder:this});return this.$el.triggerHandler(b,Array.prototype.slice.call(arguments,1)),b},on:function(a,b){return this.$el.on(a+".queryBuilder",b),this},off:function(a,b){return this.$el.off(a+".queryBuilder",b),this},once:function(a,b){return this.$el.one(a+".queryBuilder",b),this}}),g.plugins={},g.defaults=function(a){return"object"!=typeof a?"string"==typeof a?"object"==typeof g.DEFAULTS[a]?$.extend(!0,{},g.DEFAULTS[a]):g.DEFAULTS[a]:$.extend(!0,{},g.DEFAULTS):void $.extendext(!0,"replace",g.DEFAULTS,a)},g.define=function(a,b,c){g.plugins[a]={fct:b,def:c||{}}},g.extend=function(a){$.extend(g.prototype,a)},g.prototype.initPlugins=function(){if(this.plugins){if($.isArray(this.plugins)){var a={};this.plugins.forEach(function(b){a[b]=null}),this.plugins=a}Object.keys(this.plugins).forEach(function(a){a in g.plugins?(this.plugins[a]=$.extend(!0,{},g.plugins[a].def,this.plugins[a]||{}),g.plugins[a].fct.call(this,this.plugins[a])):l.error("Config",'Unable to find plugin "{0}"',a)},this)}},g.types={string:"string",integer:"number","double":"number",date:"datetime",time:"datetime",datetime:"datetime","boolean":"boolean"},g.inputs=["text","textarea","radio","checkbox","select"],g.modifiable_options=["display_errors","allow_groups","allow_empty","default_condition","default_filter"];var h=g.selectors={group_container:".rules-group-container",rule_container:".rule-container",filter_container:".rule-filter-container",operator_container:".rule-operator-container",value_container:".rule-value-container",error_container:".error-container",condition_container:".rules-group-header .group-conditions",rule_header:".rule-header",group_header:".rules-group-header",group_actions:".group-actions",rule_actions:".rule-actions",rules_list:".rules-group-body>.rules-list",group_condition:".rules-group-header [name$=_cond]",rule_filter:".rule-filter-container [name$=_filter]",rule_operator:".rule-operator-container [name$=_operator]",rule_value:".rule-value-container [name*=_value_]",add_rule:"[data-add=rule]",delete_rule:"[data-delete=rule]",add_group:"[data-add=group]",delete_group:"[data-delete=group]"};g.templates={},g.regional={},g.OPERATORS={equal:{type:"equal",nb_inputs:1,multiple:!1,apply_to:["string","number","datetime","boolean"]},not_equal:{type:"not_equal",nb_inputs:1,multiple:!1,apply_to:["string","number","datetime","boolean"]},"in":{type:"in",nb_inputs:1,multiple:!0,apply_to:["string","number","datetime"]},not_in:{type:"not_in",nb_inputs:1,multiple:!0,apply_to:["string","number","datetime"]},less:{type:"less",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},less_or_equal:{type:"less_or_equal",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},greater:{type:"greater",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},greater_or_equal:{type:"greater_or_equal",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},between:{type:"between",nb_inputs:2,multiple:!1,apply_to:["number","datetime"]},not_between:{type:"not_between",nb_inputs:2,multiple:!1,apply_to:["number","datetime"]},begins_with:{type:"begins_with",nb_inputs:1,multiple:!1,apply_to:["string"]},not_begins_with:{type:"not_begins_with",nb_inputs:1,multiple:!1,apply_to:["string"]},contains:{type:"contains",nb_inputs:1,multiple:!1,apply_to:["string"]},not_contains:{type:"not_contains",nb_inputs:1,multiple:!1,apply_to:["string"]},ends_with:{type:"ends_with",nb_inputs:1,multiple:!1,apply_to:["string"]},not_ends_with:{type:"not_ends_with",nb_inputs:1,multiple:!1,apply_to:["string"]},is_empty:{type:"is_empty",nb_inputs:0,multiple:!1,apply_to:["string"]},is_not_empty:{type:"is_not_empty",nb_inputs:0,multiple:!1,apply_to:["string"]},is_null:{type:"is_null",nb_inputs:0,multiple:!1,apply_to:["string","number","datetime","boolean"]},is_not_null:{type:"is_not_null",nb_inputs:0,multiple:!1,apply_to:["string","number","datetime","boolean"]}},g.DEFAULTS={filters:[],plugins:[],sort_filters:!1,display_errors:!0,allow_groups:-1,allow_empty:!1,conditions:["AND","OR"],default_condition:"AND",inputs_separator:" , ",select_placeholder:"------",display_empty_filter:!0,default_filter:null,optgroups:{},default_rule_flags:{filter_readonly:!1,operator_readonly:!1,value_readonly:!1,no_delete:!1},default_group_flags:{condition_readonly:!1,no_delete:!1},templates:{group:null,rule:null,filterSelect:null,operatorSelect:null},lang_code:"en",lang:{},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"],icons:{add_group:"glyphicon glyphicon-plus-sign",add_rule:"glyphicon glyphicon-plus",remove_group:"glyphicon glyphicon-remove",remove_rule:"glyphicon glyphicon-remove",error:"glyphicon glyphicon-warning-sign"}},g.prototype.init=function(c,d){c[0].queryBuilder=this,this.$el=c,this.settings=$.extendext(!0,"replace",{},g.DEFAULTS,d),this.model=new b,this.status={group_id:0,rule_id:0,generated_id:!1,has_optgroup:!1,has_operator_oprgroup:!1,id:null,updating_value:!1},this.settings.allow_groups===!1?this.settings.allow_groups=0:this.settings.allow_groups===!0&&(this.settings.allow_groups=-1),this.filters=this.settings.filters,this.icons=this.settings.icons,this.operators=this.settings.operators,this.templates=this.settings.templates,this.plugins=this.settings.plugins,void 0===g.regional.en&&l.error("Config",'"i18n/en.js" not loaded.'),this.lang=$.extendext(!0,"replace",{},g.regional.en,g.regional[this.settings.lang_code],this.settings.lang),Object.keys(this.templates).forEach(function(b){this.templates[b]||(this.templates[b]=g.templates[b]),"string"==typeof this.templates[b]&&(this.templates[b]=a.template(this.templates[b]))},this),this.$el.attr("id")||(this.$el.attr("id","qb_"+Math.floor(99999*Math.random())),this.status.generated_id=!0),this.status.id=this.$el.attr("id"),this.$el.addClass("query-builder form-inline"),this.filters=this.checkFilters(this.filters),this.operators=this.checkOperators(this.operators),this.bindEvents(),this.initPlugins(),this.trigger("afterInit"),d.rules?(this.setRules(d.rules),delete this.settings.rules):this.setRoot(!0)},g.prototype.checkFilters=function(a){var b=[];if(a&&0!==a.length||l.error("Config","Missing filters list"),a.forEach(function(a,c){switch(a.id||l.error("Config","Missing filter {0} id",c),-1!=b.indexOf(a.id)&&l.error("Config",'Filter "{0}" already defined',a.id),b.push(a.id),a.type?g.types[a.type]||l.error("Config",'Invalid type "{0}"',a.type):a.type="string",a.input?"function"!=typeof a.input&&-1==g.inputs.indexOf(a.input)&&l.error("Config",'Invalid input "{0}"',a.input):a.input="text",a.operators&&a.operators.forEach(function(a){"string"!=typeof a&&l.error("Config","Filter operators must be global operators types (string)")}),a.field||(a.field=a.id),a.label||(a.label=a.field),a.optgroup?(this.status.has_optgroup=!0,this.settings.optgroups[a.optgroup]||(this.settings.optgroups[a.optgroup]=a.optgroup)):a.optgroup=null,a.input){case"radio":case"checkbox":(!a.values||a.values.length<1)&&l.error("Config",'Missing filter "{0}" values',a.id);break;case"select":a.placeholder&&(void 0===a.placeholder_value&&(a.placeholder_value=-1),l.iterateOptions(a.values,function(b){b==a.placeholder_value&&l.error("Config",'Placeholder of filter "{0}" overlaps with one of its values',a.id)}))}},this),this.settings.sort_filters)if("function"==typeof this.settings.sort_filters)a.sort(this.settings.sort_filters);else{var c=this;a.sort(function(a,b){return c.translateLabel(a.label).localeCompare(c.translateLabel(b.label))})}return this.status.has_optgroup&&(a=l.groupSort(a,"optgroup")),a},g.prototype.checkOperators=function(a){var b=[];return a.forEach(function(c,d){"string"==typeof c?(g.OPERATORS[c]||l.error("Config",'Unknown operator "{0}"',c),a[d]=c=$.extendext(!0,"replace",{},g.OPERATORS[c])):(c.type||l.error("Config",'Missing "type" for operator {0}',d),g.OPERATORS[c.type]&&(a[d]=c=$.extendext(!0,"replace",{},g.OPERATORS[c.type],c)),void 0!==c.nb_inputs&&void 0!==c.apply_to||l.error("Config",'Missing "nb_inputs" and/or "apply_to" for operator "{0}"',c.type)),-1!=b.indexOf(c.type)&&l.error("Config",'Operator "{0}" already defined',c.type),b.push(c.type),c.optgroup?(this.status.has_operator_optgroup=!0,this.settings.optgroups[c.optgroup]||(this.settings.optgroups[c.optgroup]=c.optgroup)):c.optgroup=null},this),this.status.has_operator_optgroup&&(a=l.groupSort(a,"optgroup")),a},g.prototype.bindEvents=function(){var a=this;this.$el.on("change.queryBuilder",h.group_condition,function(){if($(this).is(":checked")){var a=$(this).closest(h.group_container);b(a).condition=$(this).val()}}),this.$el.on("change.queryBuilder",h.rule_filter,function(){var c=$(this).closest(h.rule_container);b(c).filter=a.getFilterById($(this).val())}),this.$el.on("change.queryBuilder",h.rule_operator,function(){var c=$(this).closest(h.rule_container);b(c).operator=a.getOperatorByType($(this).val())}),this.$el.on("click.queryBuilder",h.add_rule,function(){var c=$(this).closest(h.group_container);a.addRule(b(c))}),this.$el.on("click.queryBuilder",h.delete_rule,function(){var c=$(this).closest(h.rule_container);a.deleteRule(b(c))}),0!==this.settings.allow_groups&&(this.$el.on("click.queryBuilder",h.add_group,function(){var c=$(this).closest(h.group_container);a.addGroup(b(c))}),this.$el.on("click.queryBuilder",h.delete_group,function(){var c=$(this).closest(h.group_container);a.deleteGroup(b(c))})),this.model.on({drop:function(b,c){c.$el.remove(),a.refreshGroupsConditions()},add:function(b,c,d){0===d?c.$el.prependTo(c.parent.$el.find(">"+h.rules_list)):c.$el.insertAfter(c.parent.rules[d-1].$el),a.refreshGroupsConditions()},move:function(b,c,d,e){c.$el.detach(),0===e?c.$el.prependTo(d.$el.find(">"+h.rules_list)):c.$el.insertAfter(d.rules[e-1].$el),a.refreshGroupsConditions()},update:function(b,c,d,e,f){if(c instanceof k)switch(d){case"error":a.displayError(c);break;case"flags":a.applyRuleFlags(c);break;case"filter":a.updateRuleFilter(c);break;case"operator":a.updateRuleOperator(c,f);break;case"value":a.updateRuleValue(c)}else switch(d){case"error":a.displayError(c);break;case"flags":a.applyGroupFlags(c);break;case"condition":a.updateGroupCondition(c)}}})},g.prototype.setRoot=function(a,b,c){a=void 0===a||a===!0;var d=this.nextGroupId(),e=$(this.getGroupTemplate(d,1));return this.$el.append(e),this.model.root=new j(null,e),this.model.root.model=this.model,this.model.root.data=b,this.model.root.flags=$.extend({},this.settings.default_group_flags,c),this.trigger("afterAddGroup",this.model.root),this.model.root.condition=this.settings.default_condition,a&&this.addRule(this.model.root),this.model.root},g.prototype.addGroup=function(a,b,c,d){b=void 0===b||b===!0;var e=a.level+1,f=this.trigger("beforeAddGroup",a,b,e);if(f.isDefaultPrevented())return null;var g=this.nextGroupId(),h=$(this.getGroupTemplate(g,e)),i=a.addGroup(h);return i.data=c,i.flags=$.extend({},this.settings.default_group_flags,d),this.trigger("afterAddGroup",i),i.condition=this.settings.default_condition,b&&this.addRule(i),i},g.prototype.deleteGroup=function(a){if(a.isRoot())return!1;var b=this.trigger("beforeDeleteGroup",a);if(b.isDefaultPrevented())return!1;var c=!0;return a.each("reverse",function(a){c&=this.deleteRule(a)},function(a){c&=this.deleteGroup(a)},this),c&&(a.drop(),this.trigger("afterDeleteGroup")),c},g.prototype.updateGroupCondition=function(a){a.$el.find(">"+h.group_condition).each(function(){var b=$(this);b.prop("checked",b.val()===a.condition),b.parent().toggleClass("active",b.val()===a.condition)}),this.trigger("afterUpdateGroupCondition",a)},g.prototype.refreshGroupsConditions=function(){!function a(b){(!b.flags||b.flags&&!b.flags.condition_readonly)&&b.$el.find(">"+h.group_condition).prop("disabled",b.rules.length<=1).parent().toggleClass("disabled",b.rules.length<=1),b.each(function(a){},function(b){a(b)},this)}(this.model.root)},g.prototype.addRule=function(a,b,c){var d=this.trigger("beforeAddRule",a);if(d.isDefaultPrevented())return null;var e=this.nextRuleId(),f=$(this.getRuleTemplate(e)),g=a.addRule(f);return void 0!==b&&(g.data=b),g.flags=$.extend({},this.settings.default_rule_flags,c),this.trigger("afterAddRule",g),this.createRuleFilters(g),!this.settings.default_filter&&this.settings.display_empty_filter||(g.filter=this.getFilterById(this.settings.default_filter||this.filters[0].id)),g},g.prototype.deleteRule=function(a){if(a.flags.no_delete)return!1;var b=this.trigger("beforeDeleteRule",a);return b.isDefaultPrevented()?!1:(a.drop(),this.trigger("afterDeleteRule"),!0)},g.prototype.createRuleFilters=function(a){var b=this.change("getRuleFilters",this.filters,a),c=$(this.getRuleFilterSelect(a,b));a.$el.find(h.filter_container).html(c),this.trigger("afterCreateRuleFilters",a)},g.prototype.createRuleOperators=function(a){var b=a.$el.find(h.operator_container).empty();if(a.filter){var c=this.getOperators(a.filter),d=$(this.getRuleOperatorSelect(a,c));b.html(d),a.__.operator=c[0],this.trigger("afterCreateRuleOperators",a,c)}},g.prototype.createRuleInput=function(a){var b=a.$el.find(h.value_container).empty();if(a.__.value=void 0,a.filter&&a.operator&&0!==a.operator.nb_inputs){for(var c=this,d=$(),e=a.filter,f=0;f0&&b.append(this.settings.inputs_separator),b.append(g),d=d.add(g)}b.show(),d.on("change "+(e.input_event||""),function(){c.status.updating_value=!0,a.value=c.getRuleValue(a),c.status.updating_value=!1}),e.plugin&&d[e.plugin](e.plugin_config||{}),this.trigger("afterCreateRuleInput",a),void 0!==e.default_value?a.value=e.default_value:(c.status.updating_value=!0,a.value=c.getRuleValue(a),c.status.updating_value=!1)}},g.prototype.updateRuleFilter=function(a){this.createRuleOperators(a),this.createRuleInput(a),a.$el.find(h.rule_filter).val(a.filter?a.filter.id:"-1"),this.trigger("afterUpdateRuleFilter",a)},g.prototype.updateRuleOperator=function(a,b){var c=a.$el.find(h.value_container);a.operator&&0!==a.operator.nb_inputs?(c.show(),(c.is(":empty")||a.operator.nb_inputs!==b.nb_inputs)&&this.createRuleInput(a)):(c.hide(),a.__.value=void 0),a.operator&&a.$el.find(h.rule_operator).val(a.operator.type),this.trigger("afterUpdateRuleOperator",a)},g.prototype.updateRuleValue=function(a){this.status.updating_value||this.setRuleValue(a,a.value),this.trigger("afterUpdateRuleValue",a)},g.prototype.applyRuleFlags=function(a){var b=a.flags;b.filter_readonly&&a.$el.find(h.rule_filter).prop("disabled",!0),b.operator_readonly&&a.$el.find(h.rule_operator).prop("disabled",!0),b.value_readonly&&a.$el.find(h.rule_value).prop("disabled",!0),b.no_delete&&a.$el.find(h.delete_rule).remove(),this.trigger("afterApplyRuleFlags",a)},g.prototype.applyGroupFlags=function(a){var b=a.flags;b.condition_readonly&&a.$el.find(">"+h.group_condition).prop("disabled",!0).parent().addClass("readonly"),b.no_delete&&a.$el.find(h.delete_group).remove(),this.trigger("afterApplyGroupFlags",a)},g.prototype.clearErrors=function(a){a=a||this.model.root,a&&(a.error=null,a instanceof j&&a.each(function(a){a.error=null},function(a){this.clearErrors(a)},this))},g.prototype.displayError=function(a){if(this.settings.display_errors)if(null===a.error)a.$el.removeClass("has-error");else{var b=$.extend([],a.error,[this.lang.errors[a.error[0]]||a.error[0]]);a.$el.addClass("has-error").find(h.error_container).eq(0).attr("title",l.fmt.apply(null,b))}},g.prototype.triggerValidationError=function(a,b,c){$.isArray(b)||(b=[b]);var d=this.trigger("validationError",a,b,c);d.isDefaultPrevented()||(a.error=b)},g.prototype.destroy=function(){this.trigger("beforeDestroy"),this.status.generated_id&&this.$el.removeAttr("id"),this.clear(),this.model=null,this.$el.off(".queryBuilder").removeClass("query-builder").removeData("queryBuilder"),delete this.$el[0].queryBuilder},g.prototype.reset=function(){this.status.group_id=1,this.status.rule_id=0,this.model.root.empty(),this.addRule(this.model.root),this.trigger("afterReset")},g.prototype.clear=function(){this.status.group_id=0,this.status.rule_id=0,this.model.root&&(this.model.root.drop(),this.model.root=null),this.trigger("afterClear")},g.prototype.setOptions=function(a){$.makeArray($(Object.keys(a)).filter(g.modifiable_options)).forEach(function(b){this.settings[b]=a[b]},this)},g.prototype.getModel=function(a){return a?b(a):this.model.root},g.prototype.validate=function(){this.clearErrors();var a=this,b=function c(b){var d=0,e=0;return b.each(function(b){if(!b.filter)return a.triggerValidationError(b,"no_filter",null),void e++;if(0!==b.operator.nb_inputs){var c=a.validateValue(b,b.value);if(c!==!0)return a.triggerValidationError(b,c,b.value),void e++}d++},function(a){c(a)?d++:e++}),e>0?!1:0!==d||a.settings.allow_empty&&b.isRoot()?!0:(a.triggerValidationError(b,"empty_group",null),!1)}(this.model.root);return this.change("validate",b)},g.prototype.getRules=function(a){if(a=$.extend({get_flags:!1},a),!this.validate())return{};var b=this,c=function d(c){var e={condition:c.condition,rules:[]};if(c.data&&(e.data=$.extendext(!0,"replace",{},c.data)),a.get_flags){var f=b.getGroupFlags(c.flags,"all"===a.get_flags);$.isEmptyObject(f)||(e.flags=f)}return c.each(function(c){var d=null;0!==c.operator.nb_inputs&&(d=c.value);var f={id:c.filter.id,field:c.filter.field,type:c.filter.type,input:c.filter.input,operator:c.operator.type,value:d};if((c.filter.data||c.data)&&(f.data=$.extendext(!0,"replace",{},c.filter.data,c.data)),a.get_flags){var g=b.getRuleFlags(c.flags,"all"===a.get_flags);$.isEmptyObject(g)||(f.flags=g)}e.rules.push(f)},function(a){e.rules.push(d(a))}),e}(this.model.root);return this.change("getRules",c)},g.prototype.setRules=function(a){$.isArray(a)&&(a={condition:this.settings.default_condition,rules:a}),a&&a.rules&&(0!==a.rules.length||this.settings.allow_empty)||l.error("RulesParse","Incorrect data object passed"),this.clear(),this.setRoot(!1,a.data,this.parseGroupFlags(a)),a=this.change("setRules",a);var b=this;!function c(a,d){null!==d&&(void 0===a.condition?a.condition=b.settings.default_condition:-1==b.settings.conditions.indexOf(a.condition)&&l.error("UndefinedCondition",'Invalid condition "{0}"',a.condition),d.condition=a.condition,a.rules.forEach(function(a){var e;if(a.rules&&a.rules.length>0)if(-1!==b.settings.allow_groups&&b.settings.allow_groups1){h=["operator_not_multiple",e.type];break}break;case"select":if(d.multiple){if(void 0===b[i]||0===b[i].length||d.placeholder&&b[i]==d.placeholder_value){h=["select_empty"];break}if(!e.multiple&&b[i].length>1){h=["operator_not_multiple",e.type];break}}else if(void 0===b[i]||d.placeholder&&b[i]==d.placeholder_value){h=["select_empty"];break}break;default:switch(g.types[d.type]){case"string":if(void 0===b[i]||0===b[i].length){h=["string_empty"];break}if(void 0!==f.min&&b[i].lengthparseInt(f.max)){h=["string_exceed_max_length",f.max];break}if(f.format&&("string"==typeof f.format&&(f.format=new RegExp(f.format)),!f.format.test(b[i]))){h=["string_invalid_format",f.format];break}break;case"number":if(void 0===b[i]||isNaN(b[i])){h=["number_nan"];break}if("integer"==d.type){if(parseInt(b[i])!=b[i]){h=["number_not_integer"];break}}else if(parseFloat(b[i])!=b[i]){h=["number_not_double"];break}if(void 0!==f.min&&b[i]parseFloat(f.max)){h=["number_exceed_max",f.max];break}if(void 0!==f.step&&"any"!==f.step){var j=(b[i]/f.step).toPrecision(14);if(parseInt(j)!=j){h=["number_wrong_step",f.step];break}}break;case"datetime":if(void 0===b[i]||0===b[i].length){h=["datetime_empty"];break}if(f.format){"moment"in window||l.error("MissingLibrary","MomentJS is required for Date/Time validation. Get it here http://momentjs.com");var k=moment(b[i],f.format);if(!k.isValid()){h=["datetime_invalid",f.format];break}if(f.min&&kmoment(f.max,f.format)){h=["datetime_exceed_max",f.max];break}}break;case"boolean":if(c=b[i].trim().toLowerCase(),"true"!==c&&"false"!==c&&"1"!==c&&"0"!==c&&1!==b[i]&&0!==b[i]){h=["boolean_not_valid"];break}}}if(h!==!0)break}return h},g.prototype.nextGroupId=function(){return this.status.id+"_group_"+this.status.group_id++},g.prototype.nextRuleId=function(){return this.status.id+"_rule_"+this.status.rule_id++},g.prototype.getOperators=function(a){"string"==typeof a&&(a=this.getFilterById(a));for(var b=[],c=0,d=this.operators.length;d>c;c++){if(a.operators){if(-1==a.operators.indexOf(this.operators[c].type))continue}else if(-1==this.operators[c].apply_to.indexOf(g.types[a.type]))continue;b.push(this.operators[c])}return a.operators&&b.sort(function(b,c){return a.operators.indexOf(b.type)-a.operators.indexOf(c.type)}),this.change("getOperators",b,a)},g.prototype.getFilterById=function(a){if("-1"==a)return null;for(var b=0,c=this.filters.length;c>b;b++)if(this.filters[b].id==a)return this.filters[b];l.error("UndefinedFilter",'Undefined filter "{0}"',a)},g.prototype.getOperatorByType=function(a){if("-1"==a)return null;for(var b=0,c=this.operators.length;c>b;b++)if(this.operators[b].type==a)return this.operators[b];l.error("UndefinedOperator",'Undefined operator "{0}"',a)},g.prototype.getRuleValue=function(a){var b=a.filter,c=a.operator,d=[];if(b.valueGetter)d=b.valueGetter.call(this,a);else{for(var e=a.$el.find(h.value_container),f=0;f '+b+" "});break;case"select":h+='";break;case"textarea":h+='";break;default:switch(e.types[c.type]){case"number":h+='=f:e<=f},j=!1;i()&&(this.rules[e]instanceof h?void 0!==c&&(j=c.call(d,this.rules[e])===!1):j=b.call(d,this.rules[e])===!1,!j);e+=g);return!j},h.prototype.contains=function(a,b){return this.getNodePos(a)!==-1||!!b&&!this.each(function(a){return!0},function(b){return!b.contains(a,!0)})};var i=function(a,b){return this instanceof i?(g.call(this,a,b),this.__.filter=null,this.__.operator=null,this.__.flags={},void(this.__.value=void 0)):new i(a,b)};i.prototype=Object.create(g.prototype),i.prototype.constructor=i,c(i,["filter","operator","value"]),e.Group=h,e.Rule=i;var j=e.utils={};j.iterateOptions=function(a,b){a&&($.isArray(a)?a.forEach(function(a){$.isPlainObject(a)?$.each(a,function(a,c){return b(a,c),!1}):b(a,a)}):$.each(a,function(a,c){b(a,c)}))},j.fmt=function(a){var b=Array.prototype.slice.call(arguments,1);return a.replace(/{([0-9]+)}/g,function(a,c){return b[parseInt(c)]})},j.error=function(a,b){var c=new Error(j.fmt.apply(null,Array.prototype.slice.call(arguments,1)));throw c.name=a+"Error",c.args=Array.prototype.slice.call(arguments,2),c},j.changeType=function(a,b,c){switch(b){case"integer":return parseInt(a);case"double":return parseFloat(a);case"boolean":var d="true"===a.trim().toLowerCase()||"1"===a.trim()||1===a;return c?d?1:0:d;default:return a}},j.escapeString=function(a){return"string"!=typeof a?a:a.replace(/[\0\n\r\b\\\'\"]/g,function(a){switch(a){case"\0":return"\\0";case"\n":return"\\n";case"\r":return"\\r";case"\b":return"\\b";default:return"\\"+a}}).replace(/\t/g,"\\t").replace(/\x1a/g,"\\Z")},j.escapeRegExp=function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},j.escapeElementId=function(a){return a?a.replace(/(\\)?([:.\[\],])/g,function(a,b,c){return b?a:"\\"+c}):a},j.groupSort=function(a,b){var c=[],d=[];return a.forEach(function(a){var e;a[b]?(e=c.lastIndexOf(a[b]),e==-1?e=c.length:e++):e=c.length,c.splice(e,0,a[b]),d.splice(e,0,a)}),d},$.fn.queryBuilder=function(a){this.length>1&&j.error("Config","Unable to initialize on multiple target");var b=this.data("queryBuilder"),c="object"==typeof a&&a||{};return b||"destroy"!=a?(b||this.data("queryBuilder",new e(this,c)),"string"==typeof a?b[a].apply(b,Array.prototype.slice.call(arguments,1)):this):this},$.fn.queryBuilder.constructor=e,$.fn.queryBuilder.defaults=e.defaults,$.fn.queryBuilder.extend=e.extend,$.fn.queryBuilder.define=e.define,$.fn.queryBuilder.regional=e.regional,e.defaults({filterOperators:{equal:{op:"ogc:PropertyIsEqualTo"},not_equal:{op:"ogc:PropertyIsNotEqualTo"},less:{op:"ogc:PropertyIsLessThan"},less_or_equal:{op:"ogc:PropertyIsLessThanOrEqualTo"},greater:{op:"ogc:PropertyIsGreaterThan"},greater_or_equal:{op:"ogc:PropertyIsLessThanOrEqualTo"},between:{op:"ogc:PropertyIsBetween",sep:"And"},is_null:{op:"ogc:PropertyIsNull"}},filterStatements:{question_mark:function(){var a=[];return{add:function(b,c){return a.push(c),"?"},run:function(){return a}}},numbered:function(a){(!a||a.length>1)&&(a="$");var b=0,c=[];return{add:function(d,e){return c.push(e),b++,a+b},run:function(){return c}}},named:function(a){(!a||a.length>1)&&(a=":");var b={},c={};return{add:function(d,e){b[d.field]||(b[d.field]=1);var f=d.field+"_"+b[d.field]++;return c[f]=e,a+f},run:function(){return c}}}}}),e.extend({getFilter:function(a){a=void 0===a?this.getRules():a;var b="\n",c=this,d=function(a){var d=[];return a.condition||(a.condition=c.settings.default_condition),["AND","OR"].indexOf(a.condition.toUpperCase())===-1&&j.error("UndefinedSQLCondition",'Unable to build SQL query with condition "{0}"',a.condition),a.rules?(d.push(""),"OR"===a.condition.toUpperCase()?d.push(""):d.push(""),a.rules.forEach(function(a){var e=c.settings.filterOperators[a.operator];void 0===e&&j.error("UndefinedFilterOperator",'Unknown Filter operation for operator "{0}"',a.operator),d.push("<"+e.op+">"+b),d.push(""+a.field+""+b),d.push(""+a.value+""+b),d.push(""+b)}),"OR"===a.condition.toUpperCase()?d.push(""):d.push(""),d.push(""),d.join(" ")):""}(a);return{filter:d}}}),e.defaults({sqlOperators:{equal:{op:"= ?"},not_equal:{op:"!= ?"},"in":{op:"IN(?)",sep:", "},not_in:{op:"NOT IN(?)",sep:", "},less:{op:"< ?"},less_or_equal:{op:"<= ?"},greater:{op:"> ?"},greater_or_equal:{op:">= ?"},between:{op:"BETWEEN ?",sep:" AND "},not_between:{op:"NOT BETWEEN ?",sep:" AND "},begins_with:{op:"LIKE(?)",mod:"{0}%"},not_begins_with:{op:"NOT LIKE(?)",mod:"{0}%"},contains:{op:"LIKE(?)",mod:"%{0}%"},not_contains:{op:"NOT LIKE(?)",mod:"%{0}%"},ends_with:{op:"LIKE(?)",mod:"%{0}"},not_ends_with:{op:"NOT LIKE(?)",mod:"%{0}"},is_empty:{op:"= ''"},is_not_empty:{op:"!= ''"},is_null:{op:"IS NULL"},is_not_null:{op:"IS NOT NULL"}},sqlRuleOperator:{"=":function(a){return{val:a,op:""===a?"is_empty":"equal"}},"!=":function(a){return{val:a,op:""===a?"is_not_empty":"not_equal"}},LIKE:function(a){return"%"==a.slice(0,1)&&"%"==a.slice(-1)?{val:a.slice(1,-1),op:"contains"}:"%"==a.slice(0,1)?{val:a.slice(1),op:"ends_with"}:"%"==a.slice(-1)?{val:a.slice(0,-1),op:"begins_with"}:void j.error("SQLParse",'Invalid value for LIKE operator "{0}"',a)},IN:function(a){return{val:a,op:"in"}},"NOT IN":function(a){return{val:a,op:"not_in"}},"<":function(a){return{val:a,op:"less"}},"<=":function(a){return{val:a,op:"less_or_equal"}},">":function(a){return{val:a,op:"greater"}},">=":function(a){return{val:a,op:"greater_or_equal"}},BETWEEN:function(a){return{val:a,op:"between"}},"NOT BETWEEN":function(a){return{val:a,op:"not_between"}},IS:function(a){return null!==a&&j.error("SQLParse","Invalid value for IS operator"),{val:null,op:"is_null"}},"IS NOT":function(a){return null!==a&&j.error("SQLParse","Invalid value for IS operator"),{val:null,op:"is_not_null"}}},sqlStatements:{question_mark:function(){var a=[];return{add:function(b,c){return a.push(c),"?"},run:function(){return a}}},numbered:function(a){(!a||a.length>1)&&(a="$");var b=0,c=[];return{add:function(d,e){return c.push(e),b++,a+b},run:function(){return c}}},named:function(a){(!a||a.length>1)&&(a=":");var b={},c={};return{add:function(d,e){b[d.field]||(b[d.field]=1);var f=d.field+"_"+b[d.field]++;return c[f]=e,a+f},run:function(){return c}}}},sqlRuleStatement:{question_mark:function(a){var b=0;return{parse:function(c){return"?"==c?a[b++]:c},esc:function(a){return a.replace(/\?/g,"'?'")}}},numbered:function(a,b){(!b||b.length>1)&&(b="$");var c=new RegExp("^\\"+b+"[0-9]+$"),d=new RegExp("\\"+b+"([0-9]+)","g");return{parse:function(b){return c.test(b)?a[b.slice(1)-1]:b},esc:function(a){return a.replace(d,"'"+("$"==b?"$$":b)+"$1'")}}},named:function(a,b){(!b||b.length>1)&&(b=":");var c=new RegExp("^\\"+b),d=new RegExp("\\"+b+"("+Object.keys(a).join("|")+")","g");return{parse:function(b){return c.test(b)?a[b.slice(1)]:b},esc:function(a){return a.replace(d,"'"+("$"==b?"$$":b)+"$1'")}}}}}),e.extend({getSQL:function(a,b,c){if(c=void 0===c?this.getRules():c,b=b===!0?"\n":" ",a===!0&&(a="question_mark"),"string"==typeof a){var e=d(a);a=this.settings.sqlStatements[e[1]](e[2])}var f=this,g=function h(c){if(c.condition||(c.condition=f.settings.default_condition),["AND","OR"].indexOf(c.condition.toUpperCase())===-1&&j.error("UndefinedSQLCondition",'Unable to build SQL query with condition "{0}"',c.condition),!c.rules)return"";var d=[];return c.rules.forEach(function(c){if(c.rules&&c.rules.length>0)d.push("("+b+h(c)+b+")"+b);else{var e=f.settings.sqlOperators[c.operator],g=f.getOperatorByType(c.operator),i="";void 0===e&&j.error("UndefinedSQLOperator",'Unknown SQL operation for operator "{0}"',c.operator),0!==g.nb_inputs&&(c.value instanceof Array||(c.value=[c.value]),c.value.forEach(function(b,d){d>0&&(i+=e.sep),"integer"==c.type||"double"==c.type||"boolean"==c.type?b=j.changeType(b,c.type,!0):a||(b=j.escapeString(b)),e.mod&&(b=j.fmt(e.mod,b)),a?i+=a.add(c,b):("string"==typeof b&&(b="'"+b+"'"),i+=b)})),d.push(c.field+" "+e.op.replace(/\?/,i))}}),d.join(" "+c.condition+b)}(c);return a?{sql:g,params:a.run()}:{sql:g}},getRulesFromSQL:function(a,b){"SQLParser"in window||j.error("MissingLibrary","SQLParser is required to parse SQL queries. Get it here https://github.com/mistic100/sql-parser");var c=this;if("string"==typeof a&&(a={sql:a}),b===!0&&(b="question_mark"),"string"==typeof b){var e=d(b);b=this.settings.sqlRuleStatement[e[1]](a.params,e[2])}b&&(a.sql=b.esc(a.sql)),0!==a.sql.toUpperCase().indexOf("SELECT")&&(a.sql="SELECT * FROM table WHERE "+a.sql);var f=SQLParser.parse(a.sql);f.where||j.error("SQLParse","No WHERE clause found");var g={condition:this.settings.default_condition,rules:[]},h=g;return function i(a,d){if(["AND","OR"].indexOf(a.operation.toUpperCase())!==-1){d>0&&h.condition!=a.operation.toUpperCase()&&(h.rules.push({condition:c.settings.default_condition,rules:[]}),h=h.rules[h.rules.length-1]),h.condition=a.operation.toUpperCase(),d++;var e=h;i(a.left,d),h=e,i(a.right,d)}else{void 0!==a.left.value&&void 0!==a.right.value||j.error("SQLParse","Missing field and/or value"),$.isPlainObject(a.right.value)&&j.error("SQLParse","Value format not supported for {0}.",a.left.value);var f;f=$.isArray(a.right.value)?a.right.value.map(function(a){return a.value}):a.right.value,b&&(f=$.isArray(f)?f.map(b.parse):b.parse(f));var g=a.operation.toUpperCase();"<>"==g&&(g="!=");var k;k="NOT LIKE"==g?c.settings.sqlRuleOperator.LIKE:c.settings.sqlRuleOperator[g],void 0===k&&j.error("UndefinedSQLOperator",'Invalid SQL operation "{0}".',a.operation);var l=k.call(this,f,a.operation);"NOT LIKE"==g&&(l.op="not_"+l.op);var m=a.left.values.join(".");h.rules.push({id:c.change("getSQLFieldID",m,f),field:m,operator:l.op,value:l.val})}}(f.where.conditions,0),g},setRulesFromSQL:function(a,b){this.setRules(this.getRulesFromSQL(a,b))}}),e.regional.en={__locale:"English (en)",__author:'Damien "Mistic" Sorel, http://www.strangeplanet.fr',add_rule:"Add rule",add_group:"Add group",delete_rule:"Delete",delete_group:"Delete",conditions:{AND:"AND",OR:"OR"},operators:{equal:"equal",not_equal:"not equal","in":"in",not_in:"not in",less:"less",less_or_equal:"less or equal",greater:"greater",greater_or_equal:"greater or equal",between:"between",not_between:"not between",begins_with:"begins with",not_begins_with:"doesn't begin with",contains:"contains",not_contains:"doesn't contain",ends_with:"ends with",not_ends_with:"doesn't end with",is_empty:"is empty",is_not_empty:"is not empty",is_null:"is null",is_not_null:"is not null"},errors:{no_filter:"No filter selected",empty_group:"The group is empty",radio_empty:"No value selected",checkbox_empty:"No value selected",select_empty:"No value selected",string_empty:"Empty value",string_exceed_min_length:"Must contain at least {0} characters",string_exceed_max_length:"Must not contain more than {0} characters",string_invalid_format:"Invalid format ({0})",number_nan:"Not a number",number_not_integer:"Not an integer",number_not_double:"Not a real number",number_exceed_min:"Must be greater than {0}",number_exceed_max:"Must be lower than {0}",number_wrong_step:"Must be a multiple of {0}",datetime_empty:"Empty value",datetime_invalid:"Invalid date format ({0})",datetime_exceed_min:"Must be after {0}",datetime_exceed_max:"Must be before {0}",boolean_not_valid:"Not a boolean",operator_not_multiple:"Operator {0} cannot accept multiple values"}},e.defaults({lang_code:"en"})}); \ No newline at end of file diff --git a/dist/js/query-builder.standalone.js b/dist/js/query-builder.standalone.js index c34241a0..fb38a177 100644 --- a/dist/js/query-builder.standalone.js +++ b/dist/js/query-builder.standalone.js @@ -1,126 +1,132 @@ /*! - * jQuery.extendext 0.1.1 + * jQuery.extendext 0.1.2 * - * Copyright 2014 Damien "Mistic" Sorel (http://www.strangeplanet.fr) + * Copyright 2014-2016 Damien "Mistic" Sorel (http://www.strangeplanet.fr) * Licensed under MIT (http://opensource.org/licenses/MIT) * * Based on jQuery.extend by jQuery Foundation, Inc. and other contributors */ -(function(root, factory) { +(function (root, factory) { if (typeof define === 'function' && define.amd) { define('jQuery.extendext', ['jquery'], factory); } + else if (typeof module === 'object' && module.exports) { + module.exports = factory(require('jquery')); + } else { factory(root.jQuery); } -}(this, function($) { - "use strict"; - - $.extendext = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false, - arrayMode = 'default'; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // Skip the boolean and the target - target = arguments[ i++ ] || {}; - } +}(this, function ($) { + "use strict"; - // Handle array mode parameter - if ( typeof target === "string" ) { - arrayMode = $([target.toLowerCase(), 'default']).filter(['default','concat','replace','extend'])[0]; + $.extendext = function () { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false, + arrayMode = 'default'; - // Skip the string param - target = arguments[ i++ ] || {}; - } + // Handle a deep copy situation + if (typeof target === "boolean") { + deep = target; - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !$.isFunction(target) ) { - target = {}; - } + // Skip the boolean and the target + target = arguments[i++] || {}; + } - // Extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } + // Handle array mode parameter + if (typeof target === "string") { + arrayMode = target.toLowerCase(); + if (arrayMode !== 'concat' && arrayMode !== 'replace' && arrayMode !== 'extend') { + arrayMode = 'default'; + } - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) !== null ) { - // Special operations for arrays - if ($.isArray(options) && arrayMode !== 'default') { - clone = target && $.isArray(target) ? target : []; + // Skip the string param + target = arguments[i++] || {}; + } - switch (arrayMode) { - case 'concat': - target = clone.concat( $.extend( deep, [], options ) ); - break; + // Handle case when target is a string or something (possible in deep copy) + if (typeof target !== "object" && !$.isFunction(target)) { + target = {}; + } - case 'replace': - target = $.extend( deep, [], options ); - break; + // Extend jQuery itself if only one argument is passed + if (i === length) { + target = this; + i--; + } - case 'extend': - options.forEach(function(e, i) { - if (typeof e === 'object') { - var type = $.isArray(e) ? [] : {}; - clone[i] = $.extendext( deep, arrayMode, clone[i] || type, e ); + for (; i < length; i++) { + // Only deal with non-null/undefined values + if ((options = arguments[i]) !== null) { + // Special operations for arrays + if ($.isArray(options) && arrayMode !== 'default') { + clone = target && $.isArray(target) ? target : []; - } else if (clone.indexOf(e) === -1) { - clone.push(e); - } - }); + switch (arrayMode) { + case 'concat': + target = clone.concat($.extend(deep, [], options)); + break; - target = clone; - break; - } + case 'replace': + target = $.extend(deep, [], options); + break; - } else { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; + case 'extend': + options.forEach(function (e, i) { + if (typeof e === 'object') { + var type = $.isArray(e) ? [] : {}; + clone[i] = $.extendext(deep, arrayMode, clone[i] || type, e); - // Prevent never-ending loop - if ( target === copy ) { - continue; - } + } else if (clone.indexOf(e) === -1) { + clone.push(e); + } + }); + + target = clone; + break; + } + + } else { + // Extend the base object + for (name in options) { + src = target[name]; + copy = options[name]; + + // Prevent never-ending loop + if (target === copy) { + continue; + } - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( $.isPlainObject(copy) || - (copyIsArray = $.isArray(copy)) ) ) { + // Recurse if we're merging plain objects or arrays + if (deep && copy && ( $.isPlainObject(copy) || + (copyIsArray = $.isArray(copy)) )) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && $.isArray(src) ? src : []; + if (copyIsArray) { + copyIsArray = false; + clone = src && $.isArray(src) ? src : []; - } else { - clone = src && $.isPlainObject(src) ? src : {}; - } + } else { + clone = src && $.isPlainObject(src) ? src : {}; + } - // Never move original objects, clone them - target[ name ] = $.extendext( deep, arrayMode, clone, copy ); + // Never move original objects, clone them + target[name] = $.extendext(deep, arrayMode, clone, copy); - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; + // Don't bring in undefined values + } else if (copy !== undefined) { + target[name] = copy; + } + } + } } - } } - } - } - // Return the modified object - return target; - }; + // Return the modified object + return target; + }; })); // doT.js @@ -272,7 +278,7 @@ */ // Languages: en -// Plugins: bt-checkbox, bt-selectpicker, bt-tooltip-errors, change-filters, filter-description, invert, mongodb-support, sortable, sql-support, unique-filter +// Plugins: ogc-filter, sql-support (function(root, factory) { if (typeof define == 'function' && define.amd) { define('query-builder', ['jquery', 'doT', 'jQuery.extendext'], factory); @@ -540,6 +546,8 @@ QueryBuilder.DEFAULTS = { default_group_flags: { condition_readonly: false, + no_add_rule: false, + no_add_group: false, no_delete: false }, @@ -1096,7 +1104,10 @@ QueryBuilder.prototype.addRule = function(parent, data, flags) { this.createRuleFilters(model); if (this.settings.default_filter || !this.settings.display_empty_filter) { - model.filter = this.getFilterById(this.settings.default_filter || this.filters[0].id); + model.filter = this.change('getDefaultFilter', + this.getFilterById(this.settings.default_filter || this.filters[0].id), + model + ); } return model; @@ -1294,6 +1305,12 @@ QueryBuilder.prototype.applyGroupFlags = function(group) { group.$el.find('>' + Selectors.group_condition).prop('disabled', true) .parent().addClass('readonly'); } + if (flags.no_add_rule) { + group.$el.find(Selectors.add_rule).remove(); + } + if (flags.no_add_group) { + group.$el.find(Selectors.add_group).remove(); + } if (flags.no_delete) { group.$el.find(Selectors.delete_group).remove(); } @@ -1604,7 +1621,8 @@ QueryBuilder.prototype.setRules = function(data) { data.rules.forEach(function(item) { var model; - if (item.rules && item.rules.length > 0) { + + 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); @@ -1619,11 +1637,13 @@ QueryBuilder.prototype.setRules = function(data) { } } else { - if (item.id === undefined) { - Utils.error('RulesParse', 'Missing rule field id'); - } - if (item.operator === undefined) { - item.operator = 'equal'; + if (!item.empty) { + if (item.id === undefined) { + Utils.error('RulesParse', 'Missing rule field id'); + } + if (item.operator === undefined) { + item.operator = 'equal'; + } } model = self.addRule(group, item.data); @@ -1631,13 +1651,16 @@ QueryBuilder.prototype.setRules = function(data) { return; } - model.filter = self.getFilterById(item.id); - model.operator = self.getOperatorByType(item.operator); - model.flags = self.parseRuleFlags(item); + if (!item.empty) { + model.filter = self.getFilterById(item.id); + model.operator = self.getOperatorByType(item.operator); - if (model.operator.nb_inputs !== 0 && item.value !== undefined) { - model.value = item.value; + if (model.operator.nb_inputs !== 0 && item.value !== undefined) { + model.value = item.value; + } } + + model.flags = self.parseRuleFlags(item); } }); @@ -2104,6 +2127,8 @@ QueryBuilder.prototype.parseGroupFlags = function(group) { if (group.readonly) { $.extend(flags, { condition_readonly: true, + no_add_rule: true, + no_add_group: true, no_delete: true }); } @@ -2216,8 +2241,13 @@ QueryBuilder.templates.filterSelect = '\ '; QueryBuilder.templates.operatorSelect = '\ +{{? it.operators.length === 1 }} \ + \ +{{= it.lang.operators[it.operators[0].type] || it.operators[0].type }} \ + \ +{{?}} \ {{ var optgroup = null; }} \ - \ {{~ it.operators: operator }} \ {{? optgroup !== operator.optgroup }} \ {{? optgroup !== null }}{{?}} \ @@ -2580,7 +2610,7 @@ Node.prototype.moveAtEnd = function(target) { target = this.parent; } - this._move(target, target.length() - 1); + this._move(target, target.length() === 0 ? 0 : target.length() - 1); return this; }; @@ -3002,608 +3032,74 @@ $.fn.queryBuilder.regional = QueryBuilder.regional; /*! - * jQuery QueryBuilder Awesome Bootstrap Checkbox - * Applies Awesome Bootstrap Checkbox for checkbox and radio inputs. - */ - -QueryBuilder.define('bt-checkbox', function(options) { - if (options.font == 'glyphicons') { - var injectCSS = document.createElement('style'); - injectCSS.innerHTML = '\ -.checkbox input[type=checkbox]:checked + label:after { \ - font-family: "Glyphicons Halflings"; \ - content: "\\e013"; \ -} \ -.checkbox label:after { \ - padding-left: 4px; \ - padding-top: 2px; \ - font-size: 9px; \ -}'; - document.body.appendChild(injectCSS); - } - - this.on('getRuleInput.filter', function(h, rule, name) { - var filter = rule.filter; - - if ((filter.input === 'radio' || filter.input === 'checkbox') && !filter.plugin) { - h.value = ''; - - if (!filter.colors) { - filter.colors = {}; - } - if (filter.color) { - filter.colors._def_ = filter.color; - } - - var style = filter.vertical ? ' style="display:block"' : ''; - var i = 0; - - Utils.iterateOptions(filter.values, function(key, val) { - var color = filter.colors[key] || filter.colors._def_ || options.color; - var id = name + '_' + (i++); - - h.value+= '\ - \ - \ - \ -'; - }); - } - }); -}, { - font: 'glyphicons', - color: 'default' -}); - - -/*! - * jQuery QueryBuilder Bootstrap Selectpicker - * Applies Bootstrap Select on filters and operators combo-boxes. - */ - -/** - * @throws ConfigError - */ -QueryBuilder.define('bt-selectpicker', function(options) { - if (!$.fn.selectpicker || !$.fn.selectpicker.Constructor) { - Utils.error('MissingLibrary', 'Bootstrap Select is required to use "bt-selectpicker" plugin. Get it here: http://silviomoreto.github.io/bootstrap-select'); - } - - // init selectpicker - this.on('afterCreateRuleFilters', function(e, rule) { - rule.$el.find(Selectors.rule_filter).removeClass('form-control').selectpicker(options); - }); - - this.on('afterCreateRuleOperators', function(e, rule) { - rule.$el.find(Selectors.rule_operator).removeClass('form-control').selectpicker(options); - }); - - // update selectpicker on change - this.on('afterUpdateRuleFilter', function(e, rule) { - rule.$el.find(Selectors.rule_filter).selectpicker('render'); - }); - - this.on('afterUpdateRuleOperator', function(e, rule) { - rule.$el.find(Selectors.rule_operator).selectpicker('render'); - }); -}, { - container: 'body', - style: 'btn-inverse btn-xs', - width: 'auto', - showIcon: false -}); - - -/*! - * jQuery QueryBuilder Bootstrap Tooltip errors - * Applies Bootstrap Tooltips on validation error messages. - */ - -/** - * @throws ConfigError - */ -QueryBuilder.define('bt-tooltip-errors', function(options) { - if (!$.fn.tooltip || !$.fn.tooltip.Constructor || !$.fn.tooltip.Constructor.prototype.fixTitle) { - Utils.error('MissingLibrary', 'Bootstrap Tooltip is required to use "bt-tooltip-errors" plugin. Get it here: http://getbootstrap.com'); - } - - var self = this; - - // add BT Tooltip data - this.on('getRuleTemplate.filter getGroupTemplate.filter', function(h) { - var $h = $(h.value); - $h.find(Selectors.error_container).attr('data-toggle', 'tooltip'); - h.value = $h.prop('outerHTML'); - }); - - // init/refresh tooltip when title changes - this.model.on('update', function(e, node, field) { - if (field == 'error' && self.settings.display_errors) { - node.$el.find(Selectors.error_container).eq(0) - .tooltip(options) - .tooltip('hide') - .tooltip('fixTitle'); - } - }); -}, { - placement: 'right' -}); - - -/*! - * jQuery QueryBuilder Change Filters - * Allows to change available filters after plugin initialization. - */ - -QueryBuilder.extend({ - /** - * Change the filters of the builder - * @throws ChangeFilterError - * @param {boolean,optional} delete rules using old filters - * @param {object[]} new filters - */ - setFilters: function(delete_orphans, filters) { - var self = this; - - if (filters === undefined) { - filters = delete_orphans; - delete_orphans = false; - } - - filters = this.checkFilters(filters); - filters = this.change('setFilters', filters); - - var filtersIds = filters.map(function(filter) { - return filter.id; - }); - - // check for orphans - if (!delete_orphans) { - (function checkOrphans(node) { - node.each( - function(rule) { - if (rule.filter && filtersIds.indexOf(rule.filter.id) === -1) { - Utils.error('ChangeFilter', 'A rule is using filter "{0}"', rule.filter.id); - } - }, - checkOrphans - ); - }(this.model.root)); - } - - // replace filters - this.filters = filters; - - // apply on existing DOM - (function updateBuilder(node) { - node.each(true, - function(rule) { - if (rule.filter && filtersIds.indexOf(rule.filter.id) === -1) { - rule.drop(); - } - else { - self.createRuleFilters(rule); - - rule.$el.find(Selectors.rule_filter).val(rule.filter ? rule.filter.id : '-1'); - } - }, - updateBuilder - ); - }(this.model.root)); - - // update plugins - if (this.settings.plugins) { - if (this.settings.plugins['unique-filter']) { - this.updateDisabledFilters(); - } - if (this.settings.plugins['bt-selectpicker']) { - this.$el.find(Selectors.rule_filter).selectpicker('render'); - } - } - - // reset the default_filter if does not exist anymore - if (this.settings.default_filter) { - try { - this.getFilterById(this.settings.default_filter); - } - catch (e) { - this.settings.default_filter = null; - } - } - - this.trigger('afterSetFilters', filters); - }, - - /** - * Adds a new filter to the builder - * @param {object|object[]} the new filter - * @param {mixed,optional} numeric index or '#start' or '#end' - */ - addFilter: function(new_filters, position) { - if (position === undefined || position == '#end') { - position = this.filters.length; - } - else if (position == '#start') { - position = 0; - } - - if (!$.isArray(new_filters)) { - new_filters = [new_filters]; - } - - var filters = $.extend(true, [], this.filters); - - // numeric position - if (parseInt(position) == position) { - Array.prototype.splice.apply(filters, [position, 0].concat(new_filters)); - } - else { - // after filter by its id - if (this.filters.some(function(filter, index) { - if (filter.id == position) { - position = index + 1; - return true; - } - })) { - Array.prototype.splice.apply(filters, [position, 0].concat(new_filters)); - } - // defaults to end of list - else { - Array.prototype.push.apply(filters, new_filters); - } - } - - this.setFilters(filters); - }, - - /** - * Removes a filter from the builder - * @param {string|string[]} the filter id - * @param {boolean,optional} delete rules using old filters - */ - removeFilter: function(filter_ids, delete_orphans) { - var filters = $.extend(true, [], this.filters); - if (typeof filter_ids === 'string') { - filter_ids = [filter_ids]; - } - - filters = filters.filter(function(filter) { - return filter_ids.indexOf(filter.id) === -1; - }); - - this.setFilters(delete_orphans, filters); - } -}); - - -/*! - * jQuery QueryBuilder Filter Description - * Provides three ways to display a description about a filter: inline, Bootsrap Popover or Bootbox. - */ - -/** - * @throws ConfigError - */ -QueryBuilder.define('filter-description', function(options) { - /** - * INLINE - */ - if (options.mode === 'inline') { - this.on('afterUpdateRuleFilter', function(e, rule) { - var $p = rule.$el.find('p.filter-description'); - - if (!rule.filter || !rule.filter.description) { - $p.hide(); - } - else { - if ($p.length === 0) { - $p = $('

'); - $p.appendTo(rule.$el); - } - else { - $p.show(); - } - - $p.html(' ' + rule.filter.description); - } - }); - } - /** - * POPOVER - */ - else if (options.mode === 'popover') { - if (!$.fn.popover || !$.fn.popover.Constructor || !$.fn.popover.Constructor.prototype.fixTitle) { - Utils.error('MissingLibrary', 'Bootstrap Popover is required to use "filter-description" plugin. Get it here: http://getbootstrap.com'); - } - - this.on('afterUpdateRuleFilter', function(e, rule) { - var $b = rule.$el.find('button.filter-description'); - - if (!rule.filter || !rule.filter.description) { - $b.hide(); - - if ($b.data('bs.popover')) { - $b.popover('hide'); - } - } - else { - if ($b.length === 0) { - $b = $(''); - $b.prependTo(rule.$el.find(Selectors.rule_actions)); - - $b.popover({ - placement: 'left', - container: 'body', - html: true - }); - - $b.on('mouseout', function() { - $b.popover('hide'); - }); - } - else { - $b.show(); - } - - $b.data('bs.popover').options.content = rule.filter.description; - - if ($b.attr('aria-describedby')) { - $b.popover('show'); - } - } - }); - } - /** - * BOOTBOX - */ - else if (options.mode === 'bootbox') { - if (!('bootbox' in window)) { - Utils.error('MissingLibrary', 'Bootbox is required to use "filter-description" plugin. Get it here: http://bootboxjs.com'); - } - - this.on('afterUpdateRuleFilter', function(e, rule) { - var $b = rule.$el.find('button.filter-description'); - - if (!rule.filter || !rule.filter.description) { - $b.hide(); - } - else { - if ($b.length === 0) { - $b = $(''); - $b.prependTo(rule.$el.find(Selectors.rule_actions)); - - $b.on('click', function() { - bootbox.alert($b.data('description')); - }); - } - - $b.data('description', rule.filter.description); - } - }); - } -}, { - icon: 'glyphicon glyphicon-info-sign', - mode: 'popover' -}); - - -/*! - * jQuery QueryBuilder Invert - * Allows to invert a rule operator, a group condition or the entire builder. - */ - -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.define('invert', function(options) { - var self = this; - - /** - * Bind events - */ - this.on('afterInit', function() { - self.$el.on('click.queryBuilder', '[data-invert=group]', function() { - var $group = $(this).closest(Selectors.group_container); - self.invert(Model($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(Model($rule), options); - }); - } - }); - - /** - * Modify templates - */ - this.on('getGroupTemplate.filter', function(h, level) { - var $h = $(h.value); - $h.find(Selectors.condition_container).after(''); - 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(''); - h.value = $h.prop('outerHTML'); - }); - } -}, { - icon: 'glyphicon glyphicon-random', - recursive: true, - invert_rules: true, - display_rules_button: false, - silent_fail: false -}); - -QueryBuilder.extend({ - /** - * Invert a Group, a Rule or the whole builder - * @throws InvertConditionError, InvertOperatorError - * @param {Node,optional} - * @param {object,optional} - */ - 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) { - this.trigger('afterInvert', node, options); - } - } -}); - - -/*! - * jQuery QueryBuilder MongoDB Support - * Allows to export rules as a MongoDB find object as well as populating the builder from a MongoDB object. + * jQuery QueryBuilder OGC Filter Support + * Allows to export rules as a SQL WHERE statement as well as populating the builder from an SQL query. */ // DEFAULT CONFIG // =============================== QueryBuilder.defaults({ - mongoOperators: { - equal: function(v) { return v[0]; }, - not_equal: function(v) { return { '$ne': v[0] }; }, - in: function(v) { return { '$in': v }; }, - not_in: function(v) { return { '$nin': v }; }, - less: function(v) { return { '$lt': v[0] }; }, - less_or_equal: function(v) { return { '$lte': v[0] }; }, - greater: function(v) { return { '$gt': v[0] }; }, - greater_or_equal: function(v) { return { '$gte': v[0] }; }, - between: function(v) { return { '$gte': v[0], '$lte': v[1] }; }, - not_between: function(v) { return { '$lt': v[0], '$gt': v[1] }; }, - begins_with: function(v) { return { '$regex': '^' + Utils.escapeRegExp(v[0]) }; }, - not_begins_with: function(v) { return { '$regex': '^(?!' + Utils.escapeRegExp(v[0]) + ')' }; }, - contains: function(v) { return { '$regex': Utils.escapeRegExp(v[0]) }; }, - not_contains: function(v) { return { '$regex': '^((?!' + Utils.escapeRegExp(v[0]) + ').)*$', '$options': 's' }; }, - ends_with: function(v) { return { '$regex': Utils.escapeRegExp(v[0]) + '$' }; }, - not_ends_with: function(v) { return { '$regex': '(? SQL conversion */ + filterOperators: { + equal: { op: 'ogc:PropertyIsEqualTo' }, + not_equal: { op: 'ogc:PropertyIsNotEqualTo' }, + less: { op: 'ogc:PropertyIsLessThan' }, + less_or_equal: { op: 'ogc:PropertyIsLessThanOrEqualTo' }, + greater: { op: 'ogc:PropertyIsGreaterThan' }, + greater_or_equal: { op: 'ogc:PropertyIsLessThanOrEqualTo' }, + between: { op: 'ogc:PropertyIsBetween', sep: 'And' }, + is_null: { op: 'ogc:PropertyIsNull' } + }, - mongoRuleOperators: { - $ne: function(v) { - v = v.$ne; + + /* statements for internal -> SQL conversion */ + filterStatements: { + 'question_mark': function() { + var params = []; return { - 'val': v, - 'op': v === null ? 'is_not_null' : (v === '' ? 'is_not_empty' : 'not_equal') + add: function(rule, value) { + params.push(value); + return '?'; + }, + run: function() { + return params; + } }; }, - eq: function(v) { + + 'numbered': function(char) { + if (!char || char.length > 1) char = '$'; + var index = 0; + var params = []; return { - 'val': v, - 'op': v === null ? 'is_null' : (v === '' ? 'is_empty' : 'equal') + add: function(rule, value) { + params.push(value); + index++; + return char + index; + }, + run: function() { + return params; + } }; }, - $regex: function(v) { - v = v.$regex; - if (v.slice(0, 4) == '^(?!' && v.slice(-1) == ')') { - return { 'val': v.slice(4, -1), 'op': 'not_begins_with' }; - } - else if (v.slice(0, 5) == '^((?!' && v.slice(-5) == ').)*$') { - return { 'val': v.slice(5, -5), 'op': 'not_contains' }; - } - else if (v.slice(0, 4) == '(? 1) char = ':'; + var indexes = {}; + var params = {}; + return { + add: function(rule, value) { + if (!indexes[rule.field]) indexes[rule.field] = 1; + var key = rule.field + '_' + (indexes[rule.field]++); + params[key] = value; + return char + key; + }, + run: function() { + return params; + } + }; + } } }); @@ -3612,349 +3108,96 @@ QueryBuilder.defaults({ // =============================== QueryBuilder.extend({ /** - * Get rules as MongoDB query - * @throws UndefinedMongoConditionError, UndefinedMongoOperatorError + * Get rules as Filter query + * @throws UndefinedFilterConditionError, UndefinedFilterOperatorError * @param data {object} (optional) rules * @return {object} */ - getMongo: function(data) { + getFilter: function(data) { data = (data === undefined) ? this.getRules() : data; + var nl = '\n'; var self = this; - return (function parse(data) { + var filterXML = (function parse(data) { + var parts = []; + if (!data.condition) { data.condition = self.settings.default_condition; } if (['AND', 'OR'].indexOf(data.condition.toUpperCase()) === -1) { - Utils.error('UndefinedMongoCondition', 'Unable to build MongoDB query with condition "{0}"', data.condition); + Utils.error('UndefinedSQLCondition', 'Unable to build SQL query with condition "{0}"', data.condition); } if (!data.rules) { - return {}; + return ''; } - var parts = []; - - data.rules.forEach(function(rule) { - if (rule.rules && rule.rules.length > 0) { - parts.push(parse(rule)); - } - else { - var mdb = self.settings.mongoOperators[rule.operator]; - var ope = self.getOperatorByType(rule.operator); - var values = []; - - if (mdb === undefined) { - Utils.error('UndefinedMongoOperator', 'Unknown MongoDB operation for operator "{0}"', rule.operator); - } - - if (ope.nb_inputs !== 0) { - if (!(rule.value instanceof Array)) { - rule.value = [rule.value]; - } - - rule.value.forEach(function(v) { - values.push(Utils.changeType(v, rule.type, false)); - }); - } - - var part = {}; - part[rule.field] = mdb.call(self, values); - parts.push(part); - } - }); - - var res = {}; - if (parts.length > 0) { - res['$' + data.condition.toLowerCase()] = parts; + parts.push(''); + if (data.condition.toUpperCase() === 'OR') { + parts.push(''); + } else { + parts.push(''); } - return res; - }(data)); - }, - - /** - * Convert MongoDB object to rules - * @throws MongoParseError, UndefinedMongoConditionError, UndefinedMongoOperatorError - * @param data {object} query object - * @return {object} - */ - getRulesFromMongo: function(data) { - if (data === undefined || data === null) { - return null; - } - - var self = this; - var conditions = { - '$and': 'AND', - '$or': 'OR' - }; - - return (function parse(data) { - var topKeys = Object.keys(data); - if (topKeys.length > 1) { - Utils.error('MongoParse', 'Invalid MongoDB query format'); - } - if (!conditions[topKeys[0].toLowerCase()]) { - Utils.error('UndefinedMongoCondition', 'Unable to build MongoDB query with condition "{0}"', topKeys[0]); - } - var rules = data[topKeys[0]]; - var parts = []; + data.rules.forEach(function(rule) { - rules.forEach(function(rule) { - var keys = Object.keys(rule); + var filterCmd = self.settings.filterOperators[rule.operator]; + var value = ''; - if (conditions[keys[0].toLowerCase()]) { - parts.push(parse(rule)); + if (filterCmd === undefined) { + Utils.error('UndefinedFilterOperator', 'Unknown Filter operation for operator "{0}"', rule.operator); } - else { - var field = keys[0]; - var value = rule[field]; - var operator = determineMongoOperator(value, field); - if (operator === undefined) { - Utils.error('MongoParse', 'Invalid MongoDB query format'); +/* rule.value.forEach(function(v, i) { + if (i > 0) { + value += sql.sep; } - var mdbrl = self.settings.mongoRuleOperators[operator]; - if (mdbrl === undefined) { - Utils.error('UndefinedMongoOperator', 'JSON Rule operation unknown for operator "{0}"', operator); + if (rule.type == 'integer' || rule.type == 'double' || rule.type == 'boolean') { + v = Utils.changeType(v, rule.type, true); + } + else if (!stmt) { + v = Utils.escapeString(v); } - var opVal = mdbrl.call(self, value); - parts.push({ - id: self.change('getMongoDBFieldID', field, value), - field: field, - operator: opVal.op, - value: opVal.val - }); - } - }); - - var res = {}; - if (parts.length > 0) { - res.condition = conditions[topKeys[0].toLowerCase()]; - res.rules = parts; - } - return res; - }(data)); - }, - - /** - * Set rules from MongoDB object - * @param data {object} - */ - setRulesFromMongo: function(data) { - this.setRules(this.getRulesFromMongo(data)); - } -}); - -/** - * Find which operator is used in a MongoDB sub-object - * @param {mixed} value - * @param {string} field - * @return {string|undefined} - */ -function determineMongoOperator(value, field) { - if (value !== null && typeof value == 'object') { - var subkeys = Object.keys(value); - - if (subkeys.length === 1) { - return subkeys[0]; - } - else { - if (value.$gte !== undefined && value.$lte !== undefined) { - return 'between'; - } - if (value.$lt !== undefined && value.$gt !== undefined) { - return 'not_between'; - } - else if (value.$regex !== undefined) { // optional $options - return '$regex'; - } - else { - return; - } - } - } - else { - return 'eq'; - } -} - - -/*! - * jQuery QueryBuilder Sortable - * Enables drag & drop sort of rules. - */ - -Selectors.rule_and_group_containers = Selectors.rule_container + ', ' + Selectors.group_container; - -QueryBuilder.define('sortable', function(options) { - /** - * Init HTML5 drag and drop - */ - this.on('afterInit', function(e) { - // configure jQuery to use dataTransfer - $.event.props.push('dataTransfer'); - - var placeholder; - var src; - var self = e.builder; - - // only add "draggable" attribute when hovering drag handle - // preventing text select bug in Firefox - self.$el.on('mouseover.queryBuilder', '.drag-handle', function() { - self.$el.find(Selectors.rule_and_group_containers).attr('draggable', true); - }); - self.$el.on('mouseout.queryBuilder', '.drag-handle', function() { - self.$el.find(Selectors.rule_and_group_containers).removeAttr('draggable'); - }); - - // dragstart: create placeholder and hide current element - self.$el.on('dragstart.queryBuilder', '[draggable]', function(e) { - e.stopPropagation(); - - // notify drag and drop (only dummy text) - e.dataTransfer.setData('text', 'drag'); - - src = Model(e.target); - - // Chrome glitchs - // - helper invisible if hidden immediately - // - "dragend" is called immediately if we modify the DOM directly - setTimeout(function() { - var ph = $('
 
'); - ph.css('min-height', src.$el.height()); + else { + if (typeof v == 'string') { + v = '\'' + v + '\''; + } - placeholder = src.parent.addRule(ph, src.getPos()); + value += v; + } + });*/ - src.$el.hide(); - }, 0); - }); + parts.push('<' + filterCmd.op + '>' + nl); + parts.push('' + rule.field + '' + nl); + parts.push('' + rule.value + '' + nl); + parts.push('' + nl); - // dragenter: move the placeholder - self.$el.on('dragenter.queryBuilder', '[draggable]', function(e) { - e.preventDefault(); - e.stopPropagation(); + }); - if (placeholder) { - moveSortableToTarget(placeholder, $(e.target)); + if (data.condition.toUpperCase() === 'OR') { + parts.push('
'); + } else { + parts.push(''); } - }); - - // dragover: prevent glitches - self.$el.on('dragover.queryBuilder', '[draggable]', function(e) { - e.preventDefault(); - e.stopPropagation(); - }); - - // drop: move current element - self.$el.on('drop.queryBuilder', function(e) { - e.preventDefault(); - e.stopPropagation(); - - moveSortableToTarget(src, $(e.target)); - }); - - // dragend: show current element and delete placeholder - self.$el.on('dragend.queryBuilder', '[draggable]', function(e) { - e.preventDefault(); - e.stopPropagation(); - - src.$el.show(); - placeholder.drop(); - - self.$el.find(Selectors.rule_and_group_containers).removeAttr('draggable'); - - self.trigger('afterMove', src); - - src = placeholder = null; - }); - }); - - /** - * 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 - */ - 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(); - } - }); - - /** - * Modify templates - */ - this.on('getGroupTemplate.filter', function(h, level) { - if (level > 1) { - var $h = $(h.value); - $h.find(Selectors.condition_container).after('
'); - h.value = $h.prop('outerHTML'); - } - }); - - this.on('getRuleTemplate.filter', function(h) { - var $h = $(h.value); - $h.find(Selectors.rule_header).after('
'); - h.value = $h.prop('outerHTML'); - }); -}, { - default_no_sortable: false, - icon: 'glyphicon glyphicon-sort' -}); - -/** - * Move an element (placeholder or actual object) depending on active target - * @param {Node} - * @param {jQuery} - */ -function moveSortableToTarget(element, target) { - var parent; + parts.push('
') - // on rule - parent = target.closest(Selectors.rule_container); - if (parent.length) { - element.moveAfter(Model(parent)); - return; - } + return parts.join(' '); + }(data)); - // on group header - parent = target.closest(Selectors.group_header); - if (parent.length) { - parent = target.closest(Selectors.group_container); - element.moveAtBegin(Model(parent)); - return; + return { + filter: filterXML + }; } +}); - // on group - parent = target.closest(Selectors.group_container); - if (parent.length) { - element.moveAtEnd(Model(parent)); - return; - } +function getStmtConfig(stmt) { + var config = stmt.match(/(question_mark|numbered|named)(?:\((.)\))?/); + if (!config) config = [null, 'question_mark', undefined]; + return config; } @@ -4394,86 +3637,6 @@ function getStmtConfig(stmt) { } -/*! - * jQuery QueryBuilder Unique Filter - * Allows to define some filters as "unique": ie which can be used for only one rule, globally or in the same group. - */ - -QueryBuilder.define('unique-filter', function() { - this.status.used_filters = {}; - - this.on('afterUpdateRuleFilter', this.updateDisabledFilters); - this.on('afterDeleteRule', this.updateDisabledFilters); - this.on('afterCreateRuleFilters', this.applyDisabledFilters); - this.on('afterReset', this.clearDisabledFilters); - this.on('afterClear', this.clearDisabledFilters); -}); - -QueryBuilder.extend({ - updateDisabledFilters: function(e) { - var self = e ? e.builder : this; - - self.status.used_filters = {}; - - if (!self.model) { - return; - } - - // get used filters - (function walk(group) { - group.each(function(rule) { - if (rule.filter && rule.filter.unique) { - if (!self.status.used_filters[rule.filter.id]) { - self.status.used_filters[rule.filter.id] = []; - } - if (rule.filter.unique == 'group') { - self.status.used_filters[rule.filter.id].push(rule.parent); - } - } - }, function(group) { - walk(group); - }); - }(self.model.root)); - - self.applyDisabledFilters(e); - }, - - clearDisabledFilters: function(e) { - var self = e ? e.builder : this; - - self.status.used_filters = {}; - - self.applyDisabledFilters(e); - }, - - applyDisabledFilters: function(e) { - var self = e ? e.builder : this; - - // re-enable everything - self.$el.find(Selectors.filter_container + ' option').prop('disabled', false); - - // disable some - $.each(self.status.used_filters, function(filterId, groups) { - if (groups.length === 0) { - self.$el.find(Selectors.filter_container + ' option[value="' + filterId + '"]:not(:selected)').prop('disabled', true); - } - else { - groups.forEach(function(group) { - group.each(function(rule) { - rule.$el.find(Selectors.filter_container + ' option[value="' + filterId + '"]:not(:selected)').prop('disabled', true); - }); - }); - } - }); - - // update Selectpicker - if (self.settings.plugins && self.settings.plugins['bt-selectpicker']) { - self.$el.find(Selectors.rule_filter).selectpicker('render'); - } - } -}); - - /*! * jQuery QueryBuilder 2.3.3 * Locale: English (en) @@ -4536,8 +3699,7 @@ QueryBuilder.regional['en'] = { "datetime_exceed_max": "Must be before {0}", "boolean_not_valid": "Not a boolean", "operator_not_multiple": "Operator {0} cannot accept multiple values" - }, - "invert": "Invert" + } }; QueryBuilder.defaults({ lang_code: 'en' }); diff --git a/dist/js/query-builder.standalone.min.js b/dist/js/query-builder.standalone.min.js index 8e522d5b..962da45c 100644 --- a/dist/js/query-builder.standalone.min.js +++ b/dist/js/query-builder.standalone.min.js @@ -4,5 +4,5 @@ * Licensed under MIT (http://opensource.org/licenses/MIT) */ -!function(a,b){"function"==typeof define&&define.amd?define("jQuery.extendext",["jquery"],b):b(a.jQuery)}(this,function($){"use strict";$.extendext=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1,k="default";for("boolean"==typeof g&&(j=g,g=arguments[h++]||{}),"string"==typeof g&&(k=$([g.toLowerCase(),"default"]).filter(["default","concat","replace","extend"])[0],g=arguments[h++]||{}),"object"==typeof g||$.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!==(a=arguments[h]))if($.isArray(a)&&"default"!==k)switch(f=g&&$.isArray(g)?g:[],k){case"concat":g=f.concat($.extend(j,[],a));break;case"replace":g=$.extend(j,[],a);break;case"extend":a.forEach(function(a,b){if("object"==typeof a){var c=$.isArray(a)?[]:{};f[b]=$.extendext(j,k,f[b]||c,a)}else-1===f.indexOf(a)&&f.push(a)}),g=f}else for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&($.isPlainObject(d)||(e=$.isArray(d)))?(e?(e=!1,f=c&&$.isArray(c)?c:[]):f=c&&$.isPlainObject(c)?c:{},g[b]=$.extendext(j,k,f,d)):void 0!==d&&(g[b]=d));return g}}),function(){"use strict";function a(b,c,d){return("string"==typeof c?c:c.toString()).replace(b.define||f,function(a,c,e,f){return 0===c.indexOf("def.")&&(c=c.substring(4)),c in d||(":"===e?(b.defineParams&&f.replace(b.defineParams,function(a,b,e){d[c]={arg:b,text:e}}),c in d||(d[c]=f)):new Function("def","def['"+c+"']="+f)(d)),""}).replace(b.use||f,function(c,e){b.useParams&&(e=e.replace(b.useParams,function(a,b,c,e){if(d[c]&&d[c].arg&&e){var f=(c+":"+e).replace(/'|\\/g,"_");return d.__exp=d.__exp||{},d.__exp[f]=d[c].text.replace(new RegExp("(^|[^\\w$])"+d[c].arg+"([^\\w$])","g"),"$1"+e+"$2"),b+"def.__exp['"+f+"']"}}));var f=new Function("def","return "+e)(d);return f?a(b,f,d):f})}function b(a){return a.replace(/\\('|\\)/g,"$1").replace(/[\r\t\n]/g," ")}var c,d={version:"1.0.3",templateSettings:{evaluate:/\{\{([\s\S]+?(\}?)+)\}\}/g,interpolate:/\{\{=([\s\S]+?)\}\}/g,encode:/\{\{!([\s\S]+?)\}\}/g,use:/\{\{#([\s\S]+?)\}\}/g,useParams:/(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,define:/\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,defineParams:/^\s*([\w$]+):([\s\S]+)/,conditional:/\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,iterate:/\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,varname:"it",strip:!0,append:!0,selfcontained:!1,doNotSkipEncoded:!1},template:void 0,compile:void 0};d.encodeHTMLSource=function(a){var b={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},c=a?/[&<>"'\/]/g:/&(?!#?\w+;)|<|>|"|'|\//g;return function(a){return a?a.toString().replace(c,function(a){return b[a]||a}):""}},c=function(){return this||(0,eval)("this")}(),"undefined"!=typeof module&&module.exports?module.exports=d:"function"==typeof define&&define.amd?define("doT",function(){return d}):c.doT=d;var e={append:{start:"'+(",end:")+'",startencode:"'+encodeHTML("},split:{start:"';out+=(",end:");out+='",startencode:"';out+=encodeHTML("}},f=/$^/;d.template=function(g,h,i){h=h||d.templateSettings;var j,k,l=h.append?e.append:e.split,m=0,n=h.use||h.define?a(h,g,i||{}):g;n=("var out='"+(h.strip?n.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ").replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""):n).replace(/'|\\/g,"\\$&").replace(h.interpolate||f,function(a,c){return l.start+b(c)+l.end}).replace(h.encode||f,function(a,c){return j=!0,l.startencode+b(c)+l.end}).replace(h.conditional||f,function(a,c,d){return c?d?"';}else if("+b(d)+"){out+='":"';}else{out+='":d?"';if("+b(d)+"){out+='":"';}out+='"}).replace(h.iterate||f,function(a,c,d,e){return c?(m+=1,k=e||"i"+m,c=b(c),"';var arr"+m+"="+c+";if(arr"+m+"){var "+d+","+k+"=-1,l"+m+"=arr"+m+".length-1;while("+k+".rules-list",group_condition:".rules-group-header [name$=_cond]",rule_filter:".rule-filter-container [name$=_filter]",rule_operator:".rule-operator-container [name$=_operator]",rule_value:".rule-value-container [name*=_value_]",add_rule:"[data-add=rule]",delete_rule:"[data-delete=rule]",add_group:"[data-add=group]",delete_group:"[data-delete=group]"};g.templates={},g.regional={},g.OPERATORS={equal:{type:"equal",nb_inputs:1,multiple:!1,apply_to:["string","number","datetime","boolean"]},not_equal:{type:"not_equal",nb_inputs:1,multiple:!1,apply_to:["string","number","datetime","boolean"]},"in":{type:"in",nb_inputs:1,multiple:!0,apply_to:["string","number","datetime"]},not_in:{type:"not_in",nb_inputs:1,multiple:!0,apply_to:["string","number","datetime"]},less:{type:"less",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},less_or_equal:{type:"less_or_equal",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},greater:{type:"greater",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},greater_or_equal:{type:"greater_or_equal",nb_inputs:1,multiple:!1,apply_to:["number","datetime"]},between:{type:"between",nb_inputs:2,multiple:!1,apply_to:["number","datetime"]},not_between:{type:"not_between",nb_inputs:2,multiple:!1,apply_to:["number","datetime"]},begins_with:{type:"begins_with",nb_inputs:1,multiple:!1,apply_to:["string"]},not_begins_with:{type:"not_begins_with",nb_inputs:1,multiple:!1,apply_to:["string"]},contains:{type:"contains",nb_inputs:1,multiple:!1,apply_to:["string"]},not_contains:{type:"not_contains",nb_inputs:1,multiple:!1,apply_to:["string"]},ends_with:{type:"ends_with",nb_inputs:1,multiple:!1,apply_to:["string"]},not_ends_with:{type:"not_ends_with",nb_inputs:1,multiple:!1,apply_to:["string"]},is_empty:{type:"is_empty",nb_inputs:0,multiple:!1,apply_to:["string"]},is_not_empty:{type:"is_not_empty",nb_inputs:0,multiple:!1,apply_to:["string"]},is_null:{type:"is_null",nb_inputs:0,multiple:!1,apply_to:["string","number","datetime","boolean"]},is_not_null:{type:"is_not_null",nb_inputs:0,multiple:!1,apply_to:["string","number","datetime","boolean"]}},g.DEFAULTS={filters:[],plugins:[],sort_filters:!1,display_errors:!0,allow_groups:-1,allow_empty:!1,conditions:["AND","OR"],default_condition:"AND",inputs_separator:" , ",select_placeholder:"------",display_empty_filter:!0,default_filter:null,optgroups:{},default_rule_flags:{filter_readonly:!1,operator_readonly:!1,value_readonly:!1,no_delete:!1},default_group_flags:{condition_readonly:!1,no_delete:!1},templates:{group:null,rule:null,filterSelect:null,operatorSelect:null},lang_code:"en",lang:{},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"],icons:{add_group:"glyphicon glyphicon-plus-sign",add_rule:"glyphicon glyphicon-plus",remove_group:"glyphicon glyphicon-remove",remove_rule:"glyphicon glyphicon-remove",error:"glyphicon glyphicon-warning-sign"}},g.prototype.init=function(c,d){c[0].queryBuilder=this,this.$el=c,this.settings=$.extendext(!0,"replace",{},g.DEFAULTS,d),this.model=new b,this.status={group_id:0,rule_id:0,generated_id:!1,has_optgroup:!1,has_operator_oprgroup:!1,id:null,updating_value:!1},this.settings.allow_groups===!1?this.settings.allow_groups=0:this.settings.allow_groups===!0&&(this.settings.allow_groups=-1),this.filters=this.settings.filters,this.icons=this.settings.icons,this.operators=this.settings.operators,this.templates=this.settings.templates,this.plugins=this.settings.plugins,void 0===g.regional.en&&l.error("Config",'"i18n/en.js" not loaded.'),this.lang=$.extendext(!0,"replace",{},g.regional.en,g.regional[this.settings.lang_code],this.settings.lang),Object.keys(this.templates).forEach(function(b){this.templates[b]||(this.templates[b]=g.templates[b]),"string"==typeof this.templates[b]&&(this.templates[b]=a.template(this.templates[b]))},this),this.$el.attr("id")||(this.$el.attr("id","qb_"+Math.floor(99999*Math.random())),this.status.generated_id=!0),this.status.id=this.$el.attr("id"),this.$el.addClass("query-builder form-inline"),this.filters=this.checkFilters(this.filters),this.operators=this.checkOperators(this.operators),this.bindEvents(),this.initPlugins(),this.trigger("afterInit"),d.rules?(this.setRules(d.rules),delete this.settings.rules):this.setRoot(!0)},g.prototype.checkFilters=function(a){var b=[];if(a&&0!==a.length||l.error("Config","Missing filters list"),a.forEach(function(a,c){switch(a.id||l.error("Config","Missing filter {0} id",c),-1!=b.indexOf(a.id)&&l.error("Config",'Filter "{0}" already defined',a.id),b.push(a.id),a.type?g.types[a.type]||l.error("Config",'Invalid type "{0}"',a.type):a.type="string",a.input?"function"!=typeof a.input&&-1==g.inputs.indexOf(a.input)&&l.error("Config",'Invalid input "{0}"',a.input):a.input="text",a.operators&&a.operators.forEach(function(a){"string"!=typeof a&&l.error("Config","Filter operators must be global operators types (string)")}),a.field||(a.field=a.id),a.label||(a.label=a.field),a.optgroup?(this.status.has_optgroup=!0,this.settings.optgroups[a.optgroup]||(this.settings.optgroups[a.optgroup]=a.optgroup)):a.optgroup=null,a.input){case"radio":case"checkbox":(!a.values||a.values.length<1)&&l.error("Config",'Missing filter "{0}" values',a.id);break;case"select":a.placeholder&&(void 0===a.placeholder_value&&(a.placeholder_value=-1),l.iterateOptions(a.values,function(b){b==a.placeholder_value&&l.error("Config",'Placeholder of filter "{0}" overlaps with one of its values',a.id)}))}},this),this.settings.sort_filters)if("function"==typeof this.settings.sort_filters)a.sort(this.settings.sort_filters);else{var c=this;a.sort(function(a,b){return c.translateLabel(a.label).localeCompare(c.translateLabel(b.label))})}return this.status.has_optgroup&&(a=l.groupSort(a,"optgroup")),a},g.prototype.checkOperators=function(a){var b=[];return a.forEach(function(c,d){"string"==typeof c?(g.OPERATORS[c]||l.error("Config",'Unknown operator "{0}"',c),a[d]=c=$.extendext(!0,"replace",{},g.OPERATORS[c])):(c.type||l.error("Config",'Missing "type" for operator {0}',d),g.OPERATORS[c.type]&&(a[d]=c=$.extendext(!0,"replace",{},g.OPERATORS[c.type],c)),void 0!==c.nb_inputs&&void 0!==c.apply_to||l.error("Config",'Missing "nb_inputs" and/or "apply_to" for operator "{0}"',c.type)),-1!=b.indexOf(c.type)&&l.error("Config",'Operator "{0}" already defined',c.type),b.push(c.type),c.optgroup?(this.status.has_operator_optgroup=!0,this.settings.optgroups[c.optgroup]||(this.settings.optgroups[c.optgroup]=c.optgroup)):c.optgroup=null},this),this.status.has_operator_optgroup&&(a=l.groupSort(a,"optgroup")),a},g.prototype.bindEvents=function(){var a=this;this.$el.on("change.queryBuilder",h.group_condition,function(){if($(this).is(":checked")){var a=$(this).closest(h.group_container);b(a).condition=$(this).val()}}),this.$el.on("change.queryBuilder",h.rule_filter,function(){var c=$(this).closest(h.rule_container);b(c).filter=a.getFilterById($(this).val())}),this.$el.on("change.queryBuilder",h.rule_operator,function(){var c=$(this).closest(h.rule_container);b(c).operator=a.getOperatorByType($(this).val())}),this.$el.on("click.queryBuilder",h.add_rule,function(){var c=$(this).closest(h.group_container);a.addRule(b(c))}),this.$el.on("click.queryBuilder",h.delete_rule,function(){var c=$(this).closest(h.rule_container);a.deleteRule(b(c))}),0!==this.settings.allow_groups&&(this.$el.on("click.queryBuilder",h.add_group,function(){var c=$(this).closest(h.group_container);a.addGroup(b(c))}),this.$el.on("click.queryBuilder",h.delete_group,function(){var c=$(this).closest(h.group_container);a.deleteGroup(b(c))})),this.model.on({drop:function(b,c){c.$el.remove(),a.refreshGroupsConditions()},add:function(b,c,d){0===d?c.$el.prependTo(c.parent.$el.find(">"+h.rules_list)):c.$el.insertAfter(c.parent.rules[d-1].$el),a.refreshGroupsConditions()},move:function(b,c,d,e){c.$el.detach(),0===e?c.$el.prependTo(d.$el.find(">"+h.rules_list)):c.$el.insertAfter(d.rules[e-1].$el),a.refreshGroupsConditions()},update:function(b,c,d,e,f){if(c instanceof k)switch(d){case"error":a.displayError(c);break;case"flags":a.applyRuleFlags(c);break;case"filter":a.updateRuleFilter(c);break;case"operator":a.updateRuleOperator(c,f);break;case"value":a.updateRuleValue(c)}else switch(d){case"error":a.displayError(c);break;case"flags":a.applyGroupFlags(c);break;case"condition":a.updateGroupCondition(c)}}})},g.prototype.setRoot=function(a,b,c){a=void 0===a||a===!0;var d=this.nextGroupId(),e=$(this.getGroupTemplate(d,1));return this.$el.append(e),this.model.root=new j(null,e),this.model.root.model=this.model,this.model.root.data=b,this.model.root.flags=$.extend({},this.settings.default_group_flags,c),this.trigger("afterAddGroup",this.model.root),this.model.root.condition=this.settings.default_condition,a&&this.addRule(this.model.root),this.model.root},g.prototype.addGroup=function(a,b,c,d){b=void 0===b||b===!0;var e=a.level+1,f=this.trigger("beforeAddGroup",a,b,e);if(f.isDefaultPrevented())return null;var g=this.nextGroupId(),h=$(this.getGroupTemplate(g,e)),i=a.addGroup(h);return i.data=c,i.flags=$.extend({},this.settings.default_group_flags,d),this.trigger("afterAddGroup",i),i.condition=this.settings.default_condition,b&&this.addRule(i),i},g.prototype.deleteGroup=function(a){if(a.isRoot())return!1;var b=this.trigger("beforeDeleteGroup",a);if(b.isDefaultPrevented())return!1;var c=!0;return a.each("reverse",function(a){c&=this.deleteRule(a)},function(a){c&=this.deleteGroup(a)},this),c&&(a.drop(),this.trigger("afterDeleteGroup")),c},g.prototype.updateGroupCondition=function(a){a.$el.find(">"+h.group_condition).each(function(){var b=$(this);b.prop("checked",b.val()===a.condition),b.parent().toggleClass("active",b.val()===a.condition)}),this.trigger("afterUpdateGroupCondition",a)},g.prototype.refreshGroupsConditions=function(){!function a(b){(!b.flags||b.flags&&!b.flags.condition_readonly)&&b.$el.find(">"+h.group_condition).prop("disabled",b.rules.length<=1).parent().toggleClass("disabled",b.rules.length<=1),b.each(function(a){},function(b){a(b)},this)}(this.model.root)},g.prototype.addRule=function(a,b,c){var d=this.trigger("beforeAddRule",a);if(d.isDefaultPrevented())return null;var e=this.nextRuleId(),f=$(this.getRuleTemplate(e)),g=a.addRule(f);return void 0!==b&&(g.data=b),g.flags=$.extend({},this.settings.default_rule_flags,c),this.trigger("afterAddRule",g),this.createRuleFilters(g),!this.settings.default_filter&&this.settings.display_empty_filter||(g.filter=this.getFilterById(this.settings.default_filter||this.filters[0].id)),g},g.prototype.deleteRule=function(a){if(a.flags.no_delete)return!1;var b=this.trigger("beforeDeleteRule",a);return b.isDefaultPrevented()?!1:(a.drop(),this.trigger("afterDeleteRule"),!0)},g.prototype.createRuleFilters=function(a){var b=this.change("getRuleFilters",this.filters,a),c=$(this.getRuleFilterSelect(a,b));a.$el.find(h.filter_container).html(c),this.trigger("afterCreateRuleFilters",a)},g.prototype.createRuleOperators=function(a){var b=a.$el.find(h.operator_container).empty();if(a.filter){var c=this.getOperators(a.filter),d=$(this.getRuleOperatorSelect(a,c));b.html(d),a.__.operator=c[0],this.trigger("afterCreateRuleOperators",a,c)}},g.prototype.createRuleInput=function(a){var b=a.$el.find(h.value_container).empty();if(a.__.value=void 0,a.filter&&a.operator&&0!==a.operator.nb_inputs){for(var c=this,d=$(),e=a.filter,f=0;f0&&b.append(this.settings.inputs_separator),b.append(g),d=d.add(g)}b.show(),d.on("change "+(e.input_event||""),function(){c.status.updating_value=!0,a.value=c.getRuleValue(a),c.status.updating_value=!1}),e.plugin&&d[e.plugin](e.plugin_config||{}),this.trigger("afterCreateRuleInput",a),void 0!==e.default_value?a.value=e.default_value:(c.status.updating_value=!0,a.value=c.getRuleValue(a),c.status.updating_value=!1)}},g.prototype.updateRuleFilter=function(a){this.createRuleOperators(a),this.createRuleInput(a),a.$el.find(h.rule_filter).val(a.filter?a.filter.id:"-1"),this.trigger("afterUpdateRuleFilter",a)},g.prototype.updateRuleOperator=function(a,b){var c=a.$el.find(h.value_container);a.operator&&0!==a.operator.nb_inputs?(c.show(),(c.is(":empty")||a.operator.nb_inputs!==b.nb_inputs)&&this.createRuleInput(a)):(c.hide(),a.__.value=void 0),a.operator&&a.$el.find(h.rule_operator).val(a.operator.type),this.trigger("afterUpdateRuleOperator",a)},g.prototype.updateRuleValue=function(a){this.status.updating_value||this.setRuleValue(a,a.value),this.trigger("afterUpdateRuleValue",a)},g.prototype.applyRuleFlags=function(a){var b=a.flags;b.filter_readonly&&a.$el.find(h.rule_filter).prop("disabled",!0),b.operator_readonly&&a.$el.find(h.rule_operator).prop("disabled",!0),b.value_readonly&&a.$el.find(h.rule_value).prop("disabled",!0),b.no_delete&&a.$el.find(h.delete_rule).remove(),this.trigger("afterApplyRuleFlags",a)},g.prototype.applyGroupFlags=function(a){var b=a.flags;b.condition_readonly&&a.$el.find(">"+h.group_condition).prop("disabled",!0).parent().addClass("readonly"),b.no_delete&&a.$el.find(h.delete_group).remove(),this.trigger("afterApplyGroupFlags",a)},g.prototype.clearErrors=function(a){a=a||this.model.root,a&&(a.error=null,a instanceof j&&a.each(function(a){a.error=null},function(a){this.clearErrors(a)},this))},g.prototype.displayError=function(a){if(this.settings.display_errors)if(null===a.error)a.$el.removeClass("has-error");else{var b=$.extend([],a.error,[this.lang.errors[a.error[0]]||a.error[0]]);a.$el.addClass("has-error").find(h.error_container).eq(0).attr("title",l.fmt.apply(null,b))}},g.prototype.triggerValidationError=function(a,b,c){$.isArray(b)||(b=[b]);var d=this.trigger("validationError",a,b,c);d.isDefaultPrevented()||(a.error=b)},g.prototype.destroy=function(){this.trigger("beforeDestroy"),this.status.generated_id&&this.$el.removeAttr("id"),this.clear(),this.model=null,this.$el.off(".queryBuilder").removeClass("query-builder").removeData("queryBuilder"),delete this.$el[0].queryBuilder},g.prototype.reset=function(){this.status.group_id=1,this.status.rule_id=0,this.model.root.empty(),this.addRule(this.model.root),this.trigger("afterReset")},g.prototype.clear=function(){this.status.group_id=0,this.status.rule_id=0,this.model.root&&(this.model.root.drop(),this.model.root=null),this.trigger("afterClear")},g.prototype.setOptions=function(a){$.makeArray($(Object.keys(a)).filter(g.modifiable_options)).forEach(function(b){this.settings[b]=a[b]},this)},g.prototype.getModel=function(a){return a?b(a):this.model.root},g.prototype.validate=function(){this.clearErrors();var a=this,b=function c(b){var d=0,e=0;return b.each(function(b){if(!b.filter)return a.triggerValidationError(b,"no_filter",null),void e++;if(0!==b.operator.nb_inputs){var c=a.validateValue(b,b.value);if(c!==!0)return a.triggerValidationError(b,c,b.value),void e++}d++},function(a){c(a)?d++:e++}),e>0?!1:0!==d||a.settings.allow_empty&&b.isRoot()?!0:(a.triggerValidationError(b,"empty_group",null),!1)}(this.model.root);return this.change("validate",b)},g.prototype.getRules=function(a){if(a=$.extend({get_flags:!1},a),!this.validate())return{};var b=this,c=function d(c){var e={condition:c.condition,rules:[]};if(c.data&&(e.data=$.extendext(!0,"replace",{},c.data)),a.get_flags){var f=b.getGroupFlags(c.flags,"all"===a.get_flags);$.isEmptyObject(f)||(e.flags=f)}return c.each(function(c){var d=null;0!==c.operator.nb_inputs&&(d=c.value);var f={id:c.filter.id,field:c.filter.field,type:c.filter.type,input:c.filter.input,operator:c.operator.type,value:d};if((c.filter.data||c.data)&&(f.data=$.extendext(!0,"replace",{},c.filter.data,c.data)),a.get_flags){var g=b.getRuleFlags(c.flags,"all"===a.get_flags);$.isEmptyObject(g)||(f.flags=g)}e.rules.push(f)},function(a){e.rules.push(d(a))}),e}(this.model.root);return this.change("getRules",c)},g.prototype.setRules=function(a){$.isArray(a)&&(a={condition:this.settings.default_condition,rules:a}),a&&a.rules&&(0!==a.rules.length||this.settings.allow_empty)||l.error("RulesParse","Incorrect data object passed"),this.clear(),this.setRoot(!1,a.data,this.parseGroupFlags(a)),a=this.change("setRules",a);var b=this;!function c(a,d){null!==d&&(void 0===a.condition?a.condition=b.settings.default_condition:-1==b.settings.conditions.indexOf(a.condition)&&l.error("UndefinedCondition",'Invalid condition "{0}"',a.condition),d.condition=a.condition,a.rules.forEach(function(a){var e;if(a.rules&&a.rules.length>0)if(-1!==b.settings.allow_groups&&b.settings.allow_groups1){h=["operator_not_multiple",e.type];break}break;case"select":if(d.multiple){if(void 0===b[i]||0===b[i].length||d.placeholder&&b[i]==d.placeholder_value){h=["select_empty"];break}if(!e.multiple&&b[i].length>1){h=["operator_not_multiple",e.type];break}}else if(void 0===b[i]||d.placeholder&&b[i]==d.placeholder_value){h=["select_empty"];break}break;default:switch(g.types[d.type]){case"string":if(void 0===b[i]||0===b[i].length){h=["string_empty"];break}if(void 0!==f.min&&b[i].lengthparseInt(f.max)){h=["string_exceed_max_length",f.max];break}if(f.format&&("string"==typeof f.format&&(f.format=new RegExp(f.format)),!f.format.test(b[i]))){h=["string_invalid_format",f.format];break}break;case"number":if(void 0===b[i]||isNaN(b[i])){h=["number_nan"];break}if("integer"==d.type){if(parseInt(b[i])!=b[i]){h=["number_not_integer"];break}}else if(parseFloat(b[i])!=b[i]){h=["number_not_double"];break}if(void 0!==f.min&&b[i]parseFloat(f.max)){h=["number_exceed_max",f.max];break}if(void 0!==f.step&&"any"!==f.step){var j=(b[i]/f.step).toPrecision(14);if(parseInt(j)!=j){h=["number_wrong_step",f.step];break}}break;case"datetime":if(void 0===b[i]||0===b[i].length){h=["datetime_empty"];break}if(f.format){"moment"in window||l.error("MissingLibrary","MomentJS is required for Date/Time validation. Get it here http://momentjs.com");var k=moment(b[i],f.format);if(!k.isValid()){h=["datetime_invalid",f.format];break}if(f.min&&kmoment(f.max,f.format)){h=["datetime_exceed_max",f.max];break}}break;case"boolean":if(c=b[i].trim().toLowerCase(),"true"!==c&&"false"!==c&&"1"!==c&&"0"!==c&&1!==b[i]&&0!==b[i]){h=["boolean_not_valid"];break}}}if(h!==!0)break}return h},g.prototype.nextGroupId=function(){return this.status.id+"_group_"+this.status.group_id++},g.prototype.nextRuleId=function(){return this.status.id+"_rule_"+this.status.rule_id++},g.prototype.getOperators=function(a){"string"==typeof a&&(a=this.getFilterById(a));for(var b=[],c=0,d=this.operators.length;d>c;c++){if(a.operators){if(-1==a.operators.indexOf(this.operators[c].type))continue}else if(-1==this.operators[c].apply_to.indexOf(g.types[a.type]))continue;b.push(this.operators[c])}return a.operators&&b.sort(function(b,c){return a.operators.indexOf(b.type)-a.operators.indexOf(c.type)}),this.change("getOperators",b,a)},g.prototype.getFilterById=function(a){if("-1"==a)return null;for(var b=0,c=this.filters.length;c>b;b++)if(this.filters[b].id==a)return this.filters[b];l.error("UndefinedFilter",'Undefined filter "{0}"',a)},g.prototype.getOperatorByType=function(a){if("-1"==a)return null;for(var b=0,c=this.operators.length;c>b;b++)if(this.operators[b].type==a)return this.operators[b];l.error("UndefinedOperator",'Undefined operator "{0}"',a)},g.prototype.getRuleValue=function(a){var b=a.filter,c=a.operator,d=[];if(b.valueGetter)d=b.valueGetter.call(this,a);else{for(var e=a.$el.find(h.value_container),f=0;f '+b+" "});break;case"select":h+='";break;case"textarea":h+='";break;default:switch(e.types[c.type]){case"number":h+='=f:e<=f},j=!1;i()&&(this.rules[e]instanceof h?void 0!==c&&(j=c.call(d,this.rules[e])===!1):j=b.call(d,this.rules[e])===!1,!j);e+=g);return!j},h.prototype.contains=function(a,b){return this.getNodePos(a)!==-1||!!b&&!this.each(function(a){return!0},function(b){return!b.contains(a,!0)})};var i=function(a,b){return this instanceof i?(g.call(this,a,b),this.__.filter=null,this.__.operator=null,this.__.flags={},void(this.__.value=void 0)):new i(a,b)};i.prototype=Object.create(g.prototype),i.prototype.constructor=i,c(i,["filter","operator","value"]),e.Group=h,e.Rule=i;var j=e.utils={};j.iterateOptions=function(a,b){a&&($.isArray(a)?a.forEach(function(a){$.isPlainObject(a)?$.each(a,function(a,c){return b(a,c),!1}):b(a,a)}):$.each(a,function(a,c){b(a,c)}))},j.fmt=function(a){var b=Array.prototype.slice.call(arguments,1);return a.replace(/{([0-9]+)}/g,function(a,c){return b[parseInt(c)]})},j.error=function(a,b){var c=new Error(j.fmt.apply(null,Array.prototype.slice.call(arguments,1)));throw c.name=a+"Error",c.args=Array.prototype.slice.call(arguments,2),c},j.changeType=function(a,b,c){switch(b){case"integer":return parseInt(a);case"double":return parseFloat(a);case"boolean":var d="true"===a.trim().toLowerCase()||"1"===a.trim()||1===a;return c?d?1:0:d;default:return a}},j.escapeString=function(a){return"string"!=typeof a?a:a.replace(/[\0\n\r\b\\\'\"]/g,function(a){switch(a){case"\0":return"\\0";case"\n":return"\\n";case"\r":return"\\r";case"\b":return"\\b";default:return"\\"+a}}).replace(/\t/g,"\\t").replace(/\x1a/g,"\\Z")},j.escapeRegExp=function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},j.escapeElementId=function(a){return a?a.replace(/(\\)?([:.\[\],])/g,function(a,b,c){return b?a:"\\"+c}):a},j.groupSort=function(a,b){var c=[],d=[];return a.forEach(function(a){var e;a[b]?(e=c.lastIndexOf(a[b]),e==-1?e=c.length:e++):e=c.length,c.splice(e,0,a[b]),d.splice(e,0,a)}),d},$.fn.queryBuilder=function(a){this.length>1&&j.error("Config","Unable to initialize on multiple target");var b=this.data("queryBuilder"),c="object"==typeof a&&a||{};return b||"destroy"!=a?(b||this.data("queryBuilder",new e(this,c)),"string"==typeof a?b[a].apply(b,Array.prototype.slice.call(arguments,1)):this):this},$.fn.queryBuilder.constructor=e,$.fn.queryBuilder.defaults=e.defaults,$.fn.queryBuilder.extend=e.extend,$.fn.queryBuilder.define=e.define,$.fn.queryBuilder.regional=e.regional,e.defaults({filterOperators:{equal:{op:"ogc:PropertyIsEqualTo"},not_equal:{op:"ogc:PropertyIsNotEqualTo"},less:{op:"ogc:PropertyIsLessThan"},less_or_equal:{op:"ogc:PropertyIsLessThanOrEqualTo"},greater:{op:"ogc:PropertyIsGreaterThan"},greater_or_equal:{op:"ogc:PropertyIsLessThanOrEqualTo"},between:{op:"ogc:PropertyIsBetween",sep:"And"},is_null:{op:"ogc:PropertyIsNull"}},filterStatements:{question_mark:function(){var a=[];return{add:function(b,c){return a.push(c),"?"},run:function(){return a}}},numbered:function(a){(!a||a.length>1)&&(a="$");var b=0,c=[];return{add:function(d,e){return c.push(e),b++,a+b},run:function(){return c}}},named:function(a){(!a||a.length>1)&&(a=":");var b={},c={};return{add:function(d,e){b[d.field]||(b[d.field]=1);var f=d.field+"_"+b[d.field]++;return c[f]=e,a+f},run:function(){return c}}}}}),e.extend({getFilter:function(a){a=void 0===a?this.getRules():a;var b="\n",c=this,d=function(a){var d=[];return a.condition||(a.condition=c.settings.default_condition),["AND","OR"].indexOf(a.condition.toUpperCase())===-1&&j.error("UndefinedSQLCondition",'Unable to build SQL query with condition "{0}"',a.condition),a.rules?(d.push(""),"OR"===a.condition.toUpperCase()?d.push(""):d.push(""),a.rules.forEach(function(a){var e=c.settings.filterOperators[a.operator];void 0===e&&j.error("UndefinedFilterOperator",'Unknown Filter operation for operator "{0}"',a.operator),d.push("<"+e.op+">"+b),d.push(""+a.field+""+b),d.push(""+a.value+""+b),d.push(""+b)}),"OR"===a.condition.toUpperCase()?d.push(""):d.push(""),d.push(""),d.join(" ")):""}(a);return{filter:d}}}),e.defaults({sqlOperators:{equal:{op:"= ?"},not_equal:{op:"!= ?"},"in":{op:"IN(?)",sep:", "},not_in:{op:"NOT IN(?)",sep:", "},less:{op:"< ?"},less_or_equal:{op:"<= ?"},greater:{op:"> ?"},greater_or_equal:{op:">= ?"},between:{op:"BETWEEN ?",sep:" AND "},not_between:{op:"NOT BETWEEN ?",sep:" AND "},begins_with:{op:"LIKE(?)",mod:"{0}%"},not_begins_with:{op:"NOT LIKE(?)",mod:"{0}%"},contains:{op:"LIKE(?)",mod:"%{0}%"},not_contains:{op:"NOT LIKE(?)",mod:"%{0}%"},ends_with:{op:"LIKE(?)",mod:"%{0}"},not_ends_with:{op:"NOT LIKE(?)",mod:"%{0}"},is_empty:{op:"= ''"},is_not_empty:{op:"!= ''"},is_null:{op:"IS NULL"},is_not_null:{op:"IS NOT NULL"}},sqlRuleOperator:{"=":function(a){return{val:a,op:""===a?"is_empty":"equal"}},"!=":function(a){return{val:a,op:""===a?"is_not_empty":"not_equal"}},LIKE:function(a){return"%"==a.slice(0,1)&&"%"==a.slice(-1)?{val:a.slice(1,-1),op:"contains"}:"%"==a.slice(0,1)?{val:a.slice(1),op:"ends_with"}:"%"==a.slice(-1)?{val:a.slice(0,-1),op:"begins_with"}:void j.error("SQLParse",'Invalid value for LIKE operator "{0}"',a)},IN:function(a){return{val:a,op:"in"}},"NOT IN":function(a){return{val:a,op:"not_in"}},"<":function(a){return{val:a,op:"less"}},"<=":function(a){return{val:a,op:"less_or_equal"}},">":function(a){return{val:a,op:"greater"}},">=":function(a){return{val:a,op:"greater_or_equal"}},BETWEEN:function(a){return{val:a,op:"between"}},"NOT BETWEEN":function(a){return{val:a,op:"not_between"}},IS:function(a){return null!==a&&j.error("SQLParse","Invalid value for IS operator"),{val:null,op:"is_null"}},"IS NOT":function(a){return null!==a&&j.error("SQLParse","Invalid value for IS operator"),{val:null,op:"is_not_null"}}},sqlStatements:{question_mark:function(){var a=[];return{add:function(b,c){return a.push(c),"?"},run:function(){return a}}},numbered:function(a){(!a||a.length>1)&&(a="$");var b=0,c=[];return{add:function(d,e){return c.push(e),b++,a+b},run:function(){return c}}},named:function(a){(!a||a.length>1)&&(a=":");var b={},c={};return{add:function(d,e){b[d.field]||(b[d.field]=1);var f=d.field+"_"+b[d.field]++;return c[f]=e,a+f},run:function(){return c}}}},sqlRuleStatement:{question_mark:function(a){var b=0;return{parse:function(c){return"?"==c?a[b++]:c},esc:function(a){return a.replace(/\?/g,"'?'")}}},numbered:function(a,b){(!b||b.length>1)&&(b="$");var c=new RegExp("^\\"+b+"[0-9]+$"),d=new RegExp("\\"+b+"([0-9]+)","g");return{parse:function(b){return c.test(b)?a[b.slice(1)-1]:b},esc:function(a){return a.replace(d,"'"+("$"==b?"$$":b)+"$1'")}}},named:function(a,b){(!b||b.length>1)&&(b=":");var c=new RegExp("^\\"+b),d=new RegExp("\\"+b+"("+Object.keys(a).join("|")+")","g");return{parse:function(b){return c.test(b)?a[b.slice(1)]:b},esc:function(a){return a.replace(d,"'"+("$"==b?"$$":b)+"$1'")}}}}}),e.extend({getSQL:function(a,b,c){if(c=void 0===c?this.getRules():c,b=b===!0?"\n":" ",a===!0&&(a="question_mark"),"string"==typeof a){var e=d(a);a=this.settings.sqlStatements[e[1]](e[2])}var f=this,g=function h(c){if(c.condition||(c.condition=f.settings.default_condition),["AND","OR"].indexOf(c.condition.toUpperCase())===-1&&j.error("UndefinedSQLCondition",'Unable to build SQL query with condition "{0}"',c.condition),!c.rules)return"";var d=[];return c.rules.forEach(function(c){if(c.rules&&c.rules.length>0)d.push("("+b+h(c)+b+")"+b);else{var e=f.settings.sqlOperators[c.operator],g=f.getOperatorByType(c.operator),i="";void 0===e&&j.error("UndefinedSQLOperator",'Unknown SQL operation for operator "{0}"',c.operator),0!==g.nb_inputs&&(c.value instanceof Array||(c.value=[c.value]),c.value.forEach(function(b,d){d>0&&(i+=e.sep),"integer"==c.type||"double"==c.type||"boolean"==c.type?b=j.changeType(b,c.type,!0):a||(b=j.escapeString(b)),e.mod&&(b=j.fmt(e.mod,b)),a?i+=a.add(c,b):("string"==typeof b&&(b="'"+b+"'"),i+=b)})),d.push(c.field+" "+e.op.replace(/\?/,i))}}),d.join(" "+c.condition+b)}(c);return a?{sql:g,params:a.run()}:{sql:g}},getRulesFromSQL:function(a,b){"SQLParser"in window||j.error("MissingLibrary","SQLParser is required to parse SQL queries. Get it here https://github.com/mistic100/sql-parser");var c=this;if("string"==typeof a&&(a={sql:a}),b===!0&&(b="question_mark"),"string"==typeof b){var e=d(b);b=this.settings.sqlRuleStatement[e[1]](a.params,e[2])}b&&(a.sql=b.esc(a.sql)),0!==a.sql.toUpperCase().indexOf("SELECT")&&(a.sql="SELECT * FROM table WHERE "+a.sql);var f=SQLParser.parse(a.sql);f.where||j.error("SQLParse","No WHERE clause found");var g={condition:this.settings.default_condition,rules:[]},h=g;return function i(a,d){if(["AND","OR"].indexOf(a.operation.toUpperCase())!==-1){d>0&&h.condition!=a.operation.toUpperCase()&&(h.rules.push({condition:c.settings.default_condition,rules:[]}),h=h.rules[h.rules.length-1]),h.condition=a.operation.toUpperCase(),d++;var e=h;i(a.left,d),h=e,i(a.right,d)}else{void 0!==a.left.value&&void 0!==a.right.value||j.error("SQLParse","Missing field and/or value"),$.isPlainObject(a.right.value)&&j.error("SQLParse","Value format not supported for {0}.",a.left.value);var f;f=$.isArray(a.right.value)?a.right.value.map(function(a){return a.value}):a.right.value,b&&(f=$.isArray(f)?f.map(b.parse):b.parse(f));var g=a.operation.toUpperCase();"<>"==g&&(g="!=");var k;k="NOT LIKE"==g?c.settings.sqlRuleOperator.LIKE:c.settings.sqlRuleOperator[g],void 0===k&&j.error("UndefinedSQLOperator",'Invalid SQL operation "{0}".',a.operation);var l=k.call(this,f,a.operation);"NOT LIKE"==g&&(l.op="not_"+l.op);var m=a.left.values.join(".");h.rules.push({id:c.change("getSQLFieldID",m,f),field:m,operator:l.op,value:l.val})}}(f.where.conditions,0),g},setRulesFromSQL:function(a,b){this.setRules(this.getRulesFromSQL(a,b))}}),e.regional.en={__locale:"English (en)",__author:'Damien "Mistic" Sorel, http://www.strangeplanet.fr',add_rule:"Add rule",add_group:"Add group",delete_rule:"Delete",delete_group:"Delete",conditions:{AND:"AND",OR:"OR"},operators:{equal:"equal",not_equal:"not equal","in":"in",not_in:"not in",less:"less",less_or_equal:"less or equal",greater:"greater",greater_or_equal:"greater or equal",between:"between",not_between:"not between",begins_with:"begins with",not_begins_with:"doesn't begin with",contains:"contains",not_contains:"doesn't contain",ends_with:"ends with",not_ends_with:"doesn't end with",is_empty:"is empty",is_not_empty:"is not empty",is_null:"is null",is_not_null:"is not null"},errors:{no_filter:"No filter selected",empty_group:"The group is empty",radio_empty:"No value selected",checkbox_empty:"No value selected",select_empty:"No value selected",string_empty:"Empty value",string_exceed_min_length:"Must contain at least {0} characters",string_exceed_max_length:"Must not contain more than {0} characters",string_invalid_format:"Invalid format ({0})",number_nan:"Not a number",number_not_integer:"Not an integer",number_not_double:"Not a real number",number_exceed_min:"Must be greater than {0}",number_exceed_max:"Must be lower than {0}",number_wrong_step:"Must be a multiple of {0}",datetime_empty:"Empty value",datetime_invalid:"Invalid date format ({0})",datetime_exceed_min:"Must be after {0}",datetime_exceed_max:"Must be before {0}",boolean_not_valid:"Not a boolean",operator_not_multiple:"Operator {0} cannot accept multiple values"}},e.defaults({lang_code:"en"})}); \ No newline at end of file diff --git a/dist/scss/default.scss b/dist/scss/default.scss index 49b4ebd7..95539019 100644 --- a/dist/scss/default.scss +++ b/dist/scss/default.scss @@ -168,8 +168,3 @@ $ticks-position: 5px, 10px !default; } } - -@import 'plugins/bt-tooltip-errors'; -@import 'plugins/filter-description'; -@import 'plugins/invert'; -@import 'plugins/sortable'; \ No newline at end of file diff --git a/src/plugins/ogc-filter/plugin.js b/src/plugins/ogc-filter/plugin.js new file mode 100644 index 00000000..ad8935a5 --- /dev/null +++ b/src/plugins/ogc-filter/plugin.js @@ -0,0 +1,168 @@ +/*! + * jQuery QueryBuilder OGC Filter Support + * Allows to export rules as a SQL WHERE statement as well as populating the builder from an SQL query. + */ + +// DEFAULT CONFIG +// =============================== +QueryBuilder.defaults({ + /* operators for internal -> SQL conversion */ + filterOperators: { + equal: { op: 'ogc:PropertyIsEqualTo' }, + not_equal: { op: 'ogc:PropertyIsNotEqualTo' }, + less: { op: 'ogc:PropertyIsLessThan' }, + less_or_equal: { op: 'ogc:PropertyIsLessThanOrEqualTo' }, + greater: { op: 'ogc:PropertyIsGreaterThan' }, + greater_or_equal: { op: 'ogc:PropertyIsLessThanOrEqualTo' }, + between: { op: 'ogc:PropertyIsBetween', sep: 'And' }, + is_null: { op: 'ogc:PropertyIsNull' } + + }, + + + /* statements for internal -> SQL conversion */ + filterStatements: { + 'question_mark': function() { + var params = []; + return { + add: function(rule, value) { + params.push(value); + return '?'; + }, + run: function() { + return params; + } + }; + }, + + 'numbered': function(char) { + if (!char || char.length > 1) char = '$'; + var index = 0; + var params = []; + return { + add: function(rule, value) { + params.push(value); + index++; + return char + index; + }, + run: function() { + return params; + } + }; + }, + + 'named': function(char) { + if (!char || char.length > 1) char = ':'; + var indexes = {}; + var params = {}; + return { + add: function(rule, value) { + if (!indexes[rule.field]) indexes[rule.field] = 1; + var key = rule.field + '_' + (indexes[rule.field]++); + params[key] = value; + return char + key; + }, + run: function() { + return params; + } + }; + } + } +}); + + +// PUBLIC METHODS +// =============================== +QueryBuilder.extend({ + /** + * Get rules as Filter query + * @throws UndefinedFilterConditionError, UndefinedFilterOperatorError + * @param data {object} (optional) rules + * @return {object} + */ + getFilter: function(data) { + data = (data === undefined) ? this.getRules() : data; + var nl = '\n'; + + var self = this; + + var filterXML = (function parse(data) { + var parts = []; + + if (!data.condition) { + data.condition = self.settings.default_condition; + } + if (['AND', 'OR'].indexOf(data.condition.toUpperCase()) === -1) { + Utils.error('UndefinedSQLCondition', 'Unable to build SQL query with condition "{0}"', data.condition); + } + + if (!data.rules) { + return ''; + } + + parts.push(''); + if (data.condition.toUpperCase() === 'OR') { + parts.push(''); + } else { + parts.push(''); + } + + + data.rules.forEach(function(rule) { + + var filterCmd = self.settings.filterOperators[rule.operator]; + var value = ''; + + if (filterCmd === undefined) { + Utils.error('UndefinedFilterOperator', 'Unknown Filter operation for operator "{0}"', rule.operator); + } + +/* rule.value.forEach(function(v, i) { + if (i > 0) { + value += sql.sep; + } + + if (rule.type == 'integer' || rule.type == 'double' || rule.type == 'boolean') { + v = Utils.changeType(v, rule.type, true); + } + else if (!stmt) { + v = Utils.escapeString(v); + } + + else { + if (typeof v == 'string') { + v = '\'' + v + '\''; + } + + value += v; + } + });*/ + + parts.push('<' + filterCmd.op + '>' + nl); + parts.push('' + rule.field + '' + nl); + parts.push('' + rule.value + '' + nl); + parts.push('' + nl); + + }); + + if (data.condition.toUpperCase() === 'OR') { + parts.push(''); + } else { + parts.push(''); + } + parts.push('') + + return parts.join(' '); + }(data)); + + return { + filter: filterXML + }; + } +}); + +function getStmtConfig(stmt) { + var config = stmt.match(/(question_mark|numbered|named)(?:\((.)\))?/); + if (!config) config = [null, 'question_mark', undefined]; + return config; +} diff --git a/tests/index.html b/tests/index.html index 4d6d8fea..ff5ee9e8 100644 --- a/tests/index.html +++ b/tests/index.html @@ -50,6 +50,7 @@ +