├── .editorconfig
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── main.yml
├── .gitignore
├── .jsdoc.json
├── LICENSE
├── README.md
├── build
├── dist.mjs
├── jsdoc.js
├── jsdoc.md
└── liveserver.mjs
├── examples
├── index.html
└── screenshot.png
├── package.json
├── src
├── .wrapper.js
├── core.js
├── data.js
├── defaults.js
├── i18n
│ ├── .wrapper.js
│ ├── ar.json
│ ├── az.json
│ ├── bg.json
│ ├── cs.json
│ ├── da.json
│ ├── de.json
│ ├── el.json
│ ├── en.json
│ ├── eo.json
│ ├── es.json
│ ├── fa-IR.json
│ ├── fr.json
│ ├── he.json
│ ├── hu.json
│ ├── it.json
│ ├── lt.json
│ ├── nl.json
│ ├── no.json
│ ├── pl.json
│ ├── pt-BR.json
│ ├── pt-PT.json
│ ├── ro.json
│ ├── ru.json
│ ├── sk.json
│ ├── sq.json
│ ├── sv.json
│ ├── sw.json
│ ├── tr.json
│ ├── ua.json
│ └── zh-CN.json
├── jquery.js
├── main.js
├── model.js
├── plugins.js
├── plugins
│ ├── bt-checkbox
│ │ ├── plugin.js
│ │ └── plugin.scss
│ ├── bt-tooltip-errors
│ │ ├── plugin.js
│ │ └── plugin.scss
│ ├── change-filters
│ │ └── plugin.js
│ ├── chosen-selectpicker
│ │ └── plugin.js
│ ├── filter-description
│ │ ├── plugin.js
│ │ └── plugin.scss
│ ├── invert
│ │ ├── i18n
│ │ │ ├── ar.json
│ │ │ ├── az.json
│ │ │ ├── cs.json
│ │ │ ├── el.json
│ │ │ ├── en.json
│ │ │ ├── eo.json
│ │ │ ├── fr.json
│ │ │ ├── he.json
│ │ │ ├── hu.json
│ │ │ ├── lt.json
│ │ │ ├── pl.json
│ │ │ ├── pt-BR.json
│ │ │ ├── ru.json
│ │ │ ├── sk.json
│ │ │ ├── sv.json
│ │ │ ├── sw.json
│ │ │ ├── tr.json
│ │ │ ├── ua.json
│ │ │ └── zh-CN.json
│ │ ├── plugin.js
│ │ └── plugin.scss
│ ├── mongodb-support
│ │ └── plugin.js
│ ├── not-group
│ │ ├── i18n
│ │ │ ├── en.json
│ │ │ ├── eo.json
│ │ │ ├── fr.json
│ │ │ ├── he.json
│ │ │ ├── hu.json
│ │ │ ├── lt.json
│ │ │ ├── ru.json
│ │ │ ├── sk.json
│ │ │ ├── sv.json
│ │ │ └── sw.json
│ │ └── plugin.js
│ ├── sortable
│ │ ├── plugin.js
│ │ └── plugin.scss
│ ├── sql-support
│ │ └── plugin.js
│ └── unique-filter
│ │ └── plugin.js
├── public.js
├── scss
│ ├── dark.scss
│ └── default.scss
├── template.js
└── utils.js
├── tests
├── common.js
├── core.module.js
├── data.module.js
├── index.html
├── plugins-gui.module.js
├── plugins.module.js
├── plugins.mongo-support.module.js
├── plugins.not-group.module.js
├── plugins.sql-support.module.js
└── utils.module.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_style = space
7 | indent_size = 4
8 | trim_trailing_whitespace = true
9 |
10 | [*.json]
11 | indent_size = 2
12 | insert_final_newline = false
13 |
14 | [*.scss]
15 | indent_size = 2
16 |
17 | [*.html]
18 | indent_size = 2
19 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Guidelines for contributing
2 |
3 | ## Work on `dev`
4 | Any merge request should be created from and issued to the `dev` branch.
5 |
6 | Do not add the `dist` files to your pull request. The directory is ignored for a reason: it is generated and pushed only when doing a release on `master`.
7 |
8 | ## Core vs Plugins
9 | I want to keep the core clean of extra (and certainly awesome) functionalities. That includes, but is not limited to, export/import plugins, visual aids, etc.
10 |
11 | Check the doc about [creating plugins](http://querybuilder.js.org/dev/plugins.html) and [use events](http://querybuilder.js.org/dev/events.html).
12 |
13 | I reserve the right to refuse any plugin I think is not useful for many people. Particularly, only import/export plugins for mainstream data storages will be integrated in the main repository. Others should be in a separated repository. But it's totally possible to add a link to your repository in the documentation.
14 |
15 | ## Unit tests
16 | Any big feature must have it's own QUnit tests suite. Of course existing tests must still pass after changes.
17 |
18 | I won't merge any branch not passing the TravisCI build, including JShint/JSCS/SCSSlint compliance.
19 |
20 | ## Translations
21 | Source language files are plain JSON files which will be converted to executable JS files by the build task. The `__locale` key must be filled with the international name of the language + 2-chars code and the `__author` key can be used to give information about the translator.
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Issues guidelines**
2 |
3 | - Please search in the [documentation](http://querybuilder.js.org) before asking.
4 | - Any issue without enough details won't get any answer and will be closed.
5 | - Help requests must be exhaustive, precise and come with some code explaining the need (use Markdown code highlight).
6 | - Bug reports must come with a simple test case, preferably on jsFiddle, Plunker, etc. (QueryBuilder is available on [jsDelivr](https://cdn.jsdelivr.net/npm/jQuery-QueryBuilder/dist/) and [unpkg](https://unpkg.com/jQuery-QueryBuilder/dist/) to be used on such platforms).
7 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Merge request checklist**
2 |
3 | - [ ] I read the [guidelines for contributing](https://github.com/mistic100/jQuery-QueryBuilder/blob/master/.github/CONTRIBUTING.md)
4 | - [ ] I created my branch from `dev` and I am issuing the PR to `dev`
5 | - [ ] I didn't pushed the `dist` directory
6 | - [ ] If it's a new feature, I added the necessary unit tests
7 | - [ ] If it's a new language, I filled the `__locale` and `__author` fields
8 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v3
12 |
13 | - uses: actions/setup-node@v3
14 | with:
15 | node-version: '16'
16 | cache: 'yarn'
17 |
18 | - name: build
19 | run: |
20 | yarn install
21 | yarn build
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | doc
4 | .sass-cache
5 | .idea
6 | *.iml
7 | package-lock.json
8 |
--------------------------------------------------------------------------------
/.jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "opts": {
3 | "private": false,
4 | "template": "node_modules/foodoc/template",
5 | "readme": "build/jsdoc.md"
6 | },
7 | "plugins": [
8 | "plugins/markdown"
9 | ],
10 | "templates": {
11 | "systemName": "jQuery QueryBuilder API",
12 | "systemSummary": "jQuery plugin for user friendly query/filter creator",
13 | "systemColor": "#004482",
14 | "copyright": "Licensed under MIT License, documentation under CC BY 3.0.",
15 | "includeDate": false,
16 | "inverseNav": false,
17 | "cleverLinks": true,
18 | "sort": "longname, version, since",
19 | "analytics": {
20 | "ua": "UA-28192323-3",
21 | "domain": "auto"
22 | },
23 | "navMembers": [
24 | {"kind": "class", "title": "Classes", "summary": "All documented classes."},
25 | {"kind": "external", "title": "Externals", "summary": "All documented external members."},
26 | {"kind": "global", "title": "Globals", "summary": "All documented globals."},
27 | {"kind": "mixin", "title": "Mixins", "summary": "All documented mixins."},
28 | {"kind": "interface", "title": "Interfaces", "summary": "All documented interfaces."},
29 | {"kind": "module", "title": "Modules", "summary": "All documented modules."},
30 | {"kind": "event", "title": "Events", "summary": "All documented events."},
31 | {"kind": "namespace", "title": "Namespaces", "summary": "All documented namespaces."},
32 | {"kind": "tutorial", "title": "Tutorials", "summary": "All available tutorials."}
33 | ],
34 | "scripts": [
35 | "https://cdnjs.cloudflare.com/ajax/libs/trianglify/1.0.1/trianglify.min.js",
36 | "js/custom.js"
37 | ]
38 | }
39 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-2018 Damien Sorel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jQuery QueryBuilder
2 |
3 | [](https://www.npmjs.com/package/jQuery-QueryBuilder)
4 | [](https://www.jsdelivr.com/package/npm/jQuery-QueryBuilder)
5 | [](https://github.com/mistic100/jQuery-QueryBuilder/actions)
6 | [](https://gitlocalize.com/repo/5259/whole_project?utm_source=badge)
7 |
8 | jQuery plugin offering an simple interface to create complex queries.
9 |
10 | [](https://querybuilder.js.org)
11 |
12 |
13 |
14 | ## Documentation
15 | [querybuilder.js.org](https://querybuilder.js.org)
16 |
17 |
18 |
19 | ## Install
20 |
21 | #### Manually
22 |
23 | [Download the latest release](https://github.com/mistic100/jQuery-QueryBuilder/releases)
24 |
25 | #### With npm
26 |
27 | ```bash
28 | $ npm install jQuery-QueryBuilder
29 | ```
30 |
31 | #### Via CDN
32 |
33 | jQuery-QueryBuilder is available on [jsDelivr](https://www.jsdelivr.com/package/npm/jQuery-QueryBuilder).
34 | ### Dependencies
35 | * [jQuery 3](https://jquery.com)
36 | * [Bootstrap 5](https://getbootstrap.com/docs/5.3/) CSS and bundle.js which includes `Popper` for tooltips and popovers
37 | * [Bootstrap Icons](https://icons.getbootstrap.com/)
38 | * [jQuery.extendext](https://github.com/mistic100/jQuery.extendext)
39 | * [MomentJS](https://momentjs.com) (optional, for Date/Time validation)
40 | * [SQL Parser](https://github.com/mistic100/sql-parser) (optional, for SQL methods)
41 | * Other Bootstrap/jQuery plugins used by plugins
42 |
43 | ($.extendext is directly included in the [standalone](https://github.com/mistic100/jQuery-QueryBuilder/blob/master/dist/js/query-builder.standalone.js) file)
44 |
45 |
46 |
47 | ## Developement
48 |
49 | Install Node dependencies with `npm install`.
50 |
51 | #### Build
52 |
53 | Run `npm run build` in the root directory to generate production files inside `dist`.
54 |
55 | #### Serve
56 |
57 | Run `npm run serve` to open the example page with automatic build and livereload.
58 |
59 |
60 | ## License
61 | This library is available under the MIT license.
62 |
--------------------------------------------------------------------------------
/build/dist.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { globSync } from 'glob';
4 | import * as sass from 'sass';
5 | import pkg from '../package.json' assert { type: 'json' };
6 |
7 | const DEV = process.argv[2] === '--dev';
8 |
9 | const DIST = 'dist/';
10 |
11 | const CORE_JS = [
12 | 'src/main.js',
13 | 'src/defaults.js',
14 | 'src/plugins.js',
15 | 'src/core.js',
16 | 'src/public.js',
17 | 'src/data.js',
18 | 'src/template.js',
19 | 'src/utils.js',
20 | 'src/model.js',
21 | 'src/jquery.js',
22 | ];
23 |
24 | const CORE_SASS = [
25 | 'src/scss/dark.scss',
26 | 'src/scss/default.scss',
27 | ];
28 |
29 | const STANDALONE_JS = {
30 | 'jquery-extendext': 'node_modules/jquery-extendext/jquery-extendext.js',
31 | 'query-builder': `${DIST}js/query-builder.js`,
32 | };
33 |
34 | const BANNER = () => `/*!
35 | * jQuery QueryBuilder ${pkg.version}
36 | * Copyright 2014-${new Date().getFullYear()} Damien "Mistic" Sorel (http://www.strangeplanet.fr)
37 | * Licensed under MIT (https://opensource.org/licenses/MIT)
38 | */`;
39 |
40 | const LANG_BANNER = (locale, author) => `/*!
41 | * jQuery QueryBuilder ${pkg.version}
42 | * Locale: ${locale}
43 | * Author: ${author}
44 | * Licensed under MIT (https://opensource.org/licenses/MIT)
45 | */`;
46 |
47 | const ALL_PLUGINS_JS = glob('src/plugins/*/plugin.js')
48 | .sort()
49 | .reduce((all, p) => {
50 | const n = p.split('/')[2];
51 | all[n] = p;
52 | return all;
53 | }, {});
54 |
55 | const ALL_PLUGINS_SASS = glob('src/plugins/*/plugin.scss')
56 | .sort()
57 | .reduce((all, p) => {
58 | const n = p.split('/')[2];
59 | all[n] = p;
60 | return all;
61 | }, {});
62 |
63 | const ALL_LANGS = glob('src/i18n/*.json')
64 | .map(p => p.split(/[\/\.]/)[2])
65 | .sort();
66 |
67 | function glob(pattern) {
68 | return globSync(pattern)
69 | .map(p => p.split(path.sep).join('/'));
70 | }
71 |
72 | /**
73 | * Build lang files
74 | */
75 | function buildLangs() {
76 | const wrapper = fs.readFileSync('src/i18n/.wrapper.js', { encoding: 'utf8' })
77 | .split('@@js\n');
78 |
79 | ALL_LANGS.forEach(lang => {
80 | const outpath = `${DIST}i18n/query-builder.${lang}.js`;
81 | console.log(`LANG ${lang} (${outpath})`);
82 | fs.writeFileSync(outpath, getLang(lang, wrapper));
83 | });
84 | }
85 |
86 | /**
87 | * Get the content of a single lang
88 | */
89 | function getLang(lang, wrapper = ['', '']) {
90 | const corepath = `src/i18n/${lang}.json`;
91 | const content = JSON.parse(fs.readFileSync(corepath, { encoding: 'utf8' }));
92 |
93 | Object.keys(ALL_PLUGINS_JS).forEach(plugin => {
94 | const pluginpath = `src/plugins/${plugin}/i18n/${lang}.json`;
95 | try {
96 | const plugincontent = JSON.parse(fs.readFileSync(pluginpath, { encoding: 'utf8' }));
97 | Object.assign(content, plugincontent);
98 | } catch { }
99 | });
100 |
101 | return LANG_BANNER(content.__locale || lang, content.__author || '')
102 | + '\n\n'
103 | + wrapper[0]
104 | + `QueryBuilder.regional['${lang}'] = `
105 | + JSON.stringify(content, null, 2)
106 | + ';\n\n'
107 | + `QueryBuilder.defaults({ lang_code: '${lang}' });`
108 | + wrapper[1];
109 | }
110 |
111 | /**
112 | * Build main JS file
113 | */
114 | function buildMain() {
115 | const wrapper = fs.readFileSync('src/.wrapper.js', { encoding: 'utf8' })
116 | .split('@@js\n');
117 |
118 | const files_to_load = [
119 | ...CORE_JS,
120 | ...Object.values(ALL_PLUGINS_JS),
121 | ];
122 |
123 | const output = BANNER()
124 | + '\n\n'
125 | + wrapper[0]
126 | + files_to_load.map(f => fs.readFileSync(f, { encoding: 'utf8' })).join('\n\n')
127 | + '\n\n'
128 | + getLang('en')
129 | + wrapper[1];
130 |
131 | const outpath = `${DIST}js/query-builder.js`;
132 | console.log(`MAIN (${outpath})`);
133 | fs.writeFileSync(outpath, output);
134 | }
135 |
136 | /**
137 | * Build standalone JS file
138 | */
139 | function buildStandalone() {
140 | const output = Object.entries(STANDALONE_JS)
141 | .map(([name, file]) => {
142 | return fs.readFileSync(file, { encoding: 'utf8' })
143 | .replace(/define\((.*?)\);/, `define('${name}', $1);`);
144 | })
145 | .join('\n\n');
146 |
147 | const outpath = `${DIST}js/query-builder.standalone.js`;
148 | console.log(`STANDALONE (${outpath})`);
149 | fs.writeFileSync(outpath, output);
150 | }
151 |
152 | /**
153 | * Copy SASS files
154 | */
155 | function copySass() {
156 | Object.entries(ALL_PLUGINS_SASS).forEach(([plugin, path]) => {
157 | const outpath = `${DIST}scss/plugins/${plugin}.scss`;
158 | console.log(`SASS ${plugin} (${path})`);
159 | fs.copyFileSync(path, outpath);
160 | });
161 |
162 | CORE_SASS.forEach(path => {
163 | const name = path.split('/').pop();
164 |
165 | const content = fs.readFileSync(path, { encoding: 'utf8' });
166 |
167 | let output = BANNER()
168 | + '\n'
169 | + content;
170 | if (name === 'default.scss') {
171 | output += '\n'
172 | + Object.keys(ALL_PLUGINS_SASS).map(p => `@import "plugins/${p}";`).join('\n');
173 | }
174 |
175 | const outpath = `${DIST}scss/${name}`;
176 | console.log(`SASS (${path})`);
177 | fs.writeFileSync(outpath, output);
178 | });
179 | }
180 |
181 | /**
182 | * Build CSS files
183 | */
184 | function buildCss() {
185 | CORE_SASS.forEach(p => {
186 | const path = p.replace('src/', DIST);
187 | const name = path.split('/').pop();
188 |
189 | const output = sass.compile(path);
190 |
191 | const outpath = `${DIST}css/query-builder.${name.split('.').shift()}.css`;
192 | console.log(`CSS (${path})`);
193 | fs.writeFileSync(outpath, output.css);
194 | });
195 | }
196 |
197 | if (!DEV) {
198 | fs.rmSync(DIST, { recursive: true, force: true });
199 | }
200 | fs.mkdirSync(DIST + 'css', { recursive: true });
201 | fs.mkdirSync(DIST + 'i18n', { recursive: true });
202 | fs.mkdirSync(DIST + 'js', { recursive: true });
203 | fs.mkdirSync(DIST + 'scss', { recursive: true });
204 | fs.mkdirSync(DIST + 'scss/plugins', { recursive: true });
205 |
206 | buildLangs();
207 | buildMain();
208 | buildStandalone();
209 | copySass();
210 | buildCss();
211 |
--------------------------------------------------------------------------------
/build/jsdoc.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var header = $('.page-header');
3 | var pattern = Trianglify({
4 | width: window.screen.width | header.outerWidth(),
5 | height: header.outerHeight(),
6 | cell_size: 90,
7 | seed: 'jQuery QueryBuilder',
8 | x_colors: ['#0074d9', '#001224']
9 | });
10 |
11 | header.css('background-image', 'url(' + pattern.png() + ')');
12 | }());
13 |
--------------------------------------------------------------------------------
/build/jsdoc.md:
--------------------------------------------------------------------------------
1 | # [Main documentation](..)
2 |
3 | # Entry point: [$.fn.QueryBuilder](external-_jQuery.fn_.html)
4 |
5 | # [QueryBuilder](QueryBuilder.html)
6 |
7 | # [Rule](Rule.html) & [Group](Group.html)
8 |
9 | # [Events](list_event.html)
10 |
11 | # [Plugins](module-plugins.html)
12 |
--------------------------------------------------------------------------------
/build/liveserver.mjs:
--------------------------------------------------------------------------------
1 | import liveServer from 'alive-server';
2 | import path from 'path';
3 |
4 | const rootDir = process.cwd();
5 |
6 | const EXAMPLES_DIR = 'examples';
7 | const DIST_DIR = 'dist';
8 |
9 | liveServer.start({
10 | open: true,
11 | root: path.join(rootDir, EXAMPLES_DIR),
12 | watch: [
13 | path.join(rootDir, EXAMPLES_DIR),
14 | path.join(rootDir, DIST_DIR),
15 | ],
16 | mount: [
17 | ['/node_modules', path.join(rootDir, 'node_modules')],
18 | ['/dist', path.join(rootDir, DIST_DIR)],
19 | ],
20 | });
21 |
--------------------------------------------------------------------------------
/examples/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mistic100/jQuery-QueryBuilder/90d265c2fa92cef699d32a458552246b26fc0480/examples/screenshot.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jQuery-QueryBuilder",
3 | "version": "2.5.0",
4 | "author": {
5 | "name": "Damien \"Mistic\" Sorel",
6 | "email": "contact@git.strangeplanet.fr",
7 | "url": "https://www.strangeplanet.fr"
8 | },
9 | "description": "jQuery plugin for user friendly query/filter creator",
10 | "main": "dist/js/query-builder.js",
11 | "files": [
12 | "dist/",
13 | "src/"
14 | ],
15 | "dependencies": {
16 | "bootstrap": "^5.3.0",
17 | "@popperjs/core": "^2.11.8",
18 | "bootstrap-icons": "^1.11.3",
19 | "jquery": "^3.5.1",
20 | "jquery-extendext": "^1.0.0",
21 | "moment": "^2.29.1",
22 | "sql-parser-mistic": "^1.2.3"
23 | },
24 | "devDependencies": {
25 | "alive-server": "^1.3.0",
26 | "awesome-bootstrap-checkbox": "^0.3.7",
27 | "bootbox": "^6.0.0",
28 | "bootstrap-slider": "^10.0.0",
29 | "chosenjs": "^1.4.3",
30 | "concurrently": "^8.2.0",
31 | "deepmerge": "^2.1.0",
32 | "foodoc": "^0.0.9",
33 | "glob": "^10.3.1",
34 | "interactjs": "^1.3.3",
35 | "nodemon": "^2.0.22",
36 | "sass": "^1.63.6",
37 | "@selectize/selectize": "^0.15.2"
38 | },
39 | "keywords": [
40 | "jquery",
41 | "query",
42 | "builder",
43 | "filter"
44 | ],
45 | "license": "MIT",
46 | "homepage": "https://querybuilder.js.org",
47 | "repository": {
48 | "type": "git",
49 | "url": "git://github.com/mistic100/jQuery-QueryBuilder.git"
50 | },
51 | "bugs": {
52 | "url": "https://github.com/mistic100/jQuery-QueryBuilder/issues"
53 | },
54 | "scripts": {
55 | "build": "node ./build/dist.mjs",
56 | "watch:build": "nodemon --watch src -e js,scss,json ./build/dist.mjs --dev",
57 | "watch:serve": "node ./build/liveserver.mjs",
58 | "serve": "concurrently \"npm:watch:build\" \"npm:watch:serve\""
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/.wrapper.js:
--------------------------------------------------------------------------------
1 | (function(root, factory) {
2 | if (typeof define == 'function' && define.amd) {
3 | define(['jquery', 'jquery-extendext'], factory);
4 | }
5 | else if (typeof module === 'object' && module.exports) {
6 | module.exports = factory(require('jquery'), require('jquery-extendext'));
7 | }
8 | else {
9 | factory(root.jQuery);
10 | }
11 | }(this, function($) {
12 | "use strict";
13 |
14 | @@js
15 |
16 | return QueryBuilder;
17 |
18 | }));
19 |
--------------------------------------------------------------------------------
/src/defaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Allowed types and their internal representation
3 | * @type {object.}
4 | * @readonly
5 | * @private
6 | */
7 | QueryBuilder.types = {
8 | 'string': 'string',
9 | 'integer': 'number',
10 | 'double': 'number',
11 | 'date': 'datetime',
12 | 'time': 'datetime',
13 | 'datetime': 'datetime',
14 | 'boolean': 'boolean'
15 | };
16 |
17 | /**
18 | * Allowed inputs
19 | * @type {string[]}
20 | * @readonly
21 | * @private
22 | */
23 | QueryBuilder.inputs = [
24 | 'text',
25 | 'number',
26 | 'textarea',
27 | 'radio',
28 | 'checkbox',
29 | 'select'
30 | ];
31 |
32 | /**
33 | * Runtime modifiable options with `setOptions` method
34 | * @type {string[]}
35 | * @readonly
36 | * @private
37 | */
38 | QueryBuilder.modifiable_options = [
39 | 'display_errors',
40 | 'allow_groups',
41 | 'allow_empty',
42 | 'default_condition',
43 | 'default_filter'
44 | ];
45 |
46 | /**
47 | * CSS selectors for common components
48 | * @type {object.}
49 | * @readonly
50 | */
51 | QueryBuilder.selectors = {
52 | group_container: '.rules-group-container',
53 | rule_container: '.rule-container',
54 | filter_container: '.rule-filter-container',
55 | operator_container: '.rule-operator-container',
56 | value_container: '.rule-value-container',
57 | error_container: '.error-container',
58 | condition_container: '.rules-group-header .group-conditions',
59 |
60 | rule_header: '.rule-header',
61 | group_header: '.rules-group-header',
62 | group_actions: '.group-actions',
63 | rule_actions: '.rule-actions',
64 |
65 | rules_list: '.rules-group-body>.rules-list',
66 |
67 | group_condition: '.rules-group-header [name$=_cond]',
68 | rule_filter: '.rule-filter-container [name$=_filter]',
69 | rule_operator: '.rule-operator-container [name$=_operator]',
70 | rule_value: '.rule-value-container [name*=_value_]',
71 |
72 | add_rule: '[data-add=rule]',
73 | delete_rule: '[data-delete=rule]',
74 | add_group: '[data-add=group]',
75 | delete_group: '[data-delete=group]'
76 | };
77 |
78 | /**
79 | * Template strings (see template.js)
80 | * @type {object.}
81 | * @readonly
82 | */
83 | QueryBuilder.templates = {};
84 |
85 | /**
86 | * Localized strings (see i18n/)
87 | * @type {object.}
88 | * @readonly
89 | */
90 | QueryBuilder.regional = {};
91 |
92 | /**
93 | * Default operators
94 | * @type {object.}
95 | * @readonly
96 | */
97 | QueryBuilder.OPERATORS = {
98 | equal: { type: 'equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] },
99 | not_equal: { type: 'not_equal', nb_inputs: 1, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] },
100 | in: { type: 'in', nb_inputs: 1, multiple: true, apply_to: ['string', 'number', 'datetime'] },
101 | not_in: { type: 'not_in', nb_inputs: 1, multiple: true, apply_to: ['string', 'number', 'datetime'] },
102 | less: { type: 'less', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] },
103 | less_or_equal: { type: 'less_or_equal', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] },
104 | greater: { type: 'greater', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] },
105 | greater_or_equal: { type: 'greater_or_equal', nb_inputs: 1, multiple: false, apply_to: ['number', 'datetime'] },
106 | between: { type: 'between', nb_inputs: 2, multiple: false, apply_to: ['number', 'datetime'] },
107 | not_between: { type: 'not_between', nb_inputs: 2, multiple: false, apply_to: ['number', 'datetime'] },
108 | begins_with: { type: 'begins_with', nb_inputs: 1, multiple: false, apply_to: ['string'] },
109 | not_begins_with: { type: 'not_begins_with', nb_inputs: 1, multiple: false, apply_to: ['string'] },
110 | contains: { type: 'contains', nb_inputs: 1, multiple: false, apply_to: ['string'] },
111 | not_contains: { type: 'not_contains', nb_inputs: 1, multiple: false, apply_to: ['string'] },
112 | ends_with: { type: 'ends_with', nb_inputs: 1, multiple: false, apply_to: ['string'] },
113 | not_ends_with: { type: 'not_ends_with', nb_inputs: 1, multiple: false, apply_to: ['string'] },
114 | is_empty: { type: 'is_empty', nb_inputs: 0, multiple: false, apply_to: ['string'] },
115 | is_not_empty: { type: 'is_not_empty', nb_inputs: 0, multiple: false, apply_to: ['string'] },
116 | is_null: { type: 'is_null', nb_inputs: 0, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] },
117 | is_not_null: { type: 'is_not_null', nb_inputs: 0, multiple: false, apply_to: ['string', 'number', 'datetime', 'boolean'] }
118 | };
119 |
120 | /**
121 | * Default configuration
122 | * @type {object}
123 | * @readonly
124 | */
125 | QueryBuilder.DEFAULTS = {
126 | filters: [],
127 | plugins: [],
128 |
129 | sort_filters: false,
130 | display_errors: true,
131 | allow_groups: -1,
132 | allow_empty: false,
133 | conditions: ['AND', 'OR'],
134 | default_condition: 'AND',
135 | inputs_separator: ' , ',
136 | select_placeholder: '------',
137 | display_empty_filter: true,
138 | default_filter: null,
139 | optgroups: {},
140 |
141 | default_rule_flags: {
142 | filter_readonly: false,
143 | operator_readonly: false,
144 | value_readonly: false,
145 | no_delete: false
146 | },
147 |
148 | default_group_flags: {
149 | condition_readonly: false,
150 | no_add_rule: false,
151 | no_add_group: false,
152 | no_delete: false
153 | },
154 |
155 | templates: {
156 | group: null,
157 | rule: null,
158 | filterSelect: null,
159 | operatorSelect: null,
160 | ruleValueSelect: null
161 | },
162 |
163 | lang_code: 'en',
164 | lang: {},
165 |
166 | operators: [
167 | 'equal',
168 | 'not_equal',
169 | 'in',
170 | 'not_in',
171 | 'less',
172 | 'less_or_equal',
173 | 'greater',
174 | 'greater_or_equal',
175 | 'between',
176 | 'not_between',
177 | 'begins_with',
178 | 'not_begins_with',
179 | 'contains',
180 | 'not_contains',
181 | 'ends_with',
182 | 'not_ends_with',
183 | 'is_empty',
184 | 'is_not_empty',
185 | 'is_null',
186 | 'is_not_null'
187 | ],
188 |
189 | icons: {
190 | add_group: 'bi-plus-circle-fill',
191 | add_rule: 'bi-plus-lg',
192 | remove_group: 'bi-x-lg',
193 | remove_rule: 'bi-x-lg',
194 | error: 'bi-exclamation-triangle'
195 | }
196 | };
197 |
--------------------------------------------------------------------------------
/src/i18n/.wrapper.js:
--------------------------------------------------------------------------------
1 | (function(root, factory) {
2 | if (typeof define == 'function' && define.amd) {
3 | define(['jquery', 'query-builder'], factory);
4 | }
5 | else {
6 | factory(root.jQuery);
7 | }
8 | }(this, function($) {
9 | "use strict";
10 |
11 | var QueryBuilder = $.fn.queryBuilder;
12 |
13 | @@js
14 |
15 | }));
--------------------------------------------------------------------------------
/src/i18n/ar.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Arabic (ar)",
3 | "__author": "Mohamed YOUNES, https://github.com/MedYOUNES",
4 | "add_rule": "إضافة حُكم",
5 | "add_group": "إضافة زُمْرَة",
6 | "delete_rule": "حذف",
7 | "delete_group": "حذف",
8 | "conditions": {
9 | "AND": "و",
10 | "OR": "أو"
11 | },
12 | "operators": {
13 | "equal": "يساوي",
14 | "not_equal": "غير مساوٍ",
15 | "in": "في",
16 | "not_in": "ليس في",
17 | "less": "أقل من",
18 | "less_or_equal": "أصغر أو مساو",
19 | "greater": "أكبر",
20 | "greater_or_equal": "أكبر أو مساو",
21 | "between": "محصور بين",
22 | "not_between": "غير محصور بين",
23 | "begins_with": "يبدأ بـ",
24 | "not_begins_with": "لا يبدأ بـ",
25 | "contains": "يحتوي على",
26 | "not_contains": "لا يحتوي على",
27 | "ends_with": "ينتهي بـ",
28 | "not_ends_with": "لا ينتهي بـ",
29 | "is_empty": "فارغ",
30 | "is_not_empty": "غير فارغ",
31 | "is_null": "صفر",
32 | "is_not_null": "ليس صفرا"
33 | },
34 | "errors": {
35 | "no_filter": "لم تحدد أي مرشح",
36 | "empty_group": "الزمرة فارغة",
37 | "radio_empty": "لم تحدد أي قيمة",
38 | "checkbox_empty": "لم تحدد أي قيمة",
39 | "select_empty": "لم تحدد أي قيمة",
40 | "string_empty": "النص فارغ",
41 | "string_exceed_min_length": "النص دون الأدنى المسموح به",
42 | "string_exceed_max_length": "النص فوق الأقصى المسموح به",
43 | "string_invalid_format": "تركيبة غير صحيحة",
44 | "number_nan": "ليس عددا",
45 | "number_not_integer": "ليس عددا صحيحا",
46 | "number_not_double": "ليس عددا كسريا",
47 | "number_exceed_min": "العدد أصغر من الأدنى المسموح به",
48 | "number_exceed_max": "العدد أكبر من الأقصى المسموح به",
49 | "number_wrong_step": "أخطأت في حساب مضاعفات العدد",
50 | "datetime_empty": "لم تحدد التاريخ",
51 | "datetime_invalid": "صيغة التاريخ غير صحيحة",
52 | "datetime_exceed_min": "التاريخ دون الأدنى المسموح به",
53 | "datetime_exceed_max": "التاريخ أكبر من الأقصى المسموح به",
54 | "boolean_not_valid": "ليست قيمة منطقية ثنائية",
55 | "operator_not_multiple": "العامل ليس متعدد القيَم"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/i18n/az.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Azerbaijan (az)",
3 | "__author": "Megaplan, mborisv ",
4 |
5 | "add_rule": "Əlavə etmək",
6 | "add_group": "Qrup əlavə etmək",
7 | "delete_rule": "Silmək",
8 | "delete_group": "Silmək",
9 | "conditions": {
10 | "AND": "VƏ",
11 | "OR": "VƏ YA"
12 | },
13 | "operators": {
14 | "equal": "bərabərdir",
15 | "not_equal": "bərabər deyil",
16 | "in": "qeyd edilmişlərdən",
17 | "not_in": "qeyd olunmamışlardan",
18 | "less": "daha az",
19 | "less_or_equal": "daha az və ya bərabər",
20 | "greater": "daha çox",
21 | "greater_or_equal": "daha çox və ya bərabər",
22 | "between": "arasında",
23 | "begins_with": "başlayır",
24 | "not_begins_with": "başlamır",
25 | "contains": "ibarətdir",
26 | "not_contains": "yoxdur",
27 | "ends_with": "başa çatır",
28 | "not_ends_with": "başa çatmır",
29 | "is_empty": "boş sətir",
30 | "is_not_empty": "boş olmayan sətir",
31 | "is_null": "boşdur",
32 | "is_not_null": "boş deyil"
33 | },
34 | "errors": {
35 | "no_filter": "Filterlər seçilməyib",
36 | "empty_group": "Qrup boşdur",
37 | "radio_empty": "Məna seçilməyib",
38 | "checkbox_empty": "Məna seçilməyib",
39 | "select_empty": "Məna seçilməyib",
40 | "string_empty": "Doldurulmayıb",
41 | "string_exceed_min_length": "{0} daha çox simvol olmalıdır",
42 | "string_exceed_max_length": "{0} daha az simvol olmalıdır",
43 | "string_invalid_format": "Yanlış format ({0})",
44 | "number_nan": "Rəqəm deyil",
45 | "number_not_integer": "Rəqəm deyil",
46 | "number_not_double": "Rəqəm deyil",
47 | "number_exceed_min": "{0} daha çox olmalıdır",
48 | "number_exceed_max": "{0} daha az olmalıdır",
49 | "number_wrong_step": "{0} bölünən olmalıdır",
50 | "datetime_empty": "Doldurulmayıb",
51 | "datetime_invalid": "Yanlış tarix formatı ({0})",
52 | "datetime_exceed_min": "{0} sonra olmalıdır",
53 | "datetime_exceed_max": "{0} əvvəl olmalıdır",
54 | "boolean_not_valid": "Loqik olmayan",
55 | "operator_not_multiple": "\"{1}\" operatoru çoxlu məna daşımır"
56 | }
57 | }
--------------------------------------------------------------------------------
/src/i18n/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Bulgarian (bg)",
3 | "__author": "Valentin Hristov",
4 |
5 | "add_rule": "Добави правило",
6 | "add_group": "Добави група",
7 | "delete_rule": "Изтрий",
8 | "delete_group": "Изтрий",
9 |
10 | "conditions": {
11 | "AND": "И",
12 | "OR": "ИЛИ"
13 | },
14 |
15 | "operators": {
16 | "equal": "равно",
17 | "not_equal": "различно",
18 | "in": "в",
19 | "not_in": "не е в",
20 | "less": "по-малко",
21 | "less_or_equal": "по-малко или равно",
22 | "greater": "по-голям",
23 | "greater_or_equal": "по-голям или равно",
24 | "between": "между",
25 | "not_between": "не е между",
26 | "begins_with": "започва с",
27 | "not_begins_with": "не започва с",
28 | "contains": "съдържа",
29 | "not_contains": "не съдържа",
30 | "ends_with": "завършва с",
31 | "not_ends_with": "не завършва с",
32 | "is_empty": "е празно",
33 | "is_not_empty": "не е празно",
34 | "is_null": "е нищо",
35 | "is_not_null": "различно от нищо"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Не е избран филтър",
40 | "empty_group": "Групата е празна",
41 | "radio_empty": "Не е селектирана стойност",
42 | "checkbox_empty": "Не е селектирана стойност",
43 | "select_empty": "Не е селектирана стойност",
44 | "string_empty": "Празна стойност",
45 | "string_exceed_min_length": "Необходимо е да съдържа поне {0} символа",
46 | "string_exceed_max_length": "Необходимо е да съдържа повече от {0} символа",
47 | "string_invalid_format": "Невалиден формат ({0})",
48 | "number_nan": "Не е число",
49 | "number_not_integer": "Не е цяло число",
50 | "number_not_double": "Не е реално число",
51 | "number_exceed_min": "Трябва да е по-голямо от {0}",
52 | "number_exceed_max": "Трябва да е по-малко от {0}",
53 | "number_wrong_step": "Трябва да е кратно на {0}",
54 | "datetime_empty": "Празна стойност",
55 | "datetime_invalid": "Невалиден формат на дата ({0})",
56 | "datetime_exceed_min": "Трябва да е след {0}",
57 | "datetime_exceed_max": "Трябва да е преди {0}",
58 | "boolean_not_valid": "Не е булева",
59 | "operator_not_multiple": "Оператора \"{1}\" не може да приеме множество стойности"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/i18n/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Čeština (cs)",
3 | "__author": "Megaplan, mborisv ",
4 |
5 | "add_rule": "Přidat",
6 | "add_group": "Přidat skupinu",
7 | "delete_rule": "Odstranit",
8 | "delete_group": "Odstranit skupinu",
9 | "conditions": {
10 | "AND": "I",
11 | "OR": "NEBO"
12 | },
13 | "operators": {
14 | "equal": "stejně",
15 | "not_equal": "liší se",
16 | "in": "z uvedených",
17 | "not_in": "ne z uvedených",
18 | "less": "méně",
19 | "less_or_equal": "méně nebo stejně",
20 | "greater": "více",
21 | "greater_or_equal": "více nebo stejně",
22 | "between": "mezi",
23 | "begins_with": "začíná z",
24 | "not_begins_with": "nezačíná z",
25 | "contains": "obsahuje",
26 | "not_contains": "neobsahuje",
27 | "ends_with": "končí na",
28 | "not_ends_with": "nekončí na",
29 | "is_empty": "prázdný řádek",
30 | "is_not_empty": "neprázdný řádek",
31 | "is_null": "prázdno",
32 | "is_not_null": "plno"
33 | },
34 | "errors": {
35 | "no_filter": "není vybraný filtr",
36 | "empty_group": "prázdná skupina",
37 | "radio_empty": "Není udaná hodnota",
38 | "checkbox_empty": "Není udaná hodnota",
39 | "select_empty": "Není udaná hodnota",
40 | "string_empty": "Nevyplněno",
41 | "string_exceed_min_length": "Musí obsahovat více {0} symbolů",
42 | "string_exceed_max_length": "Musí obsahovat méně {0} symbolů",
43 | "string_invalid_format": "Nesprávný formát ({0})",
44 | "number_nan": "Žádné číslo",
45 | "number_not_integer": "Žádné číslo",
46 | "number_not_double": "Žádné číslo",
47 | "number_exceed_min": "Musí být více {0}",
48 | "number_exceed_max": "Musí být méně {0}",
49 | "number_wrong_step": "Musí být násobkem {0}",
50 | "datetime_empty": "Nevyplněno",
51 | "datetime_invalid": "Nesprávný formát datumu ({0})",
52 | "datetime_exceed_min": "Musí být po {0}",
53 | "datetime_exceed_max": "Musí být do {0}",
54 | "boolean_not_valid": "Nelogické",
55 | "operator_not_multiple": "Operátor \"{1}\" nepodporuje mnoho hodnot"
56 | }
57 | }
--------------------------------------------------------------------------------
/src/i18n/da.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Danish (da)",
3 | "__author": "Jna Borup Coyle, github@coyle.dk",
4 |
5 | "add_rule": "Tilføj regel",
6 | "add_group": "Tilføj gruppe",
7 | "delete_rule": "Slet regel",
8 | "delete_group": "Slet gruppe",
9 |
10 | "conditions": {
11 | "AND": "OG",
12 | "OR": "ELLER"
13 | },
14 |
15 | "condition_and": "OG",
16 | "condition_or": "ELLER",
17 |
18 | "operators": {
19 | "equal": "lig med",
20 | "not_equal": "ikke lige med",
21 | "in": "i",
22 | "not_in": "ikke i",
23 | "less": "mindre",
24 | "less_or_equal": "mindre eller lig med",
25 | "greater": "større",
26 | "greater_or_equal": "større eller lig med",
27 | "begins_with": "begynder med",
28 | "not_begins_with": "begynder ikke med",
29 | "contains": "indeholder",
30 | "not_contains": "indeholder ikke",
31 | "ends_with": "slutter med",
32 | "not_ends_with": "slutter ikke med",
33 | "is_empty": "er tom",
34 | "is_not_empty": "er ikke tom",
35 | "is_null": "er null",
36 | "is_not_null": "er ikke null"
37 | }
38 | }
--------------------------------------------------------------------------------
/src/i18n/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "German (de)",
3 | "__author": "\"raimu\"",
4 |
5 | "add_rule": "neue Regel",
6 | "add_group": "neue Gruppe",
7 | "delete_rule": "löschen",
8 | "delete_group": "löschen",
9 |
10 | "conditions": {
11 | "AND": "UND",
12 | "OR": "ODER"
13 | },
14 |
15 | "operators": {
16 | "equal": "gleich",
17 | "not_equal": "ungleich",
18 | "in": "in",
19 | "not_in": "nicht in",
20 | "less": "kleiner",
21 | "less_or_equal": "kleiner gleich",
22 | "greater": "größer",
23 | "greater_or_equal": "größer gleich",
24 | "between": "zwischen",
25 | "not_between": "nicht zwischen",
26 | "begins_with": "beginnt mit",
27 | "not_begins_with": "beginnt nicht mit",
28 | "contains": "enthält",
29 | "not_contains": "enthält nicht",
30 | "ends_with": "endet mit",
31 | "not_ends_with": "endet nicht mit",
32 | "is_empty": "ist leer",
33 | "is_not_empty": "ist nicht leer",
34 | "is_null": "ist null",
35 | "is_not_null": "ist nicht null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Kein Filter ausgewählt",
40 | "empty_group": "Die Gruppe ist leer",
41 | "radio_empty": "Kein Wert ausgewählt",
42 | "checkbox_empty": "Kein Wert ausgewählt",
43 | "select_empty": "Kein Wert ausgewählt",
44 | "string_empty": "Leerer Wert",
45 | "string_exceed_min_length": "Muss mindestens {0} Zeichen enthalten",
46 | "string_exceed_max_length": "Darf nicht mehr als {0} Zeichen enthalten",
47 | "string_invalid_format": "Ungültiges Format ({0})",
48 | "number_nan": "Keine Zahl",
49 | "number_not_integer": "Keine Ganzzahl",
50 | "number_not_double": "Keine Dezimalzahl",
51 | "number_exceed_min": "Muss größer als {0} sein",
52 | "number_exceed_max": "Muss kleiner als {0} sein",
53 | "number_wrong_step": "Muss ein Vielfaches von {0} sein",
54 | "datetime_invalid": "Ungültiges Datumsformat ({0})",
55 | "datetime_exceed_min": "Muss nach dem {0} sein",
56 | "datetime_exceed_max": "Muss vor dem {0} sein"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/i18n/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Greek (el)",
3 | "__author": "Stelios Patsatzis, https://www.linkedin.com/in/stelios-patsatzis-89841561",
4 | "add_rule": "Προσθήκη Συνθήκης",
5 | "add_group": "Προσθήκη Ομάδας",
6 | "delete_rule": "Διαγραφή",
7 | "delete_group": "Διαγραφή",
8 | "conditions": {
9 | "AND": "Λογικό ΚΑΙ",
10 | "OR": "Λογικό Η"
11 | },
12 | "operators": {
13 | "equal": "Ισούται με",
14 | "not_equal": "Διάφορο από ",
15 | "in": "Περιέχει",
16 | "not_in": "Δεν Περιέχει",
17 | "less": "Λιγότερο από",
18 | "less_or_equal": "Λιγότερο ή Ίσο",
19 | "greater": "Μεγαλύτερο από",
20 | "greater_or_equal": "Μεγαλύτερο ή Ίσο",
21 | "between": "Μεταξύ",
22 | "not_between": "Εκτός",
23 | "begins_with": "Αρχίζει με",
24 | "not_begins_with": "Δεν αρχίζει με",
25 | "contains": "Περιέχει",
26 | "not_contains": "Δεν περιέχει",
27 | "ends_with": "Τελειώνει σε",
28 | "not_ends_with": "Δεν τελειώνει σε",
29 | "is_empty": "Είναι άδειο",
30 | "is_not_empty": "Δεν είναι άδειο",
31 | "is_null": "Είναι NULL",
32 | "is_not_null": "Δεν είναι NULL"
33 | },
34 | "errors": {
35 | "no_filter": "Χωρίς φίλτρα",
36 | "empty_group": "Άδεια ομάδα",
37 | "radio_empty": "Χωρίς τιμή",
38 | "checkbox_empty": "Χωρίς τιμή",
39 | "select_empty": "Χωρίς τιμή",
40 | "string_empty": "Χωρίς τιμή",
41 | "string_exceed_min_length": "Ελάχιστο όριο {0} χαρακτήρων",
42 | "string_exceed_max_length": "Μέγιστο όριο {0} χαρακτήρων",
43 | "string_invalid_format": "Λανθασμένη μορφή ({0})",
44 | "number_nan": "Δεν είναι αριθμός",
45 | "number_not_integer": "Δεν είναι ακέραιος αριθμός",
46 | "number_not_double": "Δεν είναι πραγματικός αριθμός",
47 | "number_exceed_min": "Πρέπει να είναι μεγαλύτερο απο {0}",
48 | "number_exceed_max": "Πρέπει να είναι μικρότερο απο {0}",
49 | "number_wrong_step": "Πρέπει να είναι πολλαπλάσιο του {0}",
50 | "datetime_empty": "Χωρίς τιμή",
51 | "datetime_invalid": "Λανθασμένη μορφή ημερομηνίας ({0})",
52 | "datetime_exceed_min": "Νεότερο από {0}",
53 | "datetime_exceed_max": "Παλαιότερο από {0}",
54 | "boolean_not_valid": "Δεν είναι BOOLEAN",
55 | "operator_not_multiple": "Η συνθήκη \"{1}\" δεν μπορεί να δεχθεί πολλαπλές τιμές"
56 | }
57 | }
--------------------------------------------------------------------------------
/src/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "English (en)",
3 | "__author": "Damien \"Mistic\" Sorel, http://www.strangeplanet.fr",
4 |
5 | "add_rule": "Add rule",
6 | "add_group": "Add group",
7 | "delete_rule": "Delete",
8 | "delete_group": "Delete",
9 |
10 | "conditions": {
11 | "AND": "AND",
12 | "OR": "OR"
13 | },
14 |
15 | "operators": {
16 | "equal": "equal",
17 | "not_equal": "not equal",
18 | "in": "in",
19 | "not_in": "not in",
20 | "less": "less",
21 | "less_or_equal": "less or equal",
22 | "greater": "greater",
23 | "greater_or_equal": "greater or equal",
24 | "between": "between",
25 | "not_between": "not between",
26 | "begins_with": "begins with",
27 | "not_begins_with": "doesn't begin with",
28 | "contains": "contains",
29 | "not_contains": "doesn't contain",
30 | "ends_with": "ends with",
31 | "not_ends_with": "doesn't end with",
32 | "is_empty": "is empty",
33 | "is_not_empty": "is not empty",
34 | "is_null": "is null",
35 | "is_not_null": "is not null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "No filter selected",
40 | "empty_group": "The group is empty",
41 | "radio_empty": "No value selected",
42 | "checkbox_empty": "No value selected",
43 | "select_empty": "No value selected",
44 | "string_empty": "Empty value",
45 | "string_exceed_min_length": "Must contain at least {0} characters",
46 | "string_exceed_max_length": "Must not contain more than {0} characters",
47 | "string_invalid_format": "Invalid format ({0})",
48 | "number_nan": "Not a number",
49 | "number_not_integer": "Not an integer",
50 | "number_not_double": "Not a real number",
51 | "number_exceed_min": "Must be greater than {0}",
52 | "number_exceed_max": "Must be lower than {0}",
53 | "number_wrong_step": "Must be a multiple of {0}",
54 | "number_between_invalid": "Invalid values, {0} is greater than {1}",
55 | "datetime_empty": "Empty value",
56 | "datetime_invalid": "Invalid date format ({0})",
57 | "datetime_exceed_min": "Must be after {0}",
58 | "datetime_exceed_max": "Must be before {0}",
59 | "datetime_between_invalid": "Invalid values, {0} is greater than {1}",
60 | "boolean_not_valid": "Not a boolean",
61 | "operator_not_multiple": "Operator \"{1}\" cannot accept multiple values"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/eo.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Esperanto (eo)",
3 | "__author": "Robin van der Vliet, https://robinvandervliet.com/",
4 |
5 | "add_rule": "Aldoni regulon",
6 | "add_group": "Aldoni grupon",
7 | "delete_rule": "Forigi",
8 | "delete_group": "Forigi",
9 |
10 | "conditions": {
11 | "AND": "KAJ",
12 | "OR": "AŬ"
13 | },
14 |
15 | "operators": {
16 | "equal": "estas egala al",
17 | "not_equal": "ne estas egala al",
18 | "in": "estas en",
19 | "not_in": "ne estas en",
20 | "less": "estas malpli ol",
21 | "less_or_equal": "estas malpli ol aŭ egala al",
22 | "greater": "estas pli ol",
23 | "greater_or_equal": "estas pli ol aŭ egala al",
24 | "between": "estas inter",
25 | "not_between": "ne estas inter",
26 | "begins_with": "komenciĝas per",
27 | "not_begins_with": "ne komenciĝas per",
28 | "contains": "enhavas",
29 | "not_contains": "ne enhavas",
30 | "ends_with": "finiĝas per",
31 | "not_ends_with": "ne finiĝas per",
32 | "is_empty": "estas malplena",
33 | "is_not_empty": "ne estas malplena",
34 | "is_null": "estas senvalora",
35 | "is_not_null": "ne estas senvalora"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Neniu filtrilo elektita",
40 | "empty_group": "La grupo estas malplena",
41 | "radio_empty": "Neniu valoro elektita",
42 | "checkbox_empty": "Neniu valoro elektita",
43 | "select_empty": "Neniu valoro elektita",
44 | "string_empty": "Malplena valoro",
45 | "string_exceed_min_length": "Devas enhavi pli ol {0} signojn",
46 | "string_exceed_max_length": "Devas ne enhavi pli ol {0} signojn",
47 | "string_invalid_format": "Nevalida strukturo ({0})",
48 | "number_nan": "Ne estas nombro",
49 | "number_not_integer": "Ne estas entjera nombro",
50 | "number_not_double": "Ne estas reela nombro",
51 | "number_exceed_min": "Devas esti pli ol {0}",
52 | "number_exceed_max": "Devas esti malpli ol {0}",
53 | "number_wrong_step": "Devas esti oblo de {0}",
54 | "number_between_invalid": "Nevalidaj valoroj, {0} estas pli ol {1}",
55 | "datetime_empty": "Malplena valoro",
56 | "datetime_invalid": "Nevalida dato ({0})",
57 | "datetime_exceed_min": "Devas esti post {0}",
58 | "datetime_exceed_max": "Devas esti antaŭ {0}",
59 | "datetime_between_invalid": "Nevalidaj valoroj, {0} estas post {1}",
60 | "boolean_not_valid": "Ne estas bulea valoro",
61 | "operator_not_multiple": "La operacio \"{1}\" ne akceptas plurajn valorojn"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Spanish (es)",
3 | "__author": "\"pyarza\", \"kddlb\"",
4 |
5 | "add_rule": "Añadir regla",
6 | "add_group": "Añadir grupo",
7 | "delete_rule": "Borrar",
8 | "delete_group": "Borrar",
9 |
10 | "conditions": {
11 | "AND": "Y",
12 | "OR": "O"
13 | },
14 |
15 | "operators": {
16 | "equal": "igual",
17 | "not_equal": "distinto",
18 | "in": "en",
19 | "not_in": "no en",
20 | "less": "menor",
21 | "less_or_equal": "menor o igual",
22 | "greater": "mayor",
23 | "greater_or_equal": "mayor o igual",
24 | "between": "entre",
25 | "not_between": "no está entre",
26 | "begins_with": "empieza por",
27 | "not_begins_with": "no empieza por",
28 | "contains": "contiene",
29 | "not_contains": "no contiene",
30 | "ends_with": "acaba con",
31 | "not_ends_with": "no acaba con",
32 | "is_empty": "está vacío",
33 | "is_not_empty": "no está vacío",
34 | "is_null": "es nulo",
35 | "is_not_null": "no es nulo"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "No se ha seleccionado ningún filtro",
40 | "empty_group": "El grupo está vacío",
41 | "radio_empty": "Ningún valor seleccionado",
42 | "checkbox_empty": "Ningún valor seleccionado",
43 | "select_empty": "Ningún valor seleccionado",
44 | "string_empty": "Cadena vacía",
45 | "string_exceed_min_length": "Debe contener al menos {0} caracteres",
46 | "string_exceed_max_length": "No debe contener más de {0} caracteres",
47 | "string_invalid_format": "Formato inválido ({0})",
48 | "number_nan": "No es un número",
49 | "number_not_integer": "No es un número entero",
50 | "number_not_double": "No es un número real",
51 | "number_exceed_min": "Debe ser mayor que {0}",
52 | "number_exceed_max": "Debe ser menor que {0}",
53 | "number_wrong_step": "Debe ser múltiplo de {0}",
54 | "datetime_invalid": "Formato de fecha inválido ({0})",
55 | "datetime_exceed_min": "Debe ser posterior a {0}",
56 | "datetime_exceed_max": "Debe ser anterior a {0}",
57 | "number_between_invalid": "Valores Inválidos, {0} es mayor que {1}",
58 | "datetime_empty": "Campo vacio",
59 | "datetime_between_invalid": "Valores Inválidos, {0} es mayor que {1}",
60 | "boolean_not_valid": "No es booleano",
61 | "operator_not_multiple": "El operador \"{1}\" no puede aceptar valores multiples"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/fa-IR.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Farsi (fa-ir)",
3 | "__author": "Behzad Sedighzade, behzad.sedighzade@gmail.com",
4 |
5 | "add_rule": "افزودن قاعده",
6 | "add_group": "افزودن گروه",
7 | "delete_rule": "حذف قاعده",
8 | "delete_group": "حذف گروه",
9 |
10 | "conditions": {
11 | "AND": "و",
12 | "OR": "یا"
13 | },
14 |
15 | "operators": {
16 | "equal": "برابر با",
17 | "not_equal": "مخالف",
18 | "in": "شامل مجموعه شود",
19 | "not_in": "شامل مجموعه نشود",
20 | "less": "کمتر از",
21 | "less_or_equal": "کمتر یا مساوی با",
22 | "greater": "بزرگتر از",
23 | "greater_or_equal": "بزرگتر یا مساوی با",
24 | "between": "مابین",
25 | "not_between": "مابین نباشد",
26 | "begins_with": "شروع شود با",
27 | "not_begins_with": "شروع نشود با",
28 | "contains": "شامل شود",
29 | "not_contains": "شامل نشود",
30 | "ends_with": "خاتمه یابد با",
31 | "not_ends_with": "خاتمه نیابد با",
32 | "is_empty": "خالی باشد",
33 | "is_not_empty": "خالی نباشد",
34 | "is_null": "باشد ( null ) پوچ",
35 | "is_not_null": "نباشد( null ) پوچ "
36 | },
37 |
38 | "errors": {
39 | "no_filter": "هیچ قاعده ای انتخاب نشده است",
40 | "empty_group": "گروه خالی است",
41 | "radio_empty": "مقداری انتخاب نشده است",
42 | "checkbox_empty": "مقداری انتخاب نشده است",
43 | "select_empty": "مقداری انتخاب نشده است",
44 | "string_empty": "مقدار متنی خالی است",
45 | "string_exceed_min_length": "رشته حداقل باید {0} عدد حرف داشته باشد",
46 | "string_exceed_max_length": "رشته حداکثر {0} عدد حرف می تواند قبول کند",
47 | "string_invalid_format": "قالب رشته {0} نامعتبر ست",
48 | "number_nan": "عدد وارد کنید",
49 | "number_not_integer": "مقدار صحیح وارد کنید",
50 | "number_not_double": "مقدار اعشاری وارد کنید",
51 | "number_exceed_min": "باید از {0} بزرگتر باشد",
52 | "number_exceed_max": "باید از {0} کمتر باشد",
53 | "number_wrong_step": "باید مضربی از {0} باشد",
54 | "datetime_empty": "مقدار تاریخ خالی وارد شده!",
55 | "datetime_invalid": "قالب تاریخ ( {0} ) اشتباه است",
56 | "datetime_exceed_min": "باید بعد از {0} باشد",
57 | "datetime_exceed_max": "باید قبل از {0} باشد",
58 | "boolean_not_valid": "مقدار دودویی وارد کنید",
59 | "operator_not_multiple": "اپراتور \"{1}\" نمی تواند چند مقدار قبول کند"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/i18n/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "French (fr)",
3 | "__author": "Damien \"Mistic\" Sorel, http://www.strangeplanet.fr",
4 |
5 | "add_rule": "Ajouter une règle",
6 | "add_group": "Ajouter un groupe",
7 | "delete_rule": "Supprimer",
8 | "delete_group": "Supprimer",
9 |
10 | "conditions": {
11 | "AND": "ET",
12 | "OR": "OU"
13 | },
14 |
15 | "operators": {
16 | "equal": "est égal à",
17 | "not_equal": "n'est pas égal à",
18 | "in": "est compris dans",
19 | "not_in": "n'est pas compris dans",
20 | "less": "est inférieur à",
21 | "less_or_equal": "est inférieur ou égal à",
22 | "greater": "est supérieur à",
23 | "greater_or_equal": "est supérieur ou égal à",
24 | "between": "est entre",
25 | "not_between": "n'est pas entre",
26 | "begins_with": "commence par",
27 | "not_begins_with": "ne commence pas par",
28 | "contains": "contient",
29 | "not_contains": "ne contient pas",
30 | "ends_with": "finit par",
31 | "not_ends_with": "ne finit pas par",
32 | "is_empty": "est vide",
33 | "is_not_empty": "n'est pas vide",
34 | "is_null": "est nul",
35 | "is_not_null": "n'est pas nul"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Aucun filtre sélectionné",
40 | "empty_group": "Le groupe est vide",
41 | "radio_empty": "Pas de valeur selectionnée",
42 | "checkbox_empty": "Pas de valeur selectionnée",
43 | "select_empty": "Pas de valeur selectionnée",
44 | "string_empty": "Valeur vide",
45 | "string_exceed_min_length": "Doit contenir au moins {0} caractères",
46 | "string_exceed_max_length": "Ne doit pas contenir plus de {0} caractères",
47 | "string_invalid_format": "Format invalide ({0})",
48 | "number_nan": "N'est pas un nombre",
49 | "number_not_integer": "N'est pas un entier",
50 | "number_not_double": "N'est pas un nombre réel",
51 | "number_exceed_min": "Doit être plus grand que {0}",
52 | "number_exceed_max": "Doit être plus petit que {0}",
53 | "number_wrong_step": "Doit être un multiple de {0}",
54 | "number_between_invalid": "Valeurs invalides, {0} est plus grand que {1}",
55 | "datetime_empty": "Valeur vide",
56 | "datetime_invalid": "Fomat de date invalide ({0})",
57 | "datetime_exceed_min": "Doit être après {0}",
58 | "datetime_exceed_max": "Doit être avant {0}",
59 | "datetime_between_invalid": "Valeurs invalides, {0} est plus grand que {1}",
60 | "boolean_not_valid": "N'est pas un booléen",
61 | "operator_not_multiple": "L'opérateur \"{1}\" ne peut utiliser plusieurs valeurs"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/he.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Hebrew (he)",
3 | "__author": "Kfir Stri https://github.com/kfirstri",
4 |
5 | "add_rule": "הוסף כלל",
6 | "add_group": "הוסף קבוצה",
7 | "delete_rule": "מחק",
8 | "delete_group": "מחק",
9 |
10 | "conditions": {
11 | "AND": "וגם",
12 | "OR": "או"
13 | },
14 |
15 | "operators": {
16 | "equal": "שווה ל",
17 | "not_equal": "שונה מ",
18 | "in": "חלק מ",
19 | "not_in": "לא חלק מ",
20 | "less": "פחות מ",
21 | "less_or_equal": "פחות או שווה ל",
22 | "greater": "גדול מ",
23 | "greater_or_equal": "גדול או שווה ל",
24 | "between": "בין",
25 | "not_between": "לא בין",
26 | "begins_with": "מתחיל ב",
27 | "not_begins_with": "לא מתחיל ב",
28 | "contains": "מכיל",
29 | "not_contains": "לא מכיל",
30 | "ends_with": "מסתיים ב",
31 | "not_ends_with": "לא מסתיים ב",
32 | "is_empty": "ריק",
33 | "is_not_empty": "לא ריק",
34 | "is_null": "חסר ערך",
35 | "is_not_null": "לא חסר ערך"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "לא נבחרו מסננים",
40 | "empty_group": "הקבוצה רירקה",
41 | "radio_empty": "לא נבחר אף ערך",
42 | "checkbox_empty": "לא נבחר אף ערך",
43 | "select_empty": "לא נבחר אף ערך",
44 | "string_empty": "חסר ערך",
45 | "string_exceed_min_length": "המחרוזת חייבת להכיל לפחות {0} תווים",
46 | "string_exceed_max_length": "המחרוזת לא יכולה להכיל יותר מ{0} תווים",
47 | "string_invalid_format": "המחרוזת בפורמט שגוי ({0})",
48 | "number_nan": "זהו לא מספר",
49 | "number_not_integer": "המספר אינו מספר שלם",
50 | "number_not_double": "המספר אינו מספר עשרוני",
51 | "number_exceed_min": "המספר צריך להיות גדול מ {0}",
52 | "number_exceed_max": "המספר צריך להיות קטן מ{0}",
53 | "number_wrong_step": "המספר צריך להיות כפולה של {0}",
54 | "datetime_empty": "תאריך ריק",
55 | "datetime_invalid": "פורמט תאריך שגוי ({0})",
56 | "datetime_exceed_min": "התאריך חייב להיות אחרי {0}",
57 | "datetime_exceed_max": "התאריך חייב להיות לפני {0}",
58 | "boolean_not_valid": "זהו לא בוליאני",
59 | "operator_not_multiple": "האופרטור \"{1}\" לא יכול לקבל ערכים מרובים"
60 | }
61 | }
--------------------------------------------------------------------------------
/src/i18n/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Hungarian - Magyar (hu)",
3 | "__author": "Szabó Attila \"Tailor993\", https://www.tailor993.hu",
4 |
5 | "add_rule": "Feltétel hozzáadása",
6 | "add_group": "Csoport hozzáadása",
7 | "delete_rule": "Feltétel törlése",
8 | "delete_group": "Csoport törlése",
9 |
10 | "conditions": {
11 | "AND": "ÉS",
12 | "OR": "VAGY"
13 | },
14 |
15 | "operators": {
16 | "equal": "egyenlő",
17 | "not_equal": "nem egyenlő",
18 | "in": "bennevan",
19 | "not_in": "nincs benne",
20 | "less": "kisebb",
21 | "less_or_equal": "kisebb vagy egyenlő",
22 | "greater": "nagyobb",
23 | "greater_or_equal": "nagyobb vagy egyenlő",
24 | "between": "közötte",
25 | "not_between": "nincs közötte",
26 | "begins_with": "ezzel kezdődik",
27 | "not_begins_with": "ezzel nem kezdődik",
28 | "contains": "tartalmazza",
29 | "not_contains": "nem tartalmazza",
30 | "ends_with": "erre végződik",
31 | "not_ends_with": "errre nem végződik",
32 | "is_empty": "üres",
33 | "is_not_empty": "nem üres",
34 | "is_null": "null",
35 | "is_not_null": "nem null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nincs kiválasztott feltétel",
40 | "empty_group": "A csoport üres",
41 | "radio_empty": "Nincs kiválasztott érték",
42 | "checkbox_empty": "Nincs kiválasztott érték",
43 | "select_empty": "Nincs kiválasztott érték",
44 | "string_empty": "Üres érték",
45 | "string_exceed_min_length": "A megadott szöveg rövidebb a várt {0} karakternél",
46 | "string_exceed_max_length": "A megadott szöveg nem tartalmazhat többet, mint {0} karaktert",
47 | "string_invalid_format": "Nem megfelelő formátum ({0})",
48 | "number_nan": "Nem szám",
49 | "number_not_integer": "Nem egész szám (integer)",
50 | "number_not_double": "Nem valós szám",
51 | "number_exceed_min": "Nagyobbnak kell lennie, mint {0}",
52 | "number_exceed_max": "Kisebbnek kell lennie, mint {0}",
53 | "number_wrong_step": "{0} többszörösének kell lennie.",
54 | "number_between_invalid": "INem megfelelő érték, {0} nagyobb, mint {1}",
55 | "datetime_empty": "Üres érték",
56 | "datetime_invalid": "nem megfelelő dátum formátum ({0})",
57 | "datetime_exceed_min": "A dátumnak későbbinek kell lennie, mint{0}",
58 | "datetime_exceed_max": "A dátumnak korábbinak kell lennie, mint {0}",
59 | "datetime_between_invalid": "Nem megfelelő értékek, {0} nagyobb, mint {1}",
60 | "boolean_not_valid": "Nem igaz/hamis (boolean)",
61 | "operator_not_multiple": "Ez a művelet: \"{1}\" nem fogadhat el több értéket"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Italian (it)",
3 | "__author": "davegraziosi, Giuseppe Lodi Rizzini",
4 |
5 | "add_rule": "Aggiungi regola",
6 | "add_group": "Aggiungi gruppo",
7 | "delete_rule": "Elimina",
8 | "delete_group": "Elimina",
9 |
10 | "conditions": {
11 | "AND": "E",
12 | "OR": "O"
13 | },
14 |
15 | "operators": {
16 | "equal": "uguale",
17 | "not_equal": "non uguale",
18 | "in": "in",
19 | "not_in": "non in",
20 | "less": "minore",
21 | "less_or_equal": "minore o uguale",
22 | "greater": "maggiore",
23 | "greater_or_equal": "maggiore o uguale",
24 | "between" : "compreso tra",
25 | "not_between" : "non compreso tra",
26 | "begins_with": "inizia con",
27 | "not_begins_with": "non inizia con",
28 | "contains": "contiene",
29 | "not_contains": "non contiene",
30 | "ends_with": "finisce con",
31 | "not_ends_with": "non finisce con",
32 | "is_empty": "è vuoto",
33 | "is_not_empty": "non è vuoto",
34 | "is_null": "è nullo",
35 | "is_not_null": "non è nullo"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nessun filtro selezionato",
40 | "empty_group": "Il gruppo è vuoto",
41 | "radio_empty": "No value selected",
42 | "checkbox_empty": "Nessun valore selezionato",
43 | "select_empty": "Nessun valore selezionato",
44 | "string_empty": "Valore vuoto",
45 | "string_exceed_min_length": "Deve contenere almeno {0} caratteri",
46 | "string_exceed_max_length": "Non deve contenere più di {0} caratteri",
47 | "string_invalid_format": "Formato non valido ({0})",
48 | "number_nan": "Non è un numero",
49 | "number_not_integer": "Non è un intero",
50 | "number_not_double": "Non è un numero con la virgola",
51 | "number_exceed_min": "Deve essere maggiore di {0}",
52 | "number_exceed_max": "Deve essere minore di {0}",
53 | "number_wrong_step": "Deve essere multiplo di {0}",
54 | "number_between_invalid": "Valori non validi, {0} è maggiore di {1}",
55 | "datetime_empty": "Valore vuoto",
56 | "datetime_invalid": "Formato data non valido ({0})",
57 | "datetime_exceed_min": "Deve essere successivo a {0}",
58 | "datetime_exceed_max": "Deve essere precedente a {0}",
59 | "datetime_between_invalid": "Valori non validi, {0} è maggiore di {1}",
60 | "boolean_not_valid": "Non è un booleano",
61 | "operator_not_multiple": "L'Operatore {0} non può accettare valori multipli"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/lt.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Lithuanian (lt)",
3 | "__author": "Dalius Guzauskas (aka Tichij), https://lt.linkedin.com/in/daliusg",
4 |
5 | "add_rule": "Pridėti taisyklę",
6 | "add_group": "Pridėti grupę",
7 | "delete_rule": "Ištrinti",
8 | "delete_group": "Ištrinti",
9 |
10 | "conditions": {
11 | "AND": "IR",
12 | "OR": "ARBA"
13 | },
14 |
15 | "operators": {
16 | "equal": "lygu",
17 | "not_equal": "nėra lygu",
18 | "in": "iš nurodytų",
19 | "not_in": "ne iš nurodytų",
20 | "less": "mažiau",
21 | "less_or_equal": "mažiau arba lygu",
22 | "greater": "daugiau",
23 | "greater_or_equal": "daugiau arba lygu",
24 | "between": "tarp",
25 | "not_between": "nėra tarp",
26 | "begins_with": "prasideda",
27 | "not_begins_with": "neprasideda",
28 | "contains": "turi",
29 | "not_contains": "neturi",
30 | "ends_with": "baigiasi",
31 | "not_ends_with": "nesibaigia",
32 | "is_empty": "tuščia",
33 | "is_not_empty": "ne tuščia",
34 | "is_null": "neapibrėžta",
35 | "is_not_null": "nėra neapibrėžta"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nepasirinktas filtras",
40 | "empty_group": "Grupė tuščia",
41 | "radio_empty": "Nepasirinkta reikšmė",
42 | "checkbox_empty": "Nepasirinkta reikšmė",
43 | "select_empty": "Nepasirinkta reikšmė",
44 | "string_empty": "Tuščia reikšmė",
45 | "string_exceed_min_length": "Turi būti bent {0} simbolių",
46 | "string_exceed_max_length": "Turi būti ne daugiau kaip {0} simbolių",
47 | "string_invalid_format": "Klaidingas formatas ({0})",
48 | "number_nan": "Nėra skaičius",
49 | "number_not_integer": "Ne sveikasis skaičius",
50 | "number_not_double": "Ne realusis skaičius",
51 | "number_exceed_min": "Turi būti daugiau už {0}",
52 | "number_exceed_max": "Turi būti mažiau už {0}",
53 | "number_wrong_step": "Turi būti {0} kartotinis",
54 | "number_between_invalid": "Klaidingos reikšmės, {0} yra daugiau už {1}",
55 | "datetime_empty": "Tuščia reikšmė",
56 | "datetime_invalid": "Klaidingas datos formatas ({0})",
57 | "datetime_exceed_min": "Turi būti po {0}",
58 | "datetime_exceed_max": "Turi būti prieš {0}",
59 | "datetime_between_invalid": "Klaidingos reikšmės, {0} yra daugiau už {1}",
60 | "boolean_not_valid": "Nėra loginis tipas",
61 | "operator_not_multiple": "Operatorius \"{1}\" negali priimti kelių reikšmių"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/nl.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Dutch (nl)",
3 | "__author": "\"Roywcm\"",
4 |
5 | "add_rule": "Nieuwe regel",
6 | "add_group": "Nieuwe groep",
7 | "delete_rule": "Verwijder",
8 | "delete_group": "Verwijder",
9 |
10 | "conditions": {
11 | "AND": "EN",
12 | "OR": "OF"
13 | },
14 |
15 | "operators": {
16 | "equal": "gelijk",
17 | "not_equal": "niet gelijk",
18 | "in": "in",
19 | "not_in": "niet in",
20 | "less": "minder",
21 | "less_or_equal": "minder of gelijk",
22 | "greater": "groter",
23 | "greater_or_equal": "groter of gelijk",
24 | "between": "tussen",
25 | "not_between": "niet tussen",
26 | "begins_with": "begint met",
27 | "not_begins_with": "begint niet met",
28 | "contains": "bevat",
29 | "not_contains": "bevat niet",
30 | "ends_with": "eindigt met",
31 | "not_ends_with": "eindigt niet met",
32 | "is_empty": "is leeg",
33 | "is_not_empty": "is niet leeg",
34 | "is_null": "is null",
35 | "is_not_null": "is niet null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Geen filter geselecteerd",
40 | "empty_group": "De groep is leeg",
41 | "radio_empty": "Geen waarde geselecteerd",
42 | "checkbox_empty": "Geen waarde geselecteerd",
43 | "select_empty": "Geen waarde geselecteerd",
44 | "string_empty": "Lege waarde",
45 | "string_exceed_min_length": "Dient minstens {0} karakters te bevatten",
46 | "string_exceed_max_length": "Dient niet meer dan {0} karakters te bevatten",
47 | "string_invalid_format": "Ongeldig format ({0})",
48 | "number_nan": "Niet een nummer",
49 | "number_not_integer": "Geen geheel getal",
50 | "number_not_double": "Geen echt nummer",
51 | "number_exceed_min": "Dient groter te zijn dan {0}",
52 | "number_exceed_max": "Dient lager te zijn dan {0}",
53 | "number_wrong_step": "Dient een veelvoud te zijn van {0}",
54 | "datetime_invalid": "Ongeldige datumformat ({0})",
55 | "datetime_exceed_min": "Dient na {0}",
56 | "datetime_exceed_max": "Dient voor {0}"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/i18n/no.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Norwegian (no)",
3 | "__author": "Jna Borup Coyle, github@coyle.dk",
4 |
5 | "add_rule": "Legg til regel",
6 | "add_group": "Legg til gruppe",
7 | "delete_rule": "Slett regel",
8 | "delete_group": "Slett gruppe",
9 |
10 | "conditions": {
11 | "AND": "OG",
12 | "OR": "ELLER"
13 | },
14 |
15 | "operators": {
16 | "equal": "er lik",
17 | "not_equal": "er ikke lik",
18 | "in": "finnes i",
19 | "not_in": "finnes ikke i",
20 | "less": "er mindre enn",
21 | "less_or_equal": "er mindre eller lik",
22 | "greater": "er større enn",
23 | "greater_or_equal": "er større eller lik",
24 | "begins_with": "begynner med",
25 | "not_begins_with": "begynner ikke med",
26 | "contains": "inneholder",
27 | "not_contains": "inneholder ikke",
28 | "ends_with": "slutter med",
29 | "not_ends_with": "slutter ikke med",
30 | "is_empty": "er tom",
31 | "is_not_empty": "er ikke tom",
32 | "is_null": "er null",
33 | "is_not_null": "er ikke null"
34 | }
35 | }
--------------------------------------------------------------------------------
/src/i18n/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Polish (pl)",
3 | "__author": "Artur Smolarek",
4 |
5 | "add_rule": "Dodaj regułę",
6 | "add_group": "Dodaj grupę",
7 | "delete_rule": "Usuń",
8 | "delete_group": "Usuń",
9 |
10 | "conditions": {
11 | "AND": "ORAZ",
12 | "OR": "LUB"
13 | },
14 |
15 | "operators": {
16 | "equal": "równa się",
17 | "not_equal": "jest różne od",
18 | "in": "zawiera",
19 | "not_in": "nie zawiera",
20 | "less": "mniejsze",
21 | "less_or_equal": "mniejsze lub równe",
22 | "greater": "większe",
23 | "greater_or_equal": "większe lub równe",
24 | "between": "pomiędzy",
25 | "not_between": "nie jest pomiędzy",
26 | "begins_with": "rozpoczyna się od",
27 | "not_begins_with": "nie rozpoczyna się od",
28 | "contains": "zawiera",
29 | "not_contains": "nie zawiera",
30 | "ends_with": "kończy się na",
31 | "not_ends_with": "nie kończy się na",
32 | "is_empty": "jest puste",
33 | "is_not_empty": "nie jest puste",
34 | "is_null": "jest niezdefiniowane",
35 | "is_not_null": "nie jest niezdefiniowane"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nie wybrano żadnego filtra",
40 | "empty_group": "Grupa jest pusta",
41 | "radio_empty": "Nie wybrano wartości",
42 | "checkbox_empty": "Nie wybrano wartości",
43 | "select_empty": "Nie wybrano wartości",
44 | "string_empty": "Nie wpisano wartości",
45 | "string_exceed_min_length": "Minimalna długość to {0} znaków",
46 | "string_exceed_max_length": "Maksymalna długość to {0} znaków",
47 | "string_invalid_format": "Nieprawidłowy format ({0})",
48 | "number_nan": "To nie jest liczba",
49 | "number_not_integer": "To nie jest liczba całkowita",
50 | "number_not_double": "To nie jest liczba rzeczywista",
51 | "number_exceed_min": "Musi być większe niż {0}",
52 | "number_exceed_max": "Musi być mniejsze niż {0}",
53 | "number_wrong_step": "Musi być wielokrotnością {0}",
54 | "datetime_empty": "Nie wybrano wartości",
55 | "datetime_invalid": "Nieprawidłowy format daty ({0})",
56 | "datetime_exceed_min": "Musi być po {0}",
57 | "datetime_exceed_max": "Musi być przed {0}",
58 | "boolean_not_valid": "Niepoprawna wartość logiczna",
59 | "operator_not_multiple": "Operator \"{1}\" nie przyjmuje wielu wartości"
60 | }
61 | }
--------------------------------------------------------------------------------
/src/i18n/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Brazilian Portuguese (pr-BR)",
3 | "__author": "Leandro Gehlen, leandrogehlen@gmail.com; Marcos Ferretti, marcosvferretti@gmail.com",
4 |
5 | "add_rule": "Nova Regra",
6 | "add_group": "Novo Grupo",
7 | "delete_rule": "Excluir",
8 | "delete_group": "Excluir",
9 |
10 | "conditions": {
11 | "AND": "E",
12 | "OR": "OU"
13 | },
14 |
15 | "operators": {
16 | "equal": "Igual",
17 | "not_equal": "Diferente",
18 | "in": "Contido",
19 | "not_in": "Não contido",
20 | "less": "Menor",
21 | "less_or_equal": "Menor ou igual",
22 | "greater": "Maior",
23 | "greater_or_equal": "Maior ou igual",
24 | "between": "Entre",
25 | "not_between": "Não entre",
26 | "begins_with": "Iniciando com",
27 | "not_begins_with": "Não iniciando com",
28 | "contains": "Contém",
29 | "not_contains": "Não contém",
30 | "ends_with": "Terminando com",
31 | "not_ends_with": "Terminando sem",
32 | "is_empty": "É vazio",
33 | "is_not_empty": "Não é vazio",
34 | "is_null": "É nulo",
35 | "is_not_null": "Não é nulo"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nenhum filtro selecionado",
40 | "empty_group": "O grupo está vazio",
41 | "radio_empty": "Nenhum valor selecionado",
42 | "checkbox_empty": "Nenhum valor selecionado",
43 | "select_empty": "Nenhum valor selecionado",
44 | "string_empty": "Valor vazio",
45 | "string_exceed_min_length": "É necessário conter pelo menos {0} caracteres",
46 | "string_exceed_max_length": "É necessário conter mais de {0} caracteres",
47 | "string_invalid_format": "Formato inválido ({0})",
48 | "number_nan": "Não é um número",
49 | "number_not_integer": "Não é um número inteiro",
50 | "number_not_double": "Não é um número real",
51 | "number_exceed_min": "É necessário ser maior que {0}",
52 | "number_exceed_max": "É necessário ser menor que {0}",
53 | "number_wrong_step": "É necessário ser múltiplo de {0}",
54 | "datetime_invalid": "Formato de data inválido ({0})",
55 | "datetime_exceed_min": "É necessário ser superior a {0}",
56 | "datetime_exceed_max": "É necessário ser inferior a {0}",
57 | "datetime_empty": "Nenhuma data selecionada",
58 | "boolean_not_valid": "Não é um valor booleano",
59 | "operator_not_multiple": "O operador \"{1}\" não aceita valores múltiplos"
60 | }
61 | }
--------------------------------------------------------------------------------
/src/i18n/pt-PT.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Portuguese (pt-PT)",
3 | "__author": "Miguel Guerreiro, migas.csi@gmail.com",
4 |
5 | "add_rule": "Nova Regra",
6 | "add_group": "Novo Grupo",
7 | "delete_rule": "Excluir",
8 | "delete_group": "Excluir",
9 |
10 | "conditions": {
11 | "AND": "E",
12 | "OR": "OU"
13 | },
14 |
15 | "operators": {
16 | "equal": "Igual a",
17 | "not_equal": "Diferente de",
18 | "in": "Contido",
19 | "not_in": "Não contido",
20 | "less": "Menor que",
21 | "less_or_equal": "Menor ou igual a",
22 | "greater": "Maior que",
23 | "greater_or_equal": "Maior ou igual que",
24 | "between": "Entre",
25 | "begins_with": "Começar por",
26 | "not_begins_with": "Não a começar por",
27 | "contains": "Contém",
28 | "not_contains": "Não contém",
29 | "ends_with": "Terminando com",
30 | "not_ends_with": "Terminando sem",
31 | "is_empty": "É vazio",
32 | "is_not_empty": "Não é vazio",
33 | "is_null": "É nulo",
34 | "is_not_null": "Não é nulo"
35 | },
36 |
37 | "errors": {
38 | "no_filter": "Nenhum filtro selecionado",
39 | "empty_group": "O grupo está vazio",
40 | "radio_empty": "Nenhum valor selecionado",
41 | "checkbox_empty": "Nenhum valor selecionado",
42 | "select_empty": "Nenhum valor selecionado",
43 | "string_empty": "Valor vazio",
44 | "string_exceed_min_length": "É necessário conter pelo menos {0} caracteres",
45 | "string_exceed_max_length": "É necessário conter mais de {0} caracteres",
46 | "string_invalid_format": "Formato inválido ({0})",
47 | "number_nan": "Não é um número",
48 | "number_not_integer": "Não é um número inteiro",
49 | "number_not_double": "Não é um número real",
50 | "number_exceed_min": "É necessário ser maior que {0}",
51 | "number_exceed_max": "É necessário ser menor que {0}",
52 | "number_wrong_step": "É necessário ser múltiplo de {0}",
53 | "datetime_invalid": "Formato de data inválido ({0})",
54 | "datetime_exceed_min": "É necessário ser superior a {0}",
55 | "datetime_exceed_max": "É necessário ser inferior a {0}"
56 | }
57 | }
--------------------------------------------------------------------------------
/src/i18n/ro.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Romanian (ro)",
3 | "__author": "ArianServ, totpero",
4 |
5 | "add_rule": "Adaugă regulă",
6 | "add_group": "Adaugă grup",
7 | "delete_rule": "Şterge",
8 | "delete_group": "Şterge",
9 |
10 | "conditions": {
11 | "AND": "ŞI",
12 | "OR": "SAU"
13 | },
14 |
15 | "operators": {
16 | "equal": "egal",
17 | "not_equal": "diferit",
18 | "in": "în",
19 | "not_in": "nu în",
20 | "less": "mai mic",
21 | "less_or_equal": "mai mic sau egal",
22 | "greater": "mai mare",
23 | "greater_or_equal": "mai mare sau egal",
24 | "between": "între",
25 | "not_between": "nu între",
26 | "begins_with": "începe cu",
27 | "not_begins_with": "nu începe cu",
28 | "contains": "conţine",
29 | "not_contains": "nu conţine",
30 | "ends_with": "se termină cu",
31 | "not_ends_with": "nu se termină cu",
32 | "is_empty": "este gol",
33 | "is_not_empty": "nu este gol",
34 | "is_null": "e nul",
35 | "is_not_null": "nu e nul"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nici un filtru selectat",
40 | "empty_group": "Grupul este gol",
41 | "radio_empty": "Nici o valoare nu este selectată",
42 | "checkbox_empty": "Nici o valoare nu este selectată",
43 | "select_empty": "Nici o valoare nu este selectată",
44 | "string_empty": "Valoare goală",
45 | "string_exceed_min_length": "Trebuie să conţină mai puţin de {0} caractere",
46 | "string_exceed_max_length": "Trebuie să conţină mai mult de {0} caractere",
47 | "string_invalid_format": "Format invalid ({0})",
48 | "number_nan": "Nu este număr",
49 | "number_not_integer": "Nu este număr întreg",
50 | "number_not_double": "Nu este număr real",
51 | "number_exceed_min": "Trebuie să fie mai mare decât {0}",
52 | "number_exceed_max": "Trebuie să fie mai mic decât {0}",
53 | "number_wrong_step": "Trebuie să fie multiplu de {0}",
54 | "number_between_invalid": "Valori invalide, {0} este mai mare decât {1}",
55 | "datetime_empty": "Valoare goală",
56 | "datetime_invalid": "Format dată invalid ({0})",
57 | "datetime_exceed_min": "Trebuie să fie după {0}",
58 | "datetime_exceed_max": "Trebuie să fie înainte {0}",
59 | "datetime_between_invalid": "Valori invalide, {0} este mai mare decât {1}",
60 | "boolean_not_valid": "Nu este boolean",
61 | "operator_not_multiple": "Operatorul \"{1}\" nu poate accepta mai multe valori"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Russian (ru)",
3 |
4 | "add_rule": "Добавить",
5 | "add_group": "Добавить группу",
6 | "delete_rule": "Удалить",
7 | "delete_group": "Удалить",
8 |
9 | "conditions": {
10 | "AND": "И",
11 | "OR": "ИЛИ"
12 | },
13 |
14 | "operators": {
15 | "equal": "равно",
16 | "not_equal": "не равно",
17 | "in": "из указанных",
18 | "not_in": "не из указанных",
19 | "less": "меньше",
20 | "less_or_equal": "меньше или равно",
21 | "greater": "больше",
22 | "greater_or_equal": "больше или равно",
23 | "between": "между",
24 | "not_between": "не между",
25 | "begins_with": "начинается с",
26 | "not_begins_with": "не начинается с",
27 | "contains": "содержит",
28 | "not_contains": "не содержит",
29 | "ends_with": "оканчивается на",
30 | "not_ends_with": "не оканчивается на",
31 | "is_empty": "пустая строка",
32 | "is_not_empty": "не пустая строка",
33 | "is_null": "пусто",
34 | "is_not_null": "не пусто"
35 | },
36 |
37 | "errors": {
38 | "no_filter": "Фильтр не выбран",
39 | "empty_group": "Группа пуста",
40 | "radio_empty": "Не выбрано значение",
41 | "checkbox_empty": "Не выбрано значение",
42 | "select_empty": "Не выбрано значение",
43 | "string_empty": "Не заполнено",
44 | "string_exceed_min_length": "Должен содержать больше {0} символов",
45 | "string_exceed_max_length": "Должен содержать меньше {0} символов",
46 | "string_invalid_format": "Неверный формат ({0})",
47 | "number_nan": "Не число",
48 | "number_not_integer": "Не число",
49 | "number_not_double": "Не число",
50 | "number_exceed_min": "Должно быть больше {0}",
51 | "number_exceed_max": "Должно быть меньше, чем {0}",
52 | "number_wrong_step": "Должно быть кратно {0}",
53 | "number_between_invalid": "Недопустимые значения, {0} больше {1}",
54 | "datetime_empty": "Не заполнено",
55 | "datetime_invalid": "Неверный формат даты ({0})",
56 | "datetime_exceed_min": "Должно быть, после {0}",
57 | "datetime_exceed_max": "Должно быть, до {0}",
58 | "datetime_between_invalid": "Недопустимые значения, {0} больше {1}",
59 | "boolean_not_valid": "Не логическое",
60 | "operator_not_multiple": "Оператор \"{1}\" не поддерживает много значений"
61 | }
62 | }
--------------------------------------------------------------------------------
/src/i18n/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Slovensky (sk)",
3 | "__author": "k2s",
4 |
5 | "add_rule": "Pridať podmienku",
6 | "add_group": "Pridať skupinu",
7 | "delete_rule": "Zmazať",
8 | "delete_group": "Zmazať",
9 |
10 | "conditions": {
11 | "AND": "A",
12 | "OR": "ALEBO"
13 | },
14 |
15 | "operators": {
16 | "equal": "rovné",
17 | "not_equal": "nerovné",
18 | "in": "v",
19 | "not_in": "nie v",
20 | "less": "menej",
21 | "less_or_equal": "menej alebo rovné",
22 | "greater": "väčšie",
23 | "greater_or_equal": "väčšie alebo rovné",
24 | "between": "medzi",
25 | "not_between": "nie medzi",
26 | "begins_with": "začína na",
27 | "not_begins_with": "nezačína na",
28 | "contains": "obsahuje",
29 | "not_contains": "neobsahuje",
30 | "ends_with": "končí na",
31 | "not_ends_with": "nekončí na",
32 | "is_empty": "je prázdne",
33 | "is_not_empty": "nie je prázdne",
34 | "is_null": "je null",
35 | "is_not_null": "nie je null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Nie je zvolený filter",
40 | "empty_group": "Skupina je prázdna",
41 | "radio_empty": "Nie je označená hodnota",
42 | "checkbox_empty": "Nie je označená hodnota",
43 | "select_empty": "Nie je označená hodnota",
44 | "string_empty": "Prázdna hodnota",
45 | "string_exceed_min_length": "Musí obsahovať aspon {0} znakov",
46 | "string_exceed_max_length": "Nesmie obsahovať viac ako {0} znakov",
47 | "string_invalid_format": "Chybný formát ({0})",
48 | "number_nan": "Nie je číslo",
49 | "number_not_integer": "Nie je celé číslo",
50 | "number_not_double": "Nie je desatinné číslo",
51 | "number_exceed_min": "Musí byť väčšie ako {0}",
52 | "number_exceed_max": "Musí byť menšie ako {0}",
53 | "number_wrong_step": "Musí byť násobkom čísla {0}",
54 | "number_between_invalid": "Chybné hodnoty, {0} je väčšie ako {1}",
55 | "datetime_empty": "Prázdna hodnota",
56 | "datetime_invalid": "Chybný formát dátumu ({0})",
57 | "datetime_exceed_min": "Musí byť neskôr ako {0}",
58 | "datetime_exceed_max": "Musí byť skôr ako {0}",
59 | "datetime_between_invalid": "Chybné hodnoty, {0} je neskôr ako {1}",
60 | "boolean_not_valid": "Neplatné áno/nie",
61 | "operator_not_multiple": "Operátor '{1}' nepodporuje viacero hodnôt"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/sq.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Albanian (sq)",
3 | "__author": "Tomor Pupovci",
4 |
5 | "add_rule": "Shto rregull",
6 | "add_group": "Shto grup",
7 | "delete_rule": "Fshij",
8 | "delete_group": "Fshij",
9 |
10 | "conditions": {
11 | "AND": "DHE",
12 | "OR": "OSE"
13 | },
14 |
15 | "operators": {
16 | "equal": "barabartë",
17 | "not_equal": "e ndryshme prej",
18 | "in": "në",
19 | "not_in": "jo në",
20 | "less": "më e vogël",
21 | "less_or_equal": "më e vogël ose e barabartë me",
22 | "greater": "më e madhe",
23 | "greater_or_equal": "më e madhe ose e barabartë",
24 | "between": "në mes",
25 | "begins_with": "fillon me",
26 | "not_begins_with": "nuk fillon me",
27 | "contains": "përmban",
28 | "not_contains": "nuk përmban",
29 | "ends_with": "mbaron me",
30 | "not_ends_with": "nuk mbaron me",
31 | "is_empty": "është e zbrazët",
32 | "is_not_empty": "nuk është e zbrazët",
33 | "is_null": "është null",
34 | "is_not_null": "nuk është null"
35 | },
36 |
37 | "errors": {
38 | "no_filter": "Nuk ka filter të zgjedhur",
39 | "empty_group": "Grupi është i zbrazët",
40 | "radio_empty": "Nuk ka vlerë të zgjedhur",
41 | "checkbox_empty": "Nuk ka vlerë të zgjedhur",
42 | "select_empty": "Nuk ka vlerë të zgjedhur",
43 | "string_empty": "Vlerë e zbrazët",
44 | "string_exceed_min_length": "Duhet të përmbajë së paku {0} karaktere",
45 | "string_exceed_max_length": "Nuk duhet të përmbajë më shumë se {0} karaktere",
46 | "string_invalid_format": "Format i pasaktë ({0})",
47 | "number_nan": "Nuk është numër",
48 | "number_not_integer": "Nuk është numër i plotë",
49 | "number_not_double": "Nuk është numër me presje",
50 | "number_exceed_min": "Duhet të jetë më i madh se {0}",
51 | "number_exceed_max": "Duhet të jetë më i vogël se {0}",
52 | "number_wrong_step": "Duhet të jetë shumëfish i {0}",
53 | "datetime_empty": "Vlerë e zbrazët",
54 | "datetime_invalid": "Format i pasaktë i datës ({0})",
55 | "datetime_exceed_min": "Duhet të jetë pas {0}",
56 | "datetime_exceed_max": "Duhet të jetë para {0}",
57 | "boolean_not_valid": "Nuk është boolean",
58 | "operator_not_multiple": "Operatori \"{1}\" nuk mund të pranojë vlera të shumëfishta"
59 | }
60 | }
--------------------------------------------------------------------------------
/src/i18n/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Svenska (sv)",
3 | "__author": "hekin1",
4 |
5 | "add_rule": "Lägg till regel",
6 | "add_group": "Lägg till grupp",
7 | "delete_rule": "Ta bort",
8 | "delete_group": "Ta bort",
9 |
10 | "conditions": {
11 | "AND": "OCH",
12 | "OR": "ELLER"
13 | },
14 |
15 | "operators": {
16 | "equal": "lika med",
17 | "not_equal": "ej lika med",
18 | "in": "en av",
19 | "not_in": "ej en av",
20 | "less": "mindre",
21 | "less_or_equal": "mindre eller lika med",
22 | "greater": "större",
23 | "greater_or_equal": "större eller lika med",
24 | "between": "mellan",
25 | "not_between": "ej mellan",
26 | "begins_with": "börjar med",
27 | "not_begins_with": "börjar inte med",
28 | "contains": "innehåller",
29 | "not_contains": "innehåller inte",
30 | "ends_with": "slutar med",
31 | "not_ends_with": "slutar inte med",
32 | "is_empty": "är tom",
33 | "is_not_empty": "är inte tom",
34 | "is_null": "är null",
35 | "is_not_null": "är inte null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Inget filter valt",
40 | "empty_group": "Gruppen är tom",
41 | "radio_empty": "Inget värde valt",
42 | "checkbox_empty": "Inget värde valt",
43 | "select_empty": "Inget värde valt",
44 | "string_empty": "Tomt värde",
45 | "string_exceed_min_length": "Måste innehålla minst {0} tecken",
46 | "string_exceed_max_length": "Får ej innehålla fler än {0} tecken",
47 | "string_invalid_format": "Felaktigt format ({0})",
48 | "number_nan": "Inte numeriskt",
49 | "number_not_integer": "Inte en siffra",
50 | "number_not_double": "Inte ett decimaltal",
51 | "number_exceed_min": "Måste vara större än {0}",
52 | "number_exceed_max": "Måste vara lägre än {0}",
53 | "number_wrong_step": "Måste vara en mutipel av {0}",
54 | "number_between_invalid": "Felaktiga värden, {0} är större än {1}",
55 | "datetime_empty": "Tomt värde",
56 | "datetime_invalid": "Felaktigt datumformat ({0})",
57 | "datetime_exceed_min": "Måste vara efter {0}",
58 | "datetime_exceed_max": "Måste vara före {0}",
59 | "datetime_between_invalid": "Felaktiga värden, {0} är större än {1}",
60 | "boolean_not_valid": "Inte en boolean",
61 | "operator_not_multiple": "Operatorn \"{1}\" accepterar inte flera värden"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/sw.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Swahili (sw)",
3 | "__author": "Timothy Anyona",
4 |
5 | "add_rule": "Ongeza kanuni",
6 | "add_group": "Ongeza kikundi",
7 | "delete_rule": "Futa",
8 | "delete_group": "Futa",
9 |
10 | "conditions": {
11 | "AND": "NA",
12 | "OR": "AU"
13 | },
14 |
15 | "operators": {
16 | "equal": "ni",
17 | "not_equal": "sio",
18 | "in": "mojawapo ya",
19 | "not_in": "sio mojawapo ya",
20 | "less": "isiyozidi",
21 | "less_or_equal": "isiyozidi au ni sawa na",
22 | "greater": "inayozidi",
23 | "greater_or_equal": "inayozidi au ni sawa na",
24 | "between": "kati ya",
25 | "not_between": "isiyo kati ya",
26 | "begins_with": "inaanza na",
27 | "not_begins_with": "isiyoanza na",
28 | "contains": "ina",
29 | "not_contains": "haina",
30 | "ends_with": "inaisha na",
31 | "not_ends_with": "isiyoisha na",
32 | "is_empty": "ni tupu",
33 | "is_not_empty": "sio tupu",
34 | "is_null": "ni batili",
35 | "is_not_null": "sio batili"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Chujio halijachaguliwa",
40 | "empty_group": "Kikundi ki tupu",
41 | "radio_empty": "Thamani haijachaguliwa",
42 | "checkbox_empty": "Thamani haijachaguliwa",
43 | "select_empty": "Thamani haijachaguliwa",
44 | "string_empty": "Thamani tupu",
45 | "string_exceed_min_length": "Lazima iwe na vibambo visiopungua {0}",
46 | "string_exceed_max_length": "Haifai kuwa na vibambo zaidi ya {0}",
47 | "string_invalid_format": "Fomati batili ({0})",
48 | "number_nan": "Sio nambari",
49 | "number_not_integer": "Sio namba kamili",
50 | "number_not_double": "Sio namba desimali",
51 | "number_exceed_min": "Lazima iwe zaidi ya {0}",
52 | "number_exceed_max": "Lazima iwe chini ya {0}",
53 | "number_wrong_step": "Lazima iwe kigawe cha {0}",
54 | "number_between_invalid": "Thamani batili, {0} ni kubwa kuliko {1}",
55 | "datetime_empty": "Thamani tupu",
56 | "datetime_invalid": "Fomati tarehe batili ({0})",
57 | "datetime_exceed_min": "Lazima iwe baada ya {0}",
58 | "datetime_exceed_max": "Lazima iwe kabla ya {0}",
59 | "datetime_between_invalid": "Thamani batili, {0} ni baada ya {1}",
60 | "boolean_not_valid": "Sio buleani",
61 | "operator_not_multiple": "Opereta \"{1}\" haikubali thamani nyingi"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Turkish (tr)",
3 | "__author": "Aykut Alpgiray Ateş",
4 |
5 | "add_rule": "Kural Ekle",
6 | "add_group": "Grup Ekle",
7 | "delete_rule": "Sil",
8 | "delete_group": "Sil",
9 |
10 | "conditions": {
11 | "AND": "Ve",
12 | "OR": "Veya"
13 | },
14 |
15 | "operators": {
16 | "equal": "eşit",
17 | "not_equal": "eşit değil",
18 | "in": "içinde",
19 | "not_in": "içinde değil",
20 | "less": "küçük",
21 | "less_or_equal": "küçük veya eşit",
22 | "greater": "büyük",
23 | "greater_or_equal": "büyük veya eşit",
24 | "between": "arasında",
25 | "not_between": "arasında değil",
26 | "begins_with": "ile başlayan",
27 | "not_begins_with": "ile başlamayan",
28 | "contains": "içeren",
29 | "not_contains": "içermeyen",
30 | "ends_with": "ile biten",
31 | "not_ends_with": "ile bitmeyen",
32 | "is_empty": "boş ise",
33 | "is_not_empty": "boş değil ise",
34 | "is_null": "var ise",
35 | "is_not_null": "yok ise"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "Bir filtre seçili değil",
40 | "empty_group": "Grup bir eleman içermiyor",
41 | "radio_empty": "Seçim yapılmalı",
42 | "checkbox_empty": "Seçim yapılmalı",
43 | "select_empty": "Seçim yapılmalı",
44 | "string_empty": "Bir metin girilmeli",
45 | "string_exceed_min_length": "En az {0} karakter girilmeli",
46 | "string_exceed_max_length": "En fazla {0} karakter girilebilir",
47 | "string_invalid_format": "Uyumsuz format ({0})",
48 | "number_nan": "Sayı değil",
49 | "number_not_integer": "Tam sayı değil",
50 | "number_not_double": "Ondalıklı sayı değil",
51 | "number_exceed_min": "Sayı {0}'den/dan daha büyük olmalı",
52 | "number_exceed_max": "Sayı {0}'den/dan daha küçük olmalı",
53 | "number_wrong_step": "{0} veya katı olmalı",
54 | "number_between_invalid": "Geçersiz değerler, {0} değeri {1} değerinden büyük",
55 | "datetime_empty": "Tarih Seçilmemiş",
56 | "datetime_invalid": "Uygun olmayan tarih formatı ({0})",
57 | "datetime_exceed_min": "{0} Tarihinden daha sonrası olmalı.",
58 | "datetime_exceed_max": "{0} Tarihinden daha öncesi olmalı.",
59 | "datetime_between_invalid": "Geçersiz değerler, {0} değeri {1} değerinden büyük",
60 | "boolean_not_valid": "Değer Doğru/Yanlış(bool) olmalı",
61 | "operator_not_multiple": "Operatör \"{1}\" birden fazla değer kabul etmiyor"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/i18n/ua.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Ukrainian (ua)",
3 | "__author": "Megaplan, mborisv ",
4 |
5 | "add_rule": "Додати",
6 | "add_group": "Додати групу",
7 | "delete_rule": "Видалити",
8 | "delete_group": "Видалити",
9 | "conditions": {
10 | "AND": "І",
11 | "OR": "АБО"
12 | },
13 | "operators": {
14 | "equal": "дорівнює",
15 | "not_equal": "не дорівнює",
16 | "in": "з вказаних",
17 | "not_in": "не з вказаних",
18 | "less": "менше",
19 | "less_or_equal": "менше або дорівнюж",
20 | "greater": "більше",
21 | "greater_or_equal": "більше або дорівнює",
22 | "between": "між",
23 | "begins_with": "починається з",
24 | "not_begins_with": "не починається з",
25 | "contains": "містить",
26 | "not_contains": "не містить",
27 | "ends_with": "закінчується на",
28 | "not_ends_with": "не не закінчується на",
29 | "is_empty": "порожній рядок",
30 | "is_not_empty": "не порожній рядок",
31 | "is_null": "порожньо",
32 | "is_not_null": "не порожньо"
33 | },
34 | "errors": {
35 | "no_filter": "Фільтр не вибраний",
36 | "empty_group": "Група порожня",
37 | "radio_empty": "Значення не вибрано",
38 | "checkbox_empty": "Значення не вибрано",
39 | "select_empty": "Значення не вибрано",
40 | "string_empty": "Не заповнено",
41 | "string_exceed_min_length": "Повинен містити більше {0} символів",
42 | "string_exceed_max_length": "Повинен містити менше {0} символів",
43 | "string_invalid_format": "Невірний формат ({0})",
44 | "number_nan": "Не число",
45 | "number_not_integer": "Не число",
46 | "number_not_double": "Не число",
47 | "number_exceed_min": "Повинне бути більше {0}",
48 | "number_exceed_max": "Повинне бути менше, ніж {0}",
49 | "number_wrong_step": "Повинне бути кратне {0}",
50 | "datetime_empty": "Не заповнено",
51 | "datetime_invalid": "Невірний формат дати ({0})",
52 | "datetime_exceed_min": "Повинне бути, після {0}",
53 | "datetime_exceed_max": "Повинне бути, до {0}",
54 | "boolean_not_valid": "Не логічне",
55 | "operator_not_multiple": "Оператор \"{1}\" не підтримує багато значень"
56 | }
57 | }
--------------------------------------------------------------------------------
/src/i18n/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "__locale": "Simplified Chinese (zh_CN)",
3 | "__author": "shadowwind, shatteredwindgo@gmail.com",
4 |
5 | "add_rule": "添加规则",
6 | "add_group": "添加组",
7 | "delete_rule": "删除",
8 | "delete_group": "删除组",
9 |
10 | "conditions": {
11 | "AND": "和",
12 | "OR": "或"
13 | },
14 |
15 | "operators": {
16 | "equal": "等于",
17 | "not_equal": "不等于",
18 | "in": "在...之內",
19 | "not_in": "不在...之內",
20 | "less": "小于",
21 | "less_or_equal": "小于或等于",
22 | "greater": "大于",
23 | "greater_or_equal": "大于或等于",
24 | "between": "在...之间",
25 | "not_between": "不在...之间",
26 | "begins_with": "以...开始",
27 | "not_begins_with": "不以...开始",
28 | "contains": "包含以下内容",
29 | "not_contains": "不包含以下内容",
30 | "ends_with": "以...结束",
31 | "not_ends_with": "不以...结束",
32 | "is_empty": "为空",
33 | "is_not_empty": "不为空",
34 | "is_null": "为 null",
35 | "is_not_null": "不为 null"
36 | },
37 |
38 | "errors": {
39 | "no_filter": "没有选择过滤器",
40 | "empty_group": "该组为空",
41 | "radio_empty": "没有选中项",
42 | "checkbox_empty": "没有选中项",
43 | "select_empty": "没有选中项",
44 | "string_empty": "没有输入值",
45 | "string_exceed_min_length": "必须至少包含{0}个字符",
46 | "string_exceed_max_length": "必须不超过{0}个字符",
47 | "string_invalid_format": "无效格式({0})",
48 | "number_nan": "值不是数字",
49 | "number_not_integer": "不是整数",
50 | "number_not_double": "不是浮点数",
51 | "number_exceed_min": "必须大于{0}",
52 | "number_exceed_max": "必须小于{0}",
53 | "number_wrong_step": "必须是{0}的倍数",
54 | "datetime_empty": "值为空",
55 | "datetime_invalid": "不是有效日期({0})",
56 | "datetime_exceed_min": "必须在{0}之后",
57 | "datetime_exceed_max": "必须在{0}之前",
58 | "boolean_not_valid": "不是布尔值",
59 | "operator_not_multiple": "选项\"{1}\"无法接受多个值"
60 | }
61 | }
--------------------------------------------------------------------------------
/src/jquery.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The {@link http://learn.jquery.com/plugins/|jQuery Plugins} namespace
3 | * @external "jQuery.fn"
4 | */
5 |
6 | /**
7 | * Instanciates or accesses the {@link QueryBuilder} on an element
8 | * @function
9 | * @memberof external:"jQuery.fn"
10 | * @param {*} option - initial configuration or method name
11 | * @param {...*} args - method arguments
12 | *
13 | * @example
14 | * $('#builder').queryBuilder({ /** configuration object *\/ });
15 | * @example
16 | * $('#builder').queryBuilder('methodName', methodParam1, methodParam2);
17 | */
18 | $.fn.queryBuilder = function(option) {
19 | if (this.length === 0) {
20 | Utils.error('Config', 'No target defined');
21 | }
22 | if (this.length > 1) {
23 | Utils.error('Config', 'Unable to initialize on multiple target');
24 | }
25 |
26 | var data = this.data('queryBuilder');
27 | var options = (typeof option == 'object' && option) || {};
28 |
29 | if (!data && option == 'destroy') {
30 | return this;
31 | }
32 | if (!data) {
33 | var builder = new QueryBuilder(this, options);
34 | this.data('queryBuilder', builder);
35 | builder.init(options.rules);
36 | }
37 | if (typeof option == 'string') {
38 | return data[option].apply(data, Array.prototype.slice.call(arguments, 1));
39 | }
40 |
41 | return this;
42 | };
43 |
44 | /**
45 | * @function
46 | * @memberof external:"jQuery.fn"
47 | * @see QueryBuilder
48 | */
49 | $.fn.queryBuilder.constructor = QueryBuilder;
50 |
51 | /**
52 | * @function
53 | * @memberof external:"jQuery.fn"
54 | * @see QueryBuilder.defaults
55 | */
56 | $.fn.queryBuilder.defaults = QueryBuilder.defaults;
57 |
58 | /**
59 | * @function
60 | * @memberof external:"jQuery.fn"
61 | * @see QueryBuilder.defaults
62 | */
63 | $.fn.queryBuilder.extend = QueryBuilder.extend;
64 |
65 | /**
66 | * @function
67 | * @memberof external:"jQuery.fn"
68 | * @see QueryBuilder.define
69 | */
70 | $.fn.queryBuilder.define = QueryBuilder.define;
71 |
72 | /**
73 | * @function
74 | * @memberof external:"jQuery.fn"
75 | * @see QueryBuilder.regional
76 | */
77 | $.fn.queryBuilder.regional = QueryBuilder.regional;
78 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef {object} Filter
3 | * @memberof QueryBuilder
4 | * @description See {@link http://querybuilder.js.org/index.html#filters}
5 | */
6 |
7 | /**
8 | * @typedef {object} Operator
9 | * @memberof QueryBuilder
10 | * @description See {@link http://querybuilder.js.org/index.html#operators}
11 | */
12 |
13 | /**
14 | * @param {jQuery} $el
15 | * @param {object} options - see {@link http://querybuilder.js.org/#options}
16 | * @constructor
17 | */
18 | var QueryBuilder = function($el, options) {
19 | $el[0].queryBuilder = this;
20 |
21 | /**
22 | * Element container
23 | * @member {jQuery}
24 | * @readonly
25 | */
26 | this.$el = $el;
27 |
28 | /**
29 | * Configuration object
30 | * @member {object}
31 | * @readonly
32 | */
33 | this.settings = $.extendext(true, 'replace', {}, QueryBuilder.DEFAULTS, options);
34 |
35 | /**
36 | * Internal model
37 | * @member {Model}
38 | * @readonly
39 | */
40 | this.model = new Model();
41 |
42 | /**
43 | * Internal status
44 | * @member {object}
45 | * @property {string} id - id of the container
46 | * @property {boolean} generated_id - if the container id has been generated
47 | * @property {int} group_id - current group id
48 | * @property {int} rule_id - current rule id
49 | * @property {boolean} has_optgroup - if filters have optgroups
50 | * @property {boolean} has_operator_optgroup - if operators have optgroups
51 | * @readonly
52 | * @private
53 | */
54 | this.status = {
55 | id: null,
56 | generated_id: false,
57 | group_id: 0,
58 | rule_id: 0,
59 | has_optgroup: false,
60 | has_operator_optgroup: false
61 | };
62 |
63 | /**
64 | * List of filters
65 | * @member {QueryBuilder.Filter[]}
66 | * @readonly
67 | */
68 | this.filters = this.settings.filters;
69 |
70 | /**
71 | * List of icons
72 | * @member {object.}
73 | * @readonly
74 | */
75 | this.icons = this.settings.icons;
76 |
77 | /**
78 | * List of operators
79 | * @member {QueryBuilder.Operator[]}
80 | * @readonly
81 | */
82 | this.operators = this.settings.operators;
83 |
84 | /**
85 | * List of templates
86 | * @member {object.}
87 | * @readonly
88 | */
89 | this.templates = this.settings.templates;
90 |
91 | /**
92 | * Plugins configuration
93 | * @member {object.}
94 | * @readonly
95 | */
96 | this.plugins = this.settings.plugins;
97 |
98 | /**
99 | * Translations object
100 | * @member {object}
101 | * @readonly
102 | */
103 | this.lang = null;
104 |
105 | // translations : english << 'lang_code' << custom
106 | if (QueryBuilder.regional['en'] === undefined) {
107 | Utils.error('Config', '"i18n/en.js" not loaded.');
108 | }
109 | this.lang = $.extendext(true, 'replace', {}, QueryBuilder.regional['en'], QueryBuilder.regional[this.settings.lang_code], this.settings.lang);
110 |
111 | // "allow_groups" can be boolean or int
112 | if (this.settings.allow_groups === false) {
113 | this.settings.allow_groups = 0;
114 | }
115 | else if (this.settings.allow_groups === true) {
116 | this.settings.allow_groups = -1;
117 | }
118 |
119 | // init templates
120 | Object.keys(this.templates).forEach(function(tpl) {
121 | if (!this.templates[tpl]) {
122 | this.templates[tpl] = QueryBuilder.templates[tpl];
123 | }
124 | if (typeof this.templates[tpl] !== 'function') {
125 | throw new Error(`Template ${tpl} must be a function`);
126 | }
127 | }, this);
128 |
129 | // ensure we have a container id
130 | if (!this.$el.attr('id')) {
131 | this.$el.attr('id', 'qb_' + Math.floor(Math.random() * 99999));
132 | this.status.generated_id = true;
133 | }
134 | this.status.id = this.$el.attr('id');
135 |
136 | // INIT
137 | this.$el.addClass('query-builder');
138 |
139 | this.filters = this.checkFilters(this.filters);
140 | this.operators = this.checkOperators(this.operators);
141 | this.bindEvents();
142 | this.initPlugins();
143 | };
144 |
145 | $.extend(QueryBuilder.prototype, /** @lends QueryBuilder.prototype */ {
146 | /**
147 | * Triggers an event on the builder container
148 | * @param {string} type
149 | * @returns {$.Event}
150 | */
151 | trigger: function(type) {
152 | var event = new $.Event(this._tojQueryEvent(type), {
153 | builder: this
154 | });
155 |
156 | this.$el.triggerHandler(event, Array.prototype.slice.call(arguments, 1));
157 |
158 | return event;
159 | },
160 |
161 | /**
162 | * Triggers an event on the builder container and returns the modified value
163 | * @param {string} type
164 | * @param {*} value
165 | * @returns {*}
166 | */
167 | change: function(type, value) {
168 | var event = new $.Event(this._tojQueryEvent(type, true), {
169 | builder: this,
170 | value: value
171 | });
172 |
173 | this.$el.triggerHandler(event, Array.prototype.slice.call(arguments, 2));
174 |
175 | return event.value;
176 | },
177 |
178 | /**
179 | * Attaches an event listener on the builder container
180 | * @param {string} type
181 | * @param {function} cb
182 | * @returns {QueryBuilder}
183 | */
184 | on: function(type, cb) {
185 | this.$el.on(this._tojQueryEvent(type), cb);
186 | return this;
187 | },
188 |
189 | /**
190 | * Removes an event listener from the builder container
191 | * @param {string} type
192 | * @param {function} [cb]
193 | * @returns {QueryBuilder}
194 | */
195 | off: function(type, cb) {
196 | this.$el.off(this._tojQueryEvent(type), cb);
197 | return this;
198 | },
199 |
200 | /**
201 | * Attaches an event listener called once on the builder container
202 | * @param {string} type
203 | * @param {function} cb
204 | * @returns {QueryBuilder}
205 | */
206 | once: function(type, cb) {
207 | this.$el.one(this._tojQueryEvent(type), cb);
208 | return this;
209 | },
210 |
211 | /**
212 | * Appends `.queryBuilder` and optionally `.filter` to the events names
213 | * @param {string} name
214 | * @param {boolean} [filter=false]
215 | * @returns {string}
216 | * @private
217 | */
218 | _tojQueryEvent: function(name, filter) {
219 | return name.split(' ').map(function(type) {
220 | return type + '.queryBuilder' + (filter ? '.filter' : '');
221 | }).join(' ');
222 | }
223 | });
224 |
--------------------------------------------------------------------------------
/src/plugins.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module plugins
3 | */
4 |
5 | /**
6 | * Definition of available plugins
7 | * @type {object.}
8 | */
9 | QueryBuilder.plugins = {};
10 |
11 | /**
12 | * Gets or extends the default configuration
13 | * @param {object} [options] - new configuration
14 | * @returns {undefined|object} nothing or configuration object (copy)
15 | */
16 | QueryBuilder.defaults = function(options) {
17 | if (typeof options == 'object') {
18 | $.extendext(true, 'replace', QueryBuilder.DEFAULTS, options);
19 | }
20 | else if (typeof options == 'string') {
21 | if (typeof QueryBuilder.DEFAULTS[options] == 'object') {
22 | return $.extend(true, {}, QueryBuilder.DEFAULTS[options]);
23 | }
24 | else {
25 | return QueryBuilder.DEFAULTS[options];
26 | }
27 | }
28 | else {
29 | return $.extend(true, {}, QueryBuilder.DEFAULTS);
30 | }
31 | };
32 |
33 | /**
34 | * Registers a new plugin
35 | * @param {string} name
36 | * @param {function} fct - init function
37 | * @param {object} [def] - default options
38 | */
39 | QueryBuilder.define = function(name, fct, def) {
40 | QueryBuilder.plugins[name] = {
41 | fct: fct,
42 | def: def || {}
43 | };
44 | };
45 |
46 | /**
47 | * Adds new methods to QueryBuilder prototype
48 | * @param {object.} methods
49 | */
50 | QueryBuilder.extend = function(methods) {
51 | $.extend(QueryBuilder.prototype, methods);
52 | };
53 |
54 | /**
55 | * Initializes plugins for an instance
56 | * @throws ConfigError
57 | * @private
58 | */
59 | QueryBuilder.prototype.initPlugins = function() {
60 | if (!this.plugins) {
61 | return;
62 | }
63 |
64 | if ($.isArray(this.plugins)) {
65 | var tmp = {};
66 | this.plugins.forEach(function(plugin) {
67 | tmp[plugin] = null;
68 | });
69 | this.plugins = tmp;
70 | }
71 |
72 | Object.keys(this.plugins).forEach(function(plugin) {
73 | if (plugin in QueryBuilder.plugins) {
74 | this.plugins[plugin] = $.extend(true, {},
75 | QueryBuilder.plugins[plugin].def,
76 | this.plugins[plugin] || {}
77 | );
78 |
79 | QueryBuilder.plugins[plugin].fct.call(this, this.plugins[plugin]);
80 | }
81 | else {
82 | Utils.error('Config', 'Unable to find plugin "{0}"', plugin);
83 | }
84 | }, this);
85 | };
86 |
87 | /**
88 | * Returns the config of a plugin, if the plugin is not loaded, returns the default config.
89 | * @param {string} name
90 | * @param {string} [property]
91 | * @throws ConfigError
92 | * @returns {*}
93 | */
94 | QueryBuilder.prototype.getPluginOptions = function(name, property) {
95 | var plugin;
96 | if (this.plugins && this.plugins[name]) {
97 | plugin = this.plugins[name];
98 | }
99 | else if (QueryBuilder.plugins[name]) {
100 | plugin = QueryBuilder.plugins[name].def;
101 | }
102 |
103 | if (plugin) {
104 | if (property) {
105 | return plugin[property];
106 | }
107 | else {
108 | return plugin;
109 | }
110 | }
111 | else {
112 | Utils.error('Config', 'Unable to find plugin "{0}"', name);
113 | }
114 | };
115 |
--------------------------------------------------------------------------------
/src/plugins/bt-checkbox/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class BtCheckbox
3 | * @memberof module:plugins
4 | * @description Applies Awesome Bootstrap Checkbox for checkbox and radio inputs.
5 | * @param {object} [options]
6 | * @param {string} [options.font='bootstrap-icons']
7 | * @param {string} [options.color='default']
8 | */
9 | QueryBuilder.define('bt-checkbox', function(options) {
10 | if (options.font === 'bootstrap-icons') {
11 | this.$el.addClass('bt-checkbox-bootstrap-icons');
12 | }
13 |
14 | this.on('getRuleInput.filter', function(h, rule, name) {
15 | var filter = rule.filter;
16 |
17 | if ((filter.input === 'radio' || filter.input === 'checkbox') && !filter.plugin) {
18 | h.value = '';
19 |
20 | if (!filter.colors) {
21 | filter.colors = {};
22 | }
23 | if (filter.color) {
24 | filter.colors._def_ = filter.color;
25 | }
26 |
27 | var style = filter.vertical ? ' style="display:block"' : '';
28 | var i = 0;
29 |
30 | Utils.iterateOptions(filter.values, function(key, val) {
31 | var color = filter.colors[key] || filter.colors._def_ || options.color;
32 | var id = name + '_' + (i++);
33 |
34 | h.value += `
`;
35 | });
36 | }
37 | });
38 | }, {
39 | font: 'bootstrap-icons',
40 | color: 'default'
41 | });
42 |
--------------------------------------------------------------------------------
/src/plugins/bt-checkbox/plugin.scss:
--------------------------------------------------------------------------------
1 | .query-builder.bt-checkbox-bootstrap-icons {
2 | .checkbox input[type='checkbox'] + label::before {
3 | outline: 0;
4 | }
5 |
6 | .checkbox input[type='checkbox']:checked + label::after {
7 | font-family: 'bootstrap-icons';
8 | content: '\F633'; // https://icons.getbootstrap.com/icons/check-lg/
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/plugins/bt-tooltip-errors/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class BtTooltipErrors
3 | * @memberof module:plugins
4 | * @description Applies Bootstrap Tooltips on validation error messages.
5 | * @param {object} [options]
6 | * @param {string} [options.placement='right']
7 | * @throws MissingLibraryError
8 | */
9 | QueryBuilder.define('bt-tooltip-errors', function(options) {
10 | if (! typeof bootstrap.Tooltip === "function") {
11 | alert(typeof bootstrap.Tooltip );
12 | Utils.error('MissingLibrary', 'Bootstrap Popper is required to use "bt-tooltip-errors" plugin. Get it here: http://getbootstrap.com');
13 | }
14 |
15 | var self = this;
16 |
17 | // add BT Tooltip data
18 | this.on('getRuleTemplate.filter getGroupTemplate.filter', function(h) {
19 | var $h = $($.parseHTML(h.value));
20 | $h.find(QueryBuilder.selectors.error_container).attr('data-bs-toggle', 'tooltip');
21 | h.value = $h.prop('outerHTML');
22 | });
23 |
24 | // init/refresh tooltip when title changes
25 | this.model.on('update', function(e, node, field) {
26 | if (field == 'error' && self.settings.display_errors) {
27 | node.$el.find(QueryBuilder.selectors.error_container).eq(0)
28 | .attr('data-bs-original-title',options).attr('data-bs-title',options).tooltip();
29 | }
30 | });
31 | }, {
32 | placement: 'right'
33 | });
34 |
--------------------------------------------------------------------------------
/src/plugins/bt-tooltip-errors/plugin.scss:
--------------------------------------------------------------------------------
1 | $error-tooltip-color: #F99;
2 |
3 | @if $theme-name == 'dark' {
4 | $error-tooltip-color: #F22;
5 | }
6 |
7 | .query-builder .error-container + .tooltip .tooltip-inner {
8 | color: $error-tooltip-color !important;
9 | }
10 |
--------------------------------------------------------------------------------
/src/plugins/change-filters/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class ChangeFilters
3 | * @memberof module:plugins
4 | * @description Allows to change available filters after plugin initialization.
5 | */
6 |
7 | QueryBuilder.extend(/** @lends module:plugins.ChangeFilters.prototype */ {
8 | /**
9 | * Change the filters of the builder
10 | * @param {boolean} [deleteOrphans=false] - delete rules using old filters
11 | * @param {QueryBuilder[]} filters
12 | * @fires module:plugins.ChangeFilters.changer:setFilters
13 | * @fires module:plugins.ChangeFilters.afterSetFilters
14 | * @throws ChangeFilterError
15 | */
16 | setFilters: function(deleteOrphans, filters) {
17 | var self = this;
18 |
19 | if (filters === undefined) {
20 | filters = deleteOrphans;
21 | deleteOrphans = false;
22 | }
23 |
24 | filters = this.checkFilters(filters);
25 |
26 | /**
27 | * Modifies the filters before {@link module:plugins.ChangeFilters.setFilters} method
28 | * @event changer:setFilters
29 | * @memberof module:plugins.ChangeFilters
30 | * @param {QueryBuilder.Filter[]} filters
31 | * @returns {QueryBuilder.Filter[]}
32 | */
33 | filters = this.change('setFilters', filters);
34 |
35 | var filtersIds = filters.map(function(filter) {
36 | return filter.id;
37 | });
38 |
39 | // check for orphans
40 | if (!deleteOrphans) {
41 | (function checkOrphans(node) {
42 | node.each(
43 | function(rule) {
44 | if (rule.filter && filtersIds.indexOf(rule.filter.id) === -1) {
45 | Utils.error('ChangeFilter', 'A rule is using filter "{0}"', rule.filter.id);
46 | }
47 | },
48 | checkOrphans
49 | );
50 | }(this.model.root));
51 | }
52 |
53 | // replace filters
54 | this.filters = filters;
55 |
56 | // apply on existing DOM
57 | (function updateBuilder(node) {
58 | node.each(true,
59 | function(rule) {
60 | if (rule.filter && filtersIds.indexOf(rule.filter.id) === -1) {
61 | rule.drop();
62 |
63 | self.trigger('rulesChanged');
64 | }
65 | else {
66 | self.createRuleFilters(rule);
67 |
68 | rule.$el.find(QueryBuilder.selectors.rule_filter).val(rule.filter ? rule.filter.id : '-1');
69 | self.trigger('afterUpdateRuleFilter', rule);
70 | }
71 | },
72 | updateBuilder
73 | );
74 | }(this.model.root));
75 |
76 | // update plugins
77 | if (this.settings.plugins) {
78 | if (this.settings.plugins['unique-filter']) {
79 | this.updateDisabledFilters();
80 | }
81 | if (this.settings.plugins['bt-selectpicker']) {
82 | this.$el.find(QueryBuilder.selectors.rule_filter).selectpicker('render');
83 | }
84 | }
85 |
86 | // reset the default_filter if does not exist anymore
87 | if (this.settings.default_filter) {
88 | try {
89 | this.getFilterById(this.settings.default_filter);
90 | }
91 | catch (e) {
92 | this.settings.default_filter = null;
93 | }
94 | }
95 |
96 | /**
97 | * After {@link module:plugins.ChangeFilters.setFilters} method
98 | * @event afterSetFilters
99 | * @memberof module:plugins.ChangeFilters
100 | * @param {QueryBuilder.Filter[]} filters
101 | */
102 | this.trigger('afterSetFilters', filters);
103 | },
104 |
105 | /**
106 | * Adds a new filter to the builder
107 | * @param {QueryBuilder.Filter|Filter[]} newFilters
108 | * @param {int|string} [position=#end] - index or '#start' or '#end'
109 | * @fires module:plugins.ChangeFilters.changer:setFilters
110 | * @fires module:plugins.ChangeFilters.afterSetFilters
111 | * @throws ChangeFilterError
112 | */
113 | addFilter: function(newFilters, position) {
114 | if (position === undefined || position == '#end') {
115 | position = this.filters.length;
116 | }
117 | else if (position == '#start') {
118 | position = 0;
119 | }
120 |
121 | if (!$.isArray(newFilters)) {
122 | newFilters = [newFilters];
123 | }
124 |
125 | var filters = $.extend(true, [], this.filters);
126 |
127 | // numeric position
128 | if (parseInt(position) == position) {
129 | Array.prototype.splice.apply(filters, [position, 0].concat(newFilters));
130 | }
131 | else {
132 | // after filter by its id
133 | if (this.filters.some(function(filter, index) {
134 | if (filter.id == position) {
135 | position = index + 1;
136 | return true;
137 | }
138 | })
139 | ) {
140 | Array.prototype.splice.apply(filters, [position, 0].concat(newFilters));
141 | }
142 | // defaults to end of list
143 | else {
144 | Array.prototype.push.apply(filters, newFilters);
145 | }
146 | }
147 |
148 | this.setFilters(filters);
149 | },
150 |
151 | /**
152 | * Removes a filter from the builder
153 | * @param {string|string[]} filterIds
154 | * @param {boolean} [deleteOrphans=false] delete rules using old filters
155 | * @fires module:plugins.ChangeFilters.changer:setFilters
156 | * @fires module:plugins.ChangeFilters.afterSetFilters
157 | * @throws ChangeFilterError
158 | */
159 | removeFilter: function(filterIds, deleteOrphans) {
160 | var filters = $.extend(true, [], this.filters);
161 | if (typeof filterIds === 'string') {
162 | filterIds = [filterIds];
163 | }
164 |
165 | filters = filters.filter(function(filter) {
166 | return filterIds.indexOf(filter.id) === -1;
167 | });
168 |
169 | this.setFilters(deleteOrphans, filters);
170 | }
171 | });
172 |
--------------------------------------------------------------------------------
/src/plugins/chosen-selectpicker/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class ChosenSelectpicker
3 | * @memberof module:plugins
4 | * @descriptioon Applies chosen-js Select on filters and operators combo-boxes.
5 | * @param {object} [options] Supports all the options for chosen
6 | * @throws MissingLibraryError
7 | */
8 | QueryBuilder.define('chosen-selectpicker', function(options) {
9 |
10 | if (!$.fn.chosen) {
11 | Utils.error('MissingLibrary', 'chosen is required to use "chosen-selectpicker" plugin. Get it here: https://github.com/harvesthq/chosen');
12 | }
13 |
14 | if (this.settings.plugins['bt-selectpicker']) {
15 | Utils.error('Conflict', 'bt-selectpicker is already selected as the dropdown plugin. Please remove chosen-selectpicker from the plugin list');
16 | }
17 |
18 | var Selectors = QueryBuilder.selectors;
19 |
20 | // init selectpicker
21 | this.on('afterCreateRuleFilters', function(e, rule) {
22 | rule.$el.find(Selectors.rule_filter).removeClass('form-control').chosen(options);
23 | });
24 |
25 | this.on('afterCreateRuleOperators', function(e, rule) {
26 | if (e.builder.getOperators(rule.filter).length > 1) {
27 | rule.$el.find(Selectors.rule_operator).removeClass('form-control').chosen(options);
28 | }
29 | });
30 |
31 | // update selectpicker on change
32 | this.on('afterUpdateRuleFilter', function(e, rule) {
33 | rule.$el.find(Selectors.rule_filter).trigger('chosen:updated');
34 | });
35 |
36 | this.on('afterUpdateRuleOperator', function(e, rule) {
37 | rule.$el.find(Selectors.rule_operator).trigger('chosen:updated');
38 | });
39 |
40 | this.on('beforeDeleteRule', function(e, rule) {
41 | rule.$el.find(Selectors.rule_filter).chosen('destroy');
42 | rule.$el.find(Selectors.rule_operator).chosen('destroy');
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/src/plugins/filter-description/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class FilterDescription
3 | * @memberof module:plugins
4 | * @description Provides three ways to display a description about a filter: inline, Bootsrap Popover or Bootbox.
5 | * @param {object} [options]
6 | * @param {string} [options.icon='bi-info-circle-fill']
7 | * @param {string} [options.mode='popover'] - inline, popover or bootbox
8 | * @throws ConfigError
9 | */
10 | QueryBuilder.define('filter-description', function(options) {
11 | // INLINE
12 | if (options.mode === 'inline') {
13 | this.on('afterUpdateRuleFilter afterUpdateRuleOperator', function(e, rule) {
14 | var $p = rule.$el.find('p.filter-description');
15 | var description = e.builder.getFilterDescription(rule.filter, rule);
16 |
17 | if (!description) {
18 | $p.hide();
19 | }
20 | else {
21 | if ($p.length === 0) {
22 | $p = $($.parseHTML(''));
23 | $p.appendTo(rule.$el);
24 | }
25 | else {
26 | $p.css('display', '');
27 | }
28 |
29 | $p.html(' ' + description);
30 | }
31 | });
32 | }
33 | // POPOVER
34 | else if (options.mode === 'popover') {
35 | if (!$.fn.popover || !$.fn.popover.Constructor || !$.fn.popover.Constructor.prototype.fixTitle) {
36 | Utils.error('MissingLibrary', 'Bootstrap Popover is required to use "filter-description" plugin. Get it here: http://getbootstrap.com');
37 | }
38 |
39 | this.on('afterUpdateRuleFilter afterUpdateRuleOperator', function(e, rule) {
40 | var $b = rule.$el.find('button.filter-description');
41 | var description = e.builder.getFilterDescription(rule.filter, rule);
42 |
43 | if (!description) {
44 | $b.hide();
45 |
46 | if ($b.data('bs-popover')) {
47 | $b.popover('hide');
48 | }
49 | }
50 | else {
51 | if ($b.length === 0) {
52 | $b = $($.parseHTML(''));
53 | $b.prependTo(rule.$el.find(QueryBuilder.selectors.rule_actions));
54 | const popover = new bootstrap.Popover($b.get(0), {
55 | placement: 'left',
56 | container: 'body',
57 | html: true
58 | })
59 | $b.on('mouseout', function() {
60 | popover('hide');
61 | });
62 | }
63 | else {
64 | $b.css('display', '');
65 | }
66 |
67 | $b.data('bs-popover').options.content = description;
68 |
69 | if ($b.attr('aria-describedby')) {
70 | $b.popover('show');
71 | }
72 | }
73 | });
74 | }
75 | // BOOTBOX
76 | else if (options.mode === 'bootbox') {
77 | if (!('bootbox' in window)) {
78 | Utils.error('MissingLibrary', 'Bootbox is required to use "filter-description" plugin. Get it here: http://bootboxjs.com');
79 | }
80 |
81 | this.on('afterUpdateRuleFilter afterUpdateRuleOperator', function(e, rule) {
82 | var $b = rule.$el.find('button.filter-description');
83 | var description = e.builder.getFilterDescription(rule.filter, rule);
84 |
85 | if (!description) {
86 | $b.hide();
87 | }
88 | else {
89 | if ($b.length === 0) {
90 | $b = $($.parseHTML(''));
91 | $b.prependTo(rule.$el.find(QueryBuilder.selectors.rule_actions));
92 |
93 | $b.on('click', function() {
94 | bootbox.alert($b.data('description'));
95 | });
96 | }
97 | else {
98 | $b.css('display', '');
99 | }
100 |
101 | $b.data('description', description);
102 | }
103 | });
104 | }
105 | }, {
106 | icon: 'bi-info-circle-fill',
107 | mode: 'popover'
108 | });
109 |
110 | QueryBuilder.extend(/** @lends module:plugins.FilterDescription.prototype */ {
111 | /**
112 | * Returns the description of a filter for a particular rule (if present)
113 | * @param {object} filter
114 | * @param {Rule} [rule]
115 | * @returns {string}
116 | * @private
117 | */
118 | getFilterDescription: function(filter, rule) {
119 | if (!filter) {
120 | return undefined;
121 | }
122 | else if (typeof filter.description == 'function') {
123 | return filter.description.call(this, rule);
124 | }
125 | else {
126 | return filter.description;
127 | }
128 | }
129 | });
130 |
--------------------------------------------------------------------------------
/src/plugins/filter-description/plugin.scss:
--------------------------------------------------------------------------------
1 | $description-background-color: #D9EDF7;
2 | $description-border-color: #BCE8F1;
3 | $description-text-color: #31708F;
4 |
5 | @if $theme-name == 'dark' {
6 | $description-background-color: rgba(0, 170, 255, .2);
7 | $description-text-color: #AAD1E4;
8 | $description-border-color: #346F7B;
9 | }
10 |
11 | $description-border: 1px solid $description-border-color;
12 |
13 | .query-builder p.filter-description {
14 | margin: $rule-padding 0 0 0;
15 | background: $description-background-color;
16 | border: $description-border;
17 | color: $description-text-color;
18 | border-radius: $item-border-radius;
19 | padding: #{$rule-padding * .5} $rule-padding;
20 | font-size: .8em;
21 | }
22 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/ar.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "قَلْبُ"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/az.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "invert"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/cs.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "invertní"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Εναλλαγή"
3 | }
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Invert"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/eo.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Inversigi"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Inverser"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/he.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "הפוך שאילתא"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Megfordítás (Invertálás)"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/lt.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Invertuoti"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/pl.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Odwróć"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/pt-BR.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Inverter"
3 | }
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Инвертировать"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Invertný"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Invertera"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/sw.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Pindua"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/tr.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "Ters Çevir"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/ua.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "інвертувати"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/i18n/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "invert": "倒置"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/invert/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Invert
3 | * @memberof module:plugins
4 | * @description Allows to invert a rule operator, a group condition or the entire builder.
5 | * @param {object} [options]
6 | * @param {string} [options.icon='bi-shuffle']
7 | * @param {boolean} [options.recursive=true]
8 | * @param {boolean} [options.invert_rules=true]
9 | * @param {boolean} [options.display_rules_button=false]
10 | * @param {boolean} [options.silent_fail=false]
11 | */
12 | QueryBuilder.define('invert', function(options) {
13 | var self = this;
14 | var Selectors = QueryBuilder.selectors;
15 |
16 | // Bind events
17 | this.on('afterInit', function() {
18 | self.$el.on('click.queryBuilder', '[data-invert=group]', function() {
19 | var $group = $(this).closest(Selectors.group_container);
20 | self.invert(self.getModel($group), options);
21 | });
22 |
23 | if (options.display_rules_button && options.invert_rules) {
24 | self.$el.on('click.queryBuilder', '[data-invert=rule]', function() {
25 | var $rule = $(this).closest(Selectors.rule_container);
26 | self.invert(self.getModel($rule), options);
27 | });
28 | }
29 | });
30 |
31 | // Modify templates
32 | if (!options.disable_template) {
33 | this.on('getGroupTemplate.filter', function(h) {
34 | var $h = $($.parseHTML(h.value));
35 | $h.find(Selectors.condition_container).after(
36 | ''
39 | );
40 | h.value = $h.prop('outerHTML');
41 | });
42 |
43 | if (options.display_rules_button && options.invert_rules) {
44 | this.on('getRuleTemplate.filter', function(h) {
45 | var $h = $($.parseHTML(h.value));
46 | $h.find(Selectors.rule_actions).prepend(
47 | ''
50 | );
51 | h.value = $h.prop('outerHTML');
52 | });
53 | }
54 | }
55 | }, {
56 | icon: 'bi-shuffle',
57 | recursive: true,
58 | invert_rules: true,
59 | display_rules_button: false,
60 | silent_fail: false,
61 | disable_template: false
62 | });
63 |
64 | QueryBuilder.defaults({
65 | operatorOpposites: {
66 | 'equal': 'not_equal',
67 | 'not_equal': 'equal',
68 | 'in': 'not_in',
69 | 'not_in': 'in',
70 | 'less': 'greater_or_equal',
71 | 'less_or_equal': 'greater',
72 | 'greater': 'less_or_equal',
73 | 'greater_or_equal': 'less',
74 | 'between': 'not_between',
75 | 'not_between': 'between',
76 | 'begins_with': 'not_begins_with',
77 | 'not_begins_with': 'begins_with',
78 | 'contains': 'not_contains',
79 | 'not_contains': 'contains',
80 | 'ends_with': 'not_ends_with',
81 | 'not_ends_with': 'ends_with',
82 | 'is_empty': 'is_not_empty',
83 | 'is_not_empty': 'is_empty',
84 | 'is_null': 'is_not_null',
85 | 'is_not_null': 'is_null'
86 | },
87 |
88 | conditionOpposites: {
89 | 'AND': 'OR',
90 | 'OR': 'AND'
91 | }
92 | });
93 |
94 | QueryBuilder.extend(/** @lends module:plugins.Invert.prototype */ {
95 | /**
96 | * Invert a Group, a Rule or the whole builder
97 | * @param {Node} [node]
98 | * @param {object} [options] {@link module:plugins.Invert}
99 | * @fires module:plugins.Invert.afterInvert
100 | * @throws InvertConditionError, InvertOperatorError
101 | */
102 | invert: function(node, options) {
103 | if (!(node instanceof Node)) {
104 | if (!this.model.root) return;
105 | options = node;
106 | node = this.model.root;
107 | }
108 |
109 | if (typeof options != 'object') options = {};
110 | if (options.recursive === undefined) options.recursive = true;
111 | if (options.invert_rules === undefined) options.invert_rules = true;
112 | if (options.silent_fail === undefined) options.silent_fail = false;
113 | if (options.trigger === undefined) options.trigger = true;
114 |
115 | if (node instanceof Group) {
116 | // invert group condition
117 | if (this.settings.conditionOpposites[node.condition]) {
118 | node.condition = this.settings.conditionOpposites[node.condition];
119 | }
120 | else if (!options.silent_fail) {
121 | Utils.error('InvertCondition', 'Unknown inverse of condition "{0}"', node.condition);
122 | }
123 |
124 | // recursive call
125 | if (options.recursive) {
126 | var tempOpts = $.extend({}, options, { trigger: false });
127 | node.each(function(rule) {
128 | if (options.invert_rules) {
129 | this.invert(rule, tempOpts);
130 | }
131 | }, function(group) {
132 | this.invert(group, tempOpts);
133 | }, this);
134 | }
135 | }
136 | else if (node instanceof Rule) {
137 | if (node.operator && !node.filter.no_invert) {
138 | // invert rule operator
139 | if (this.settings.operatorOpposites[node.operator.type]) {
140 | var invert = this.settings.operatorOpposites[node.operator.type];
141 | // check if the invert is "authorized"
142 | if (!node.filter.operators || node.filter.operators.indexOf(invert) != -1) {
143 | node.operator = this.getOperatorByType(invert);
144 | }
145 | }
146 | else if (!options.silent_fail) {
147 | Utils.error('InvertOperator', 'Unknown inverse of operator "{0}"', node.operator.type);
148 | }
149 | }
150 | }
151 |
152 | if (options.trigger) {
153 | /**
154 | * After {@link module:plugins.Invert.invert} method
155 | * @event afterInvert
156 | * @memberof module:plugins.Invert
157 | * @param {Node} node - the main group or rule that has been modified
158 | * @param {object} options
159 | */
160 | this.trigger('afterInvert', node, options);
161 |
162 | this.trigger('rulesChanged');
163 | }
164 | }
165 | });
166 |
--------------------------------------------------------------------------------
/src/plugins/invert/plugin.scss:
--------------------------------------------------------------------------------
1 | .query-builder {
2 | .rules-group-header [data-invert] {
3 | margin-left: 5px;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "NOT"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/eo.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "NE"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "NON"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/he.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "לא"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/hu.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "NEM"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/lt.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "NE"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "НЕ"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/sk.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "NIE"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/sv.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "INTE"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/i18n/sw.json:
--------------------------------------------------------------------------------
1 | {
2 | "NOT": "SIO"
3 | }
4 |
--------------------------------------------------------------------------------
/src/plugins/not-group/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class NotGroup
3 | * @memberof module:plugins
4 | * @description Adds a "Not" checkbox in front of group conditions.
5 | * @param {object} [options]
6 | * @param {string} [options.icon_checked='bi-check2-square']
7 | * @param {string} [options.icon_unchecked='bi-square']
8 | */
9 | QueryBuilder.define('not-group', function(options) {
10 | var self = this;
11 |
12 | // Bind events
13 | this.on('afterInit', function() {
14 | self.$el.on('click.queryBuilder', '[data-not=group]', function() {
15 | var $group = $(this).closest(QueryBuilder.selectors.group_container);
16 | var group = self.getModel($group);
17 | group.not = !group.not;
18 | });
19 |
20 | self.model.on('update', function(e, node, field) {
21 | if (node instanceof Group && field === 'not') {
22 | self.updateGroupNot(node);
23 | }
24 | });
25 | });
26 |
27 | // Init "not" property
28 | this.on('afterAddGroup', function(e, group) {
29 | group.__.not = false;
30 | });
31 |
32 | // Modify templates
33 | if (!options.disable_template) {
34 | this.on('getGroupTemplate.filter', function(h) {
35 | var $h = $($.parseHTML(h.value));
36 | $h.find(QueryBuilder.selectors.condition_container).prepend(
37 | ''
40 | );
41 | h.value = $h.prop('outerHTML');
42 | });
43 | }
44 |
45 | // Export "not" to JSON
46 | this.on('groupToJson.filter', function(e, group) {
47 | e.value.not = group.not;
48 | });
49 |
50 | // Read "not" from JSON
51 | this.on('jsonToGroup.filter', function(e, json) {
52 | e.value.not = !!json.not;
53 | });
54 |
55 | // Export "not" to SQL
56 | this.on('groupToSQL.filter', function(e, group) {
57 | if (group.not) {
58 | e.value = 'NOT ( ' + e.value + ' )';
59 | }
60 | });
61 |
62 | // Parse "NOT" function from sqlparser
63 | this.on('parseSQLNode.filter', function(e) {
64 | if (e.value.name && e.value.name.toUpperCase() == 'NOT') {
65 | e.value = e.value.arguments.value[0];
66 |
67 | // if the there is no sub-group, create one
68 | if (['AND', 'OR'].indexOf(e.value.operation.toUpperCase()) === -1) {
69 | e.value = new SQLParser.nodes.Op(
70 | self.settings.default_condition,
71 | e.value,
72 | null
73 | );
74 | }
75 |
76 | e.value.not = true;
77 | }
78 | });
79 |
80 | // Request to create sub-group if the "not" flag is set
81 | this.on('sqlGroupsDistinct.filter', function(e, group, data, i) {
82 | if (data.not && i > 0) {
83 | e.value = true;
84 | }
85 | });
86 |
87 | // Read "not" from parsed SQL
88 | this.on('sqlToGroup.filter', function(e, data) {
89 | e.value.not = !!data.not;
90 | });
91 |
92 | // Export "not" to Mongo
93 | this.on('groupToMongo.filter', function(e, group) {
94 | var key = '$' + group.condition.toLowerCase();
95 | if (group.not && e.value[key]) {
96 | e.value = { '$nor': [e.value] };
97 | }
98 | });
99 |
100 | // Parse "$nor" operator from Mongo
101 | this.on('parseMongoNode.filter', function(e) {
102 | var keys = Object.keys(e.value);
103 |
104 | if (keys[0] == '$nor') {
105 | e.value = e.value[keys[0]][0];
106 | e.value.not = true;
107 | }
108 | });
109 |
110 | // Read "not" from parsed Mongo
111 | this.on('mongoToGroup.filter', function(e, data) {
112 | e.value.not = !!data.not;
113 | });
114 | }, {
115 | icon_unchecked: 'bi-square',
116 | icon_checked: 'bi-check2-square',
117 | disable_template: false
118 | });
119 |
120 | /**
121 | * From {@link module:plugins.NotGroup}
122 | * @name not
123 | * @member {boolean}
124 | * @memberof Group
125 | * @instance
126 | */
127 | Utils.defineModelProperties(Group, ['not']);
128 |
129 | QueryBuilder.selectors.group_not = QueryBuilder.selectors.group_header + ' [data-not=group]';
130 |
131 | QueryBuilder.extend(/** @lends module:plugins.NotGroup.prototype */ {
132 | /**
133 | * Performs actions when a group's not changes
134 | * @param {Group} group
135 | * @fires module:plugins.NotGroup.afterUpdateGroupNot
136 | * @private
137 | */
138 | updateGroupNot: function(group) {
139 | var options = this.plugins['not-group'];
140 | group.$el.find('>' + QueryBuilder.selectors.group_not)
141 | .toggleClass('active', group.not)
142 | .find('i').attr('class', group.not ? options.icon_checked : options.icon_unchecked);
143 |
144 | /**
145 | * After the group's not flag has been modified
146 | * @event afterUpdateGroupNot
147 | * @memberof module:plugins.NotGroup
148 | * @param {Group} group
149 | */
150 | this.trigger('afterUpdateGroupNot', group);
151 |
152 | this.trigger('rulesChanged');
153 | }
154 | });
155 |
--------------------------------------------------------------------------------
/src/plugins/sortable/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class Sortable
3 | * @memberof module:plugins
4 | * @description Enables drag & drop sort of rules.
5 | * @param {object} [options]
6 | * @param {boolean} [options.inherit_no_drop=true]
7 | * @param {boolean} [options.inherit_no_sortable=true]
8 | * @param {string} [options.icon='bi-sort-down']
9 | * @throws MissingLibraryError, ConfigError
10 | */
11 | QueryBuilder.define('sortable', function(options) {
12 | if (!('interact' in window)) {
13 | Utils.error('MissingLibrary', 'interact.js is required to use "sortable" plugin. Get it here: http://interactjs.io');
14 | }
15 |
16 | if (options.default_no_sortable !== undefined) {
17 | Utils.error(false, 'Config', 'Sortable plugin : "default_no_sortable" options is deprecated, use standard "default_rule_flags" and "default_group_flags" instead');
18 | this.settings.default_rule_flags.no_sortable = this.settings.default_group_flags.no_sortable = options.default_no_sortable;
19 | }
20 |
21 | // recompute drop-zones during drag (when a rule is hidden)
22 | interact.dynamicDrop(true);
23 |
24 | // set move threshold to 10px
25 | interact.pointerMoveTolerance(10);
26 |
27 | var placeholder;
28 | var ghost;
29 | var src;
30 | var moved;
31 |
32 | // Init drag and drop
33 | this.on('afterAddRule afterAddGroup', function(e, node) {
34 | if (node == placeholder) {
35 | return;
36 | }
37 |
38 | var self = e.builder;
39 |
40 | // Inherit flags
41 | if (options.inherit_no_sortable && node.parent && node.parent.flags.no_sortable) {
42 | node.flags.no_sortable = true;
43 | }
44 | if (options.inherit_no_drop && node.parent && node.parent.flags.no_drop) {
45 | node.flags.no_drop = true;
46 | }
47 |
48 | // Configure drag
49 | if (!node.flags.no_sortable) {
50 | interact(node.$el[0])
51 | .draggable({
52 | allowFrom: QueryBuilder.selectors.drag_handle,
53 | onstart: function(event) {
54 | moved = false;
55 |
56 | // get model of dragged element
57 | src = self.getModel(event.target);
58 |
59 | // create ghost
60 | ghost = src.$el.clone()
61 | .appendTo(src.$el.parent())
62 | .width(src.$el.outerWidth())
63 | .addClass('dragging');
64 |
65 | // create drop placeholder
66 | var ph = $($.parseHTML('
'))
67 | .height(src.$el.outerHeight());
68 |
69 | placeholder = src.parent.addRule(ph, src.getPos());
70 |
71 | // hide dragged element
72 | src.$el.hide();
73 | },
74 | onmove: function(event) {
75 | // make the ghost follow the cursor
76 | ghost[0].style.top = event.clientY - 15 + 'px';
77 | ghost[0].style.left = event.clientX - 15 + 'px';
78 | },
79 | onend: function(event) {
80 | // starting from Interact 1.3.3, onend is called before ondrop
81 | if (event.dropzone) {
82 | moveSortableToTarget(src, $(event.relatedTarget), self);
83 | moved = true;
84 | }
85 |
86 | // remove ghost
87 | ghost.remove();
88 | ghost = undefined;
89 |
90 | // remove placeholder
91 | placeholder.drop();
92 | placeholder = undefined;
93 |
94 | // show element
95 | src.$el.css('display', '');
96 |
97 | /**
98 | * After a node has been moved with {@link module:plugins.Sortable}
99 | * @event afterMove
100 | * @memberof module:plugins.Sortable
101 | * @param {Node} node
102 | */
103 | self.trigger('afterMove', src);
104 |
105 | self.trigger('rulesChanged');
106 | }
107 | });
108 | }
109 |
110 | if (!node.flags.no_drop) {
111 | // Configure drop on groups and rules
112 | interact(node.$el[0])
113 | .dropzone({
114 | accept: QueryBuilder.selectors.rule_and_group_containers,
115 | ondragenter: function(event) {
116 | moveSortableToTarget(placeholder, $(event.target), self);
117 | },
118 | ondrop: function(event) {
119 | if (!moved) {
120 | moveSortableToTarget(src, $(event.target), self);
121 | }
122 | }
123 | });
124 |
125 | // Configure drop on group headers
126 | if (node instanceof Group) {
127 | interact(node.$el.find(QueryBuilder.selectors.group_header)[0])
128 | .dropzone({
129 | accept: QueryBuilder.selectors.rule_and_group_containers,
130 | ondragenter: function(event) {
131 | moveSortableToTarget(placeholder, $(event.target), self);
132 | },
133 | ondrop: function(event) {
134 | if (!moved) {
135 | moveSortableToTarget(src, $(event.target), self);
136 | }
137 | }
138 | });
139 | }
140 | }
141 | });
142 |
143 | // Detach interactables
144 | this.on('beforeDeleteRule beforeDeleteGroup', function(e, node) {
145 | if (!e.isDefaultPrevented()) {
146 | interact(node.$el[0]).unset();
147 |
148 | if (node instanceof Group) {
149 | interact(node.$el.find(QueryBuilder.selectors.group_header)[0]).unset();
150 | }
151 | }
152 | });
153 |
154 | // Remove drag handle from non-sortable items
155 | this.on('afterApplyRuleFlags afterApplyGroupFlags', function(e, node) {
156 | if (node.flags.no_sortable) {
157 | node.$el.find('.drag-handle').remove();
158 | }
159 | });
160 |
161 | // Modify templates
162 | if (!options.disable_template) {
163 | this.on('getGroupTemplate.filter', function(h, level) {
164 | if (level > 1) {
165 | var $h = $($.parseHTML(h.value));
166 | $h.find(QueryBuilder.selectors.condition_container).after('
');
167 | h.value = $h.prop('outerHTML');
168 | }
169 | });
170 |
171 | this.on('getRuleTemplate.filter', function(h) {
172 | var $h = $($.parseHTML(h.value));
173 | $h.find(QueryBuilder.selectors.rule_header).after('
');
174 | h.value = $h.prop('outerHTML');
175 | });
176 | }
177 | }, {
178 | inherit_no_sortable: true,
179 | inherit_no_drop: true,
180 | icon: 'bi-sort-down',
181 | disable_template: false
182 | });
183 |
184 | QueryBuilder.selectors.rule_and_group_containers = QueryBuilder.selectors.rule_container + ', ' + QueryBuilder.selectors.group_container;
185 | QueryBuilder.selectors.drag_handle = '.drag-handle';
186 |
187 | QueryBuilder.defaults({
188 | default_rule_flags: {
189 | no_sortable: false,
190 | no_drop: false
191 | },
192 | default_group_flags: {
193 | no_sortable: false,
194 | no_drop: false
195 | }
196 | });
197 |
198 | /**
199 | * Moves an element (placeholder or actual object) depending on active target
200 | * @memberof module:plugins.Sortable
201 | * @param {Node} node
202 | * @param {jQuery} target
203 | * @param {QueryBuilder} [builder]
204 | * @private
205 | */
206 | function moveSortableToTarget(node, target, builder) {
207 | var parent, method;
208 | var Selectors = QueryBuilder.selectors;
209 |
210 | // on rule
211 | parent = target.closest(Selectors.rule_container);
212 | if (parent.length) {
213 | method = 'moveAfter';
214 | }
215 |
216 | // on group header
217 | if (!method) {
218 | parent = target.closest(Selectors.group_header);
219 | if (parent.length) {
220 | parent = target.closest(Selectors.group_container);
221 | method = 'moveAtBegin';
222 | }
223 | }
224 |
225 | // on group
226 | if (!method) {
227 | parent = target.closest(Selectors.group_container);
228 | if (parent.length) {
229 | method = 'moveAtEnd';
230 | }
231 | }
232 |
233 | if (method) {
234 | node[method](builder.getModel(parent));
235 |
236 | // refresh radio value
237 | if (builder && node instanceof Rule) {
238 | builder.setRuleInputValue(node, node.value);
239 | }
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/plugins/sortable/plugin.scss:
--------------------------------------------------------------------------------
1 | $placeholder-border-color: #BBB;
2 | $placeholder-border: 1px dashed $placeholder-border-color;
3 |
4 | .query-builder {
5 | .drag-handle {
6 | @extend %rule-component;
7 | cursor: move;
8 | vertical-align: middle;
9 | margin-left: 5px;
10 | }
11 |
12 | .dragging {
13 | position: fixed;
14 | opacity: .5;
15 | z-index: 100;
16 |
17 | &::before,
18 | &::after {
19 | display: none;
20 | }
21 | }
22 |
23 | .rule-placeholder {
24 | @extend %base-container;
25 | border: $placeholder-border;
26 | opacity: .7;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/plugins/unique-filter/plugin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @class UniqueFilter
3 | * @memberof module:plugins
4 | * @description Allows to define some filters as "unique": ie which can be used for only one rule, globally or in the same group.
5 | */
6 | QueryBuilder.define('unique-filter', function() {
7 | this.status.used_filters = {};
8 |
9 | this.on('afterUpdateRuleFilter', this.updateDisabledFilters);
10 | this.on('afterDeleteRule', this.updateDisabledFilters);
11 | this.on('afterCreateRuleFilters', this.applyDisabledFilters);
12 | this.on('afterReset', this.clearDisabledFilters);
13 | this.on('afterClear', this.clearDisabledFilters);
14 |
15 | // Ensure that the default filter is not already used if unique
16 | this.on('getDefaultFilter.filter', function(e, model) {
17 | var self = e.builder;
18 |
19 | self.updateDisabledFilters();
20 |
21 | if (e.value.id in self.status.used_filters) {
22 | var found = self.filters.some(function(filter) {
23 | if (!(filter.id in self.status.used_filters) || self.status.used_filters[filter.id].length > 0 && self.status.used_filters[filter.id].indexOf(model.parent) === -1) {
24 | e.value = filter;
25 | return true;
26 | }
27 | });
28 |
29 | if (!found) {
30 | Utils.error(false, 'UniqueFilter', 'No more non-unique filters available');
31 | e.value = undefined;
32 | }
33 | }
34 | });
35 | });
36 |
37 | QueryBuilder.extend(/** @lends module:plugins.UniqueFilter.prototype */ {
38 | /**
39 | * Updates the list of used filters
40 | * @param {$.Event} [e]
41 | * @private
42 | */
43 | updateDisabledFilters: function(e) {
44 | var self = e ? e.builder : this;
45 |
46 | self.status.used_filters = {};
47 |
48 | if (!self.model) {
49 | return;
50 | }
51 |
52 | // get used filters
53 | (function walk(group) {
54 | group.each(function(rule) {
55 | if (rule.filter && rule.filter.unique) {
56 | if (!self.status.used_filters[rule.filter.id]) {
57 | self.status.used_filters[rule.filter.id] = [];
58 | }
59 | if (rule.filter.unique == 'group') {
60 | self.status.used_filters[rule.filter.id].push(rule.parent);
61 | }
62 | }
63 | }, function(group) {
64 | walk(group);
65 | });
66 | }(self.model.root));
67 |
68 | self.applyDisabledFilters(e);
69 | },
70 |
71 | /**
72 | * Clear the list of used filters
73 | * @param {$.Event} [e]
74 | * @private
75 | */
76 | clearDisabledFilters: function(e) {
77 | var self = e ? e.builder : this;
78 |
79 | self.status.used_filters = {};
80 |
81 | self.applyDisabledFilters(e);
82 | },
83 |
84 | /**
85 | * Disabled filters depending on the list of used ones
86 | * @param {$.Event} [e]
87 | * @private
88 | */
89 | applyDisabledFilters: function(e) {
90 | var self = e ? e.builder : this;
91 |
92 | // re-enable everything
93 | self.$el.find(QueryBuilder.selectors.filter_container + ' option').prop('disabled', false);
94 |
95 | // disable some
96 | $.each(self.status.used_filters, function(filterId, groups) {
97 | if (groups.length === 0) {
98 | self.$el.find(QueryBuilder.selectors.filter_container + ' option[value="' + filterId + '"]:not(:selected)').prop('disabled', true);
99 | }
100 | else {
101 | groups.forEach(function(group) {
102 | group.each(function(rule) {
103 | rule.$el.find(QueryBuilder.selectors.filter_container + ' option[value="' + filterId + '"]:not(:selected)').prop('disabled', true);
104 | });
105 | });
106 | }
107 | });
108 |
109 | // update Selectpicker
110 | if (self.settings.plugins && self.settings.plugins['bt-selectpicker']) {
111 | self.$el.find(QueryBuilder.selectors.rule_filter).selectpicker('render');
112 | }
113 | }
114 | });
115 |
--------------------------------------------------------------------------------
/src/scss/dark.scss:
--------------------------------------------------------------------------------
1 | $theme-name: dark;
2 |
3 | $group-background-color: rgba(50, 70, 80, .5);
4 | $group-border-color: #00164A;
5 |
6 | $rule-background-color: rgba(40, 40, 40, .9);
7 | $rule-border-color: #111;
8 |
9 | $error-border-color: #800;
10 | $error-background-color: #322;
11 |
12 | $ticks-color: #222;
13 |
14 | @import 'default';
15 |
--------------------------------------------------------------------------------
/src/scss/default.scss:
--------------------------------------------------------------------------------
1 | $theme-name: default !default;
2 |
3 | // common
4 | $item-vertical-spacing: 4px !default;
5 | $item-border-radius: 5px !default;
6 |
7 | // groups
8 | $group-background-color: rgba(250, 240, 210, .5) !default;
9 | $group-border-color: #DCC896 !default;
10 | $group-border: 1px solid $group-border-color !default;
11 | $group-padding: 10px !default;
12 |
13 | // rules
14 | $rule-background-color: rgba(255, 255, 255, .9) !default;
15 | $rule-border-color: #EEE !default;
16 | $rule-border: 1px solid $rule-border-color !default;
17 | $rule-padding: 5px !default;
18 | // scss-lint:disable ColorVariable
19 | $rule-value-separator: 1px solid #DDD !default;
20 |
21 | // errors
22 | $error-icon-color: #F00 !default;
23 | $error-border-color: #F99 !default;
24 | $error-background-color: #FDD !default;
25 |
26 | // ticks
27 | $ticks-width: 2px !default;
28 | $ticks-color: #CCC !default;
29 | $ticks-position: 5px, 10px !default;
30 |
31 |
32 | // ABSTRACTS
33 | %base-container {
34 | position: relative;
35 | margin: $item-vertical-spacing 0;
36 | border-radius: $item-border-radius;
37 | padding: $rule-padding;
38 | border: $rule-border;
39 | background: $rule-background-color;
40 | }
41 |
42 | %rule-component {
43 | display: inline-block;
44 | margin: 0 5px 0 0;
45 | vertical-align: middle;
46 | }
47 |
48 | .query-builder {
49 |
50 | // GROUPS
51 | .rules-group-container {
52 | @extend %base-container;
53 |
54 | padding: $group-padding;
55 | padding-bottom: #{$group-padding - $item-vertical-spacing};
56 | border: $group-border;
57 | background: $group-background-color;
58 | }
59 |
60 | .rules-group-header {
61 | margin-bottom: $group-padding;
62 |
63 | .group-conditions {
64 | .btn.readonly:not(.active),
65 | input[name$='_cond'] {
66 | border: 0;
67 | clip: rect(0 0 0 0);
68 | height: 1px;
69 | margin: -1px;
70 | overflow: hidden;
71 | padding: 0;
72 | position: absolute;
73 | width: 1px;
74 | white-space: nowrap;
75 | }
76 |
77 | .btn.readonly {
78 | border-radius: 3px;
79 | }
80 | }
81 | }
82 |
83 | .rules-list {
84 | list-style: none;
85 | padding: 0 0 0 #{nth($ticks-position, 1) + nth($ticks-position, 2)};
86 | margin: 0;
87 | }
88 |
89 | // RULES
90 | .rule-container {
91 | @extend %base-container;
92 |
93 | .rule-filter-container,
94 | .rule-operator-container,
95 | .rule-value-container {
96 | @extend %rule-component;
97 | }
98 | }
99 |
100 | .rule-value-container {
101 | border-left: $rule-value-separator;
102 | padding-left: 5px;
103 |
104 | label {
105 | margin-bottom: 0;
106 | font-weight: normal;
107 |
108 | &.block {
109 | display: block;
110 | }
111 | }
112 | }
113 |
114 | // ERRORS
115 | .error-container {
116 | @extend %rule-component;
117 | display: none;
118 | cursor: help;
119 | color: $error-icon-color;
120 | }
121 |
122 | .has-error {
123 | background-color: $error-background-color;
124 | border-color: $error-border-color;
125 |
126 | .error-container {
127 | display: inline-block !important;
128 | }
129 | }
130 |
131 | // TICKS
132 | .rules-list>* {
133 | &::before,
134 | &::after {
135 | content: '';
136 | position: absolute;
137 | left: #{-1 * nth($ticks-position, 2)};
138 | width: nth($ticks-position, 2);
139 | height: calc(50% + #{$item-vertical-spacing});
140 | border-color: $ticks-color;
141 | border-style: solid;
142 | }
143 |
144 | &::before {
145 | top: #{-2 * $ticks-width};
146 | border-width: 0 0 $ticks-width $ticks-width;
147 | }
148 |
149 | &::after {
150 | top: 50%;
151 | border-width: 0 0 0 $ticks-width;
152 | }
153 |
154 | &:first-child::before {
155 | top: #{-$group-padding - $ticks-width};
156 | height: calc(50% + #{$group-padding + $item-vertical-spacing});
157 | }
158 |
159 | &:last-child::before {
160 | border-radius: 0 0 0 #{2 * $ticks-width};
161 | }
162 |
163 | &:last-child::after {
164 | display: none;
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @namespace
3 | */
4 | var Utils = {};
5 |
6 | /**
7 | * @member {object}
8 | * @memberof QueryBuilder
9 | * @see Utils
10 | */
11 | QueryBuilder.utils = Utils;
12 |
13 | /**
14 | * @callback Utils#OptionsIteratee
15 | * @param {string} key
16 | * @param {string} value
17 | * @param {string} [optgroup]
18 | */
19 |
20 | /**
21 | * Iterates over radio/checkbox/selection options, it accept four formats
22 | *
23 | * @example
24 | * // array of values
25 | * options = ['one', 'two', 'three']
26 | * @example
27 | * // simple key-value map
28 | * options = {1: 'one', 2: 'two', 3: 'three'}
29 | * @example
30 | * // array of 1-element maps
31 | * options = [{1: 'one'}, {2: 'two'}, {3: 'three'}]
32 | * @example
33 | * // array of elements
34 | * options = [{value: 1, label: 'one', optgroup: 'group'}, {value: 2, label: 'two'}]
35 | *
36 | * @param {object|array} options
37 | * @param {Utils#OptionsIteratee} tpl
38 | */
39 | Utils.iterateOptions = function(options, tpl) {
40 | if (options) {
41 | if ($.isArray(options)) {
42 | options.forEach(function(entry) {
43 | if ($.isPlainObject(entry)) {
44 | // array of elements
45 | if ('value' in entry) {
46 | tpl(entry.value, entry.label || entry.value, entry.optgroup);
47 | }
48 | // array of one-element maps
49 | else {
50 | $.each(entry, function(key, val) {
51 | tpl(key, val);
52 | return false; // break after first entry
53 | });
54 | }
55 | }
56 | // array of values
57 | else {
58 | tpl(entry, entry);
59 | }
60 | });
61 | }
62 | // unordered map
63 | else {
64 | $.each(options, function(key, val) {
65 | tpl(key, val);
66 | });
67 | }
68 | }
69 | };
70 |
71 | /**
72 | * Replaces {0}, {1}, ... in a string
73 | * @param {string} str
74 | * @param {...*} args
75 | * @returns {string}
76 | */
77 | Utils.fmt = function(str, args) {
78 | if (!Array.isArray(args)) {
79 | args = Array.prototype.slice.call(arguments, 1);
80 | }
81 |
82 | return str.replace(/{([0-9]+)}/g, function(m, i) {
83 | return args[parseInt(i)];
84 | });
85 | };
86 |
87 | /**
88 | * Throws an Error object with custom name or logs an error
89 | * @param {boolean} [doThrow=true]
90 | * @param {string} type
91 | * @param {string} message
92 | * @param {...*} args
93 | */
94 | Utils.error = function() {
95 | var i = 0;
96 | var doThrow = typeof arguments[i] === 'boolean' ? arguments[i++] : true;
97 | var type = arguments[i++];
98 | var message = arguments[i++];
99 | var args = Array.isArray(arguments[i]) ? arguments[i] : Array.prototype.slice.call(arguments, i);
100 |
101 | if (doThrow) {
102 | var err = new Error(Utils.fmt(message, args));
103 | err.name = type + 'Error';
104 | err.args = args;
105 | throw err;
106 | }
107 | else {
108 | console.error(type + 'Error: ' + Utils.fmt(message, args));
109 | }
110 | };
111 |
112 | /**
113 | * Changes the type of a value to int, float or bool
114 | * @param {*} value
115 | * @param {string} type - 'integer', 'double', 'boolean' or anything else (passthrough)
116 | * @returns {*}
117 | */
118 | Utils.changeType = function(value, type) {
119 | if (value === '' || value === undefined) {
120 | return undefined;
121 | }
122 |
123 | switch (type) {
124 | // @formatter:off
125 | case 'integer':
126 | if (typeof value === 'string' && !/^-?\d+$/.test(value)) {
127 | return value;
128 | }
129 | return parseInt(value);
130 | case 'double':
131 | if (typeof value === 'string' && !/^-?\d+\.?\d*$/.test(value)) {
132 | return value;
133 | }
134 | return parseFloat(value);
135 | case 'boolean':
136 | if (typeof value === 'string' && !/^(0|1|true|false){1}$/i.test(value)) {
137 | return value;
138 | }
139 | return value === true || value === 1 || value.toLowerCase() === 'true' || value === '1';
140 | default: return value;
141 | // @formatter:on
142 | }
143 | };
144 |
145 | /**
146 | * Escapes a string like PHP's mysql_real_escape_string does
147 | * @param {string} value
148 | * @param {string} [additionalEscape] additionnal chars to escape
149 | * @returns {string}
150 | */
151 | Utils.escapeString = function(value, additionalEscape) {
152 | if (typeof value != 'string') {
153 | return value;
154 | }
155 |
156 | var escaped = value
157 | .replace(/[\0\n\r\b\\\'\"]/g, function(s) {
158 | switch (s) {
159 | // @formatter:off
160 | case '\0': return '\\0';
161 | case '\n': return '\\n';
162 | case '\r': return '\\r';
163 | case '\b': return '\\b';
164 | case '\'': return '\'\'';
165 | default: return '\\' + s;
166 | // @formatter:off
167 | }
168 | })
169 | // uglify compliant
170 | .replace(/\t/g, '\\t')
171 | .replace(/\x1a/g, '\\Z');
172 |
173 | if (additionalEscape) {
174 | escaped = escaped
175 | .replace(new RegExp('[' + additionalEscape + ']', 'g'), function(s) {
176 | return '\\' + s;
177 | });
178 | }
179 |
180 | return escaped;
181 | };
182 |
183 | /**
184 | * Escapes a string for use in regex
185 | * @param {string} str
186 | * @returns {string}
187 | */
188 | Utils.escapeRegExp = function(str) {
189 | return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
190 | };
191 |
192 | /**
193 | * Escapes a string for use in HTML element id
194 | * @param {string} str
195 | * @returns {string}
196 | */
197 | Utils.escapeElementId = function(str) {
198 | // Regex based on that suggested by:
199 | // https://learn.jquery.com/using-jquery-core/faq/how-do-i-select-an-element-by-an-id-that-has-characters-used-in-css-notation/
200 | // - escapes : . [ ] ,
201 | // - avoids escaping already escaped values
202 | return (str) ? str.replace(/(\\)?([:.\[\],])/g,
203 | function( $0, $1, $2 ) { return $1 ? $0 : '\\' + $2; }) : str;
204 | };
205 |
206 | /**
207 | * Sorts objects by grouping them by `key`, preserving initial order when possible
208 | * @param {object[]} items
209 | * @param {string} key
210 | * @returns {object[]}
211 | */
212 | Utils.groupSort = function(items, key) {
213 | var optgroups = [];
214 | var newItems = [];
215 |
216 | items.forEach(function(item) {
217 | var idx;
218 |
219 | if (item[key]) {
220 | idx = optgroups.lastIndexOf(item[key]);
221 |
222 | if (idx == -1) {
223 | idx = optgroups.length;
224 | }
225 | else {
226 | idx++;
227 | }
228 | }
229 | else {
230 | idx = optgroups.length;
231 | }
232 |
233 | optgroups.splice(idx, 0, item[key]);
234 | newItems.splice(idx, 0, item);
235 | });
236 |
237 | return newItems;
238 | };
239 |
240 | /**
241 | * Defines properties on an Node prototype with getter and setter.
242 | * Update events are emitted in the setter through root Model (if any).
243 | * The object must have a `__` object, non enumerable property to store values.
244 | * @param {function} obj
245 | * @param {string[]} fields
246 | */
247 | Utils.defineModelProperties = function(obj, fields) {
248 | fields.forEach(function(field) {
249 | Object.defineProperty(obj.prototype, field, {
250 | enumerable: true,
251 | get: function() {
252 | return this.__[field];
253 | },
254 | set: function(value) {
255 | var previousValue = (this.__[field] !== null && typeof this.__[field] == 'object') ?
256 | $.extend({}, this.__[field]) :
257 | this.__[field];
258 |
259 | this.__[field] = value;
260 |
261 | if (this.model !== null) {
262 | /**
263 | * After a value of the model changed
264 | * @event model:update
265 | * @memberof Model
266 | * @param {Node} node
267 | * @param {string} field
268 | * @param {*} value
269 | * @param {*} previousValue
270 | */
271 | this.model.trigger('update', this, field, value, previousValue);
272 | }
273 | }
274 | });
275 | });
276 | };
277 |
--------------------------------------------------------------------------------
/tests/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sync load of language file once QUnit and Blanket are ready
3 | * Otherwise the language file is loaded before instrumented files
4 | */
5 | QUnit.begin(function() {
6 | $.ajax({
7 | async: false,
8 | url: '../dist/i18n/query-builder.en.js',
9 | dataType: 'script'
10 | });
11 | });
12 |
13 | /**
14 | * Add GitHub link in header
15 | */
16 | QUnit.begin(function(){
17 | $('#qunit-header').append(
18 | ''
23 | );
24 | });
25 |
26 | /**
27 | * Modify Blanket results display
28 | */
29 | QUnit.done(function(){
30 | $('#blanket-main')
31 | .css('marginTop', '10px')
32 | .addClass('col-lg-8 col-lg-push-2')
33 | .find('.bl-file a').each(function(){
34 | this.innerHTML = this.innerHTML.replace(/(.*)\/src\/(.*)$/, '$2');
35 | });
36 | });
37 |
38 |
39 | /**
40 | * Custom assert to compare rules objects
41 | */
42 | QUnit.assert.rulesMatch = function(actual, expected, message) {
43 | var ok = (function match(a, b){
44 | var ok = true;
45 |
46 | if (a.hasOwnProperty('valid') && b.hasOwnProperty('valid')) {
47 | ok = QUnit.equiv(a.valid, b.valid);
48 | }
49 |
50 | if (b.hasOwnProperty('data')) {
51 | if (!a.hasOwnProperty('data')) {
52 | ok = false;
53 | }
54 | else {
55 | ok = QUnit.equiv(a.data, b.data);
56 | }
57 | }
58 |
59 | if (b.hasOwnProperty('flags')) {
60 | if (!a.hasOwnProperty('flags')) {
61 | ok = false;
62 | }
63 | else {
64 | ok = QUnit.equiv(a.flags, b.flags);
65 | }
66 | }
67 |
68 | if (b.hasOwnProperty('rules')) {
69 | if (!a.hasOwnProperty('rules')) {
70 | ok = false;
71 | }
72 | else {
73 | for (var i=0, l=a.rules.length; i
2 |
3 |
4 | jQuery-QueryBuilder
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/tests/plugins-gui.module.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | var $b = $('#builder');
3 |
4 | QUnit.module('plugins-gui', {
5 | afterEach: function() {
6 | $b.queryBuilder('destroy');
7 | }
8 | });
9 |
10 | /**
11 | * Test bt-checkbox
12 | */
13 | QUnit.test('bt-checkbox', function(assert) {
14 | $b.queryBuilder({
15 | plugins: ['bt-checkbox'],
16 | filters: [{
17 | id: 'no-color',
18 | type: 'integer',
19 | input: 'checkbox',
20 | values: {
21 | 10: 'foo',
22 | 20: 'bar'
23 | }
24 | }, {
25 | id: 'one-color',
26 | type: 'integer',
27 | input: 'checkbox',
28 | values: {
29 | 1: 'one',
30 | 2: 'two',
31 | 3: 'three'
32 | },
33 | color: 'primary'
34 | }, {
35 | id: 'multi-color',
36 | type: 'integer',
37 | input: 'radio',
38 | values: {
39 | 0: 'no',
40 | 1: 'yes',
41 | 2: 'perhaps'
42 | },
43 | colors: {
44 | 0: 'danger',
45 | 1: 'success'
46 | }
47 | }],
48 | rules: {
49 | condition: 'AND',
50 | rules: [{
51 | id: 'no-color',
52 | value: 10
53 | }, {
54 | id: 'one-color',
55 | value: [1,2,3]
56 | }, {
57 | id: 'multi-color',
58 | value: 2
59 | }]
60 | }
61 | });
62 |
63 | assert.ok(
64 | $('#builder_rule_0 .checkbox.checkbox-default').length == 2,
65 | 'Should have 2 checkboxes with default color'
66 | );
67 |
68 | assert.ok(
69 | $('#builder_rule_1 .checkbox.checkbox-primary').length == 3,
70 | 'Should have 3 checkboxes with primary color'
71 | );
72 |
73 | assert.ok(
74 | $('#builder_rule_2 .radio.radio-danger').length == 1 &&
75 | $('#builder_rule_2 .radio.radio-success').length == 1 &&
76 | $('#builder_rule_2 .radio.radio-default').length == 1,
77 | 'Should have 3 radios with danger, success and default colors'
78 | );
79 | });
80 |
81 | /**
82 | * Test chosen-selectpicker
83 | */
84 | QUnit.test('chosen-selectpicker', function(assert) {
85 | $b.queryBuilder({
86 | plugins: ['chosen-selectpicker'],
87 | filters: basic_filters,
88 | rules: basic_rules
89 | });
90 |
91 | assert.ok(
92 | $b.find('.chosen-single').length == 8,
93 | 'Should have initialized chosen on all filters and operators selectors'
94 | );
95 | });
96 |
97 |
98 | /**
99 | * Test bt-tooltip-errors
100 | */
101 | QUnit.test('bt-tooltip-errors', function(assert) {
102 | $b.queryBuilder({
103 | plugins: ['bt-tooltip-errors'],
104 | filters: basic_filters,
105 | rules: {
106 | condition: 'AND',
107 | rules: [{
108 | id: 'id',
109 | operator: 'equal',
110 | value: ''
111 | }]
112 | }
113 | });
114 |
115 | $b.queryBuilder('validate');
116 |
117 | assert.equal(
118 | $('#builder_group_0 .error-container').eq(0).data('bs-toggle'),
119 | 'tooltip',
120 | 'Should have added data-bs-toggle="tooltip" in the template'
121 | );
122 |
123 | assert.equal(
124 | $('#builder_rule_0 .error-container').data('originalTitle'),
125 | 'Empty value',
126 | 'Error title should be "Empty value"'
127 | );
128 | });
129 |
130 | /**
131 | * Test filter-description
132 | */
133 | QUnit.test('filter-description', function(assert) {
134 | var filters = [{
135 | id: 'name',
136 | type: 'string',
137 | description: 'Lorem Ipsum sit amet.'
138 | }, {
139 | id: 'age',
140 | type: 'integer',
141 | description: function(rule) {
142 | return 'Description of operator ' + rule.operator.type;
143 | }
144 | }];
145 |
146 | var rules = {
147 | condition: 'AND',
148 | rules: [{
149 | id: 'name',
150 | value: 'Mistic'
151 | }, {
152 | id: 'age',
153 | value: 25
154 | }]
155 | };
156 |
157 | $b.queryBuilder({
158 | plugins: {
159 | 'filter-description': { mode: 'inline' }
160 | },
161 | filters: filters,
162 | rules: rules
163 | });
164 |
165 | assert.match(
166 | $('#builder_rule_0 p.filter-description').html(),
167 | new RegExp('Lorem Ipsum sit amet.'),
168 | 'Paragraph should contain filter description'
169 | );
170 |
171 | assert.match(
172 | $('#builder_rule_1 p.filter-description').html(),
173 | new RegExp('Description of operator equal'),
174 | 'Paragraph should contain filter description after function execution'
175 | );
176 |
177 | $b.queryBuilder('destroy');
178 |
179 | $b.queryBuilder({
180 | plugins: {
181 | 'filter-description': { mode: 'popover' }
182 | },
183 | filters: filters,
184 | rules: rules
185 | });
186 |
187 | assert.ok(
188 | $('#builder_rule_0 button.filter-description').data('bs-toggle') == 'popover',
189 | 'Rule should contain a new button enabled with Popover'
190 | );
191 |
192 | $b.queryBuilder('destroy');
193 |
194 | $b.queryBuilder({
195 | plugins: {
196 | 'filter-description': { mode: 'bootbox' }
197 | },
198 | filters: filters,
199 | rules: rules
200 | });
201 |
202 | assert.ok(
203 | $('#builder_rule_0 button.filter-description').data('bs-toggle') == 'bootbox',
204 | 'Rule should contain a new button enabled with Bootbox'
205 | );
206 | });
207 |
208 | /**
209 | * Test sortable
210 | */
211 | QUnit.test('sortable', function(assert) {
212 | $b.queryBuilder({
213 | filters: basic_filters,
214 | rules: basic_rules,
215 | plugins: ['sortable']
216 | });
217 |
218 | assert.ok(
219 | $b.find('.drag-handle').length > 0,
220 | 'Should add the drag handles'
221 | );
222 |
223 | $b.queryBuilder('destroy');
224 |
225 | $b.queryBuilder({
226 | plugins: {
227 | 'sortable': {disable_template: true}
228 | },
229 | filters: basic_filters,
230 | rules: basic_rules
231 | });
232 |
233 | assert.ok(
234 | $b.find('.drag-handle').length === 0,
235 | 'Should not have added the handles with disable_template=true'
236 | );
237 | });
238 | });
239 |
--------------------------------------------------------------------------------
/tests/plugins.module.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | var $b = $('#builder');
3 |
4 | QUnit.module('plugins', {
5 | afterEach: function() {
6 | $b.queryBuilder('destroy');
7 | }
8 | });
9 |
10 | /**
11 | * Test plugins loading
12 | */
13 | QUnit.test('Plugins loading', function(assert) {
14 | assert.ok(QueryBuilder.prototype.getSQL !== undefined, 'Should load SQL plugin automatically');
15 |
16 | $b.queryBuilder({
17 | filters: basic_filters,
18 | plugins: ['bt-tooltip-errors', 'filter-description']
19 | });
20 |
21 | assert.deepEqual(
22 | $b[0].queryBuilder.plugins['bt-tooltip-errors'],
23 | QueryBuilder.plugins['bt-tooltip-errors'].def,
24 | 'Should load "bt-tooltip-errors" with default config'
25 | );
26 |
27 | assert.deepEqual(
28 | $b[0].queryBuilder.plugins['filter-description'],
29 | QueryBuilder.plugins['filter-description'].def,
30 | 'Should load "filter-description" with default config'
31 | );
32 |
33 | $b.queryBuilder('destroy');
34 |
35 | $b.queryBuilder({
36 | filters: basic_filters,
37 | plugins: {
38 | 'bt-tooltip-errors': null,
39 | 'filter-description': { icon: 'fa fa-info' }
40 | }
41 | });
42 |
43 | assert.deepEqual(
44 | $b[0].queryBuilder.plugins['bt-tooltip-errors'],
45 | QueryBuilder.plugins['bt-tooltip-errors'].def,
46 | 'Should load "bt-tooltip-errors" with default config'
47 | );
48 |
49 | assert.deepEqual(
50 | $b[0].queryBuilder.plugins['filter-description'],
51 | { icon: 'fa fa-info', mode: 'popover' },
52 | 'Should load "filter-description" with custom config'
53 | );
54 |
55 | $b.queryBuilder('destroy');
56 |
57 | assert.throws(
58 | function(){
59 | $b.queryBuilder({
60 | filters: basic_filters,
61 | plugins: ['__unknown__']
62 | });
63 | },
64 | /Unable to find plugin "__unknown__"/,
65 | 'Should throw error on unknown plugin'
66 | );
67 | });
68 |
69 | /**
70 | * Test unique-filter
71 | */
72 | QUnit.test('unique-filter', function(assert) {
73 | var unique_filters = $.extend(true, [], basic_filters);
74 | unique_filters[3].unique = 'group';
75 | unique_filters[4].unique = true;
76 |
77 | $b.queryBuilder({
78 | plugins: ['unique-filter'],
79 | filters: unique_filters,
80 | rules: basic_rules
81 | });
82 |
83 | assert.ok(
84 | $('select[name=builder_rule_0_filter] option[value=id]').is(':disabled') &&
85 | $('select[name=builder_rule_1_filter] option[value=id]').is(':disabled') &&
86 | $('select[name=builder_rule_2_filter] option[value=id]').is(':disabled'),
87 | '"Identifier" filter should be disabled everywhere'
88 | );
89 |
90 | assert.ok(
91 | $('select[name=builder_rule_1_filter] option[value=price]').is(':disabled') &&
92 | !$('select[name=builder_rule_2_filter] option[value=price]').is(':disabled') &&
93 | !$('select[name=builder_rule_3_filter] option[value=price]').is(':disabled'),
94 | '"Price" filter should be disabled in his group only'
95 | );
96 | });
97 |
98 | /**
99 | * Test inversion
100 | */
101 | QUnit.test('invert', function(assert) {
102 | $b.queryBuilder({
103 | plugins: ['invert'],
104 | filters: basic_filters,
105 | rules: basic_rules
106 | });
107 |
108 | $b.queryBuilder('invert');
109 |
110 | assert.rulesMatch(
111 | $b.queryBuilder('getRules'),
112 | {
113 | condition: 'OR',
114 | rules: [{
115 | id: 'price',
116 | field: 'price',
117 | operator: 'greater_or_equal',
118 | value: 10.25
119 | }, {
120 | id: 'name',
121 | field: 'name',
122 | operator: 'is_not_null',
123 | value: null
124 | }, {
125 | condition: 'AND',
126 | rules: [{
127 | id: 'category',
128 | field: 'category',
129 | operator: 'not_in',
130 | value: ['mo', 'mu']
131 | }, {
132 | id: 'id',
133 | field: 'id',
134 | operator: 'equal',
135 | value: '1234-azer-5678'
136 | }]
137 | }]
138 | },
139 | 'Should have inverted all conditions and operators'
140 | );
141 |
142 | $b.queryBuilder('destroy');
143 |
144 | $b.queryBuilder({
145 | plugins: {
146 | invert: {disable_template: true}
147 | },
148 | filters: basic_filters,
149 | rules: basic_rules
150 | });
151 |
152 | assert.ok(
153 | $b.find('[data-invert="group"]').length === 0,
154 | 'Should not have added the button with disable_template=true'
155 | );
156 | });
157 |
158 | /**
159 | * Test change filters
160 | */
161 | QUnit.test('change-filters', function(assert) {
162 | var filter_a = {
163 | id: 'a',
164 | type: 'string'
165 | };
166 |
167 | var filter_b = {
168 | id: 'b',
169 | type: 'string'
170 | };
171 |
172 | var filter_c = {
173 | id: 'c',
174 | type: 'string'
175 | };
176 |
177 | var rule_a = {
178 | id: 'a',
179 | field: 'a',
180 | operator: 'equal',
181 | value: 'foo'
182 | };
183 |
184 | var rule_b = {
185 | id: 'b',
186 | field: 'b',
187 | operator: 'equal',
188 | value: 'bar'
189 | };
190 |
191 | $b.queryBuilder({
192 | filters: [filter_a, filter_b],
193 | rules: [rule_a, rule_b]
194 | });
195 |
196 | assert.throws(
197 | function(){
198 | $b.queryBuilder('removeFilter', 'a');
199 | },
200 | /A rule is using filter "a"/,
201 | 'Should throw error when deleting filter "a" w/o force'
202 | );
203 |
204 | $b.queryBuilder('removeFilter', 'a', true);
205 |
206 | assert.rulesMatch(
207 | $b.queryBuilder('getRules'),
208 | {condition:'AND', rules: [rule_b]},
209 | 'Should have deleted rule using filter "a"'
210 | );
211 |
212 | $b.queryBuilder('addFilter', filter_c, 0);
213 |
214 | assert.optionsMatch(
215 | $('#builder_rule_1 [name$=_filter] option'),
216 | ['-1', filter_c.id, filter_b.id],
217 | 'Should have added filter "c" at begining'
218 | );
219 |
220 | $b.queryBuilder('addFilter', filter_a, 'c');
221 |
222 | assert.optionsMatch(
223 | $('#builder_rule_1 [name$=_filter] option'),
224 | ['-1', filter_c.id, filter_a.id, filter_b.id],
225 | 'Should have added filter "a" after "c"'
226 | );
227 | });
228 | });
229 |
--------------------------------------------------------------------------------
/tests/plugins.mongo-support.module.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | var $b = $('#builder');
3 |
4 | QUnit.module('plugins.mongo-support', {
5 | afterEach: function() {
6 | $b.queryBuilder('destroy');
7 | }
8 | });
9 |
10 | QUnit.test('Basics', function(assert) {
11 | var basic_rules_mongodb = {
12 | '$and': [
13 | {'price': {'$lt': 10.25}},
14 | {'name': null},
15 | {
16 | '$or': [
17 | {'category': {'$in': ['mo', 'mu']}},
18 | {'id': {'$ne': '1234-azer-5678'}}
19 | ]
20 | }
21 | ]
22 | };
23 |
24 | $b.queryBuilder({
25 | filters: basic_filters,
26 | rules: basic_rules
27 | });
28 |
29 | assert.deepEqual(
30 | $b.queryBuilder('getMongo'),
31 | basic_rules_mongodb,
32 | 'Should create MongoDB query'
33 | );
34 |
35 | assert.deepEqual(
36 | $b.queryBuilder('getRulesFromMongo', basic_rules_mongodb),
37 | basic_rules,
38 | 'Should return rules object from MongoDB query'
39 | );
40 | });
41 |
42 | QUnit.test('All operators', function(assert) {
43 | $b.queryBuilder({
44 | filters: basic_filters,
45 | rules: all_operators_rules
46 | });
47 |
48 | assert.deepEqual(
49 | $b.queryBuilder('getMongo'),
50 | all_operators_rules_mongodb,
51 | 'Should successfully convert all kind of operators to MongoDB'
52 | );
53 |
54 | $b.queryBuilder('reset');
55 |
56 | $b.queryBuilder('setRulesFromMongo', all_operators_rules_mongodb);
57 |
58 | assert.rulesMatch(
59 | $b.queryBuilder('getRules'),
60 | all_operators_rules,
61 | 'Should successfully parse all kind of operators from MongoDB'
62 | );
63 | });
64 |
65 | QUnit.test('Automatically use filter from field', function(assert) {
66 | var rules = {
67 | condition: 'AND',
68 | rules: [
69 | {
70 | id: 'name',
71 | operator: 'equal',
72 | value: 'Mistic'
73 | }
74 | ]
75 | };
76 |
77 | var mongo = {
78 | $and: [{
79 | username: 'Mistic'
80 | }]
81 | };
82 |
83 | $b.queryBuilder({
84 | filters: [
85 | {
86 | id: 'name',
87 | field: 'username',
88 | type: 'string'
89 | },
90 | {
91 | id: 'last_days',
92 | field: 'display_date',
93 | type: 'integer'
94 | }
95 | ]
96 | });
97 |
98 | $b.queryBuilder('setRulesFromMongo', mongo);
99 |
100 | assert.rulesMatch(
101 | $b.queryBuilder('getRules'),
102 | rules,
103 | 'Should use "name" filter from "username" field'
104 | );
105 | });
106 |
107 |
108 | var all_operators_rules = {
109 | condition: 'AND',
110 | rules: [{
111 | id: 'name',
112 | operator: 'equal',
113 | value: 'foo'
114 | }, {
115 | id: 'name',
116 | operator: 'not_equal',
117 | value: 'foo'
118 | }, {
119 | id: 'category',
120 | operator: 'in',
121 | value: ['bk','mo']
122 | }, {
123 | id: 'category',
124 | operator: 'not_in',
125 | value: ['bk','mo']
126 | }, {
127 | id: 'price',
128 | operator: 'less',
129 | value: 5
130 | }, {
131 | id: 'price',
132 | operator: 'less_or_equal',
133 | value: 5
134 | }, {
135 | id: 'price',
136 | operator: 'greater',
137 | value: 4
138 | }, {
139 | id: 'price',
140 | operator: 'greater_or_equal',
141 | value: 4
142 | }, {
143 | id: 'price',
144 | operator: 'between',
145 | value: [4,5]
146 | }, {
147 | id: 'price',
148 | operator: 'not_between',
149 | value: [4,5]
150 | }, {
151 | id: 'name',
152 | operator: 'begins_with',
153 | value: 'foo'
154 | }, {
155 | id: 'name',
156 | operator: 'not_begins_with',
157 | value: 'foo'
158 | }, {
159 | id: 'name',
160 | operator: 'contains',
161 | value: 'foo'
162 | }, {
163 | id: 'name',
164 | operator: 'not_contains',
165 | value: 'foo'
166 | }, {
167 | id: 'name',
168 | operator: 'ends_with',
169 | value: 'foo'
170 | }, {
171 | id: 'name',
172 | operator: 'not_ends_with',
173 | value: 'foo'
174 | }, {
175 | id: 'name',
176 | operator: 'is_empty',
177 | value: null
178 | }, {
179 | id: 'name',
180 | operator: 'is_not_empty',
181 | value: null
182 | }, {
183 | id: 'name',
184 | operator: 'is_null',
185 | value: null
186 | }, {
187 | id: 'name',
188 | operator: 'is_not_null',
189 | value: null
190 | }]
191 | };
192 |
193 | var all_operators_rules_mongodb = {
194 | $and: [
195 | { name: 'foo' },
196 | { name: {$ne: 'foo'} },
197 | { category: { $in: ['bk','mo'] }},
198 | { category: { $nin: ['bk','mo'] }},
199 | { price: {$lt: 5} },
200 | { price: {$lte: 5} },
201 | { price: {$gt: 4} },
202 | { price: {$gte: 4} },
203 | { price: {$gte: 4, $lte: 5} },
204 | { price: {$lt: 4, $gt: 5} },
205 | { name: {$regex: '^foo'} },
206 | { name: {$regex: '^(?!foo)'} },
207 | { name: {$regex: 'foo'} },
208 | { name: {$regex: '^((?!foo).)*$', $options: 's'} },
209 | { name: {$regex: 'foo$'} },
210 | { name: {$regex: '(? 0,
19 | 'Should add "not" buttons"'
20 | );
21 |
22 | $('#builder_group_0>.rules-group-header [data-not=group]').trigger('click');
23 |
24 | assert.ok(
25 | $b.queryBuilder('getModel').not,
26 | 'The root group should have "not" flag set to true'
27 | );
28 |
29 | assert.ok(
30 | $b.queryBuilder('getRules').not,
31 | 'The root json should have "not" flag set to true'
32 | );
33 |
34 | $b.queryBuilder('destroy');
35 |
36 | $b.queryBuilder({
37 | plugins: {
38 | 'not-group': {disable_template: true}
39 | },
40 | filters: basic_filters,
41 | rules: basic_rules
42 | });
43 |
44 | assert.ok(
45 | $b.find('[data-not="group"]').length === 0,
46 | 'Should not have added the button with disable_template=true'
47 | );
48 | });
49 |
50 | QUnit.test('SQL export', function (assert) {
51 | $b.queryBuilder({
52 | filters: basic_filters,
53 | rules: rules,
54 | plugins: ['not-group']
55 | });
56 |
57 | assert.equal(
58 | $b.queryBuilder('getSQL').sql,
59 | sql,
60 | 'Should export SQL with NOT function'
61 | );
62 | });
63 |
64 | QUnit.test('SQL import', function (assert) {
65 | $b.queryBuilder({
66 | filters: basic_filters,
67 | plugins: ['not-group']
68 | });
69 |
70 | $b.queryBuilder('setRulesFromSQL', sql);
71 |
72 | assert.rulesMatch(
73 | $b.queryBuilder('getRules'),
74 | rules,
75 | 'Should parse NOT SQL function'
76 | );
77 |
78 | $b.queryBuilder('setRulesFromSQL', sql2);
79 |
80 | assert.rulesMatch(
81 | $b.queryBuilder('getRules'),
82 | rules2,
83 | 'Should parse NOT SQL function with only one rule'
84 | );
85 |
86 | $b.queryBuilder('setRulesFromSQL', sql3);
87 |
88 | assert.rulesMatch(
89 | $b.queryBuilder('getRules'),
90 | rules3,
91 | 'Should parse NOT SQL function with same operation'
92 | );
93 |
94 | $b.queryBuilder('setRulesFromSQL', sql4);
95 |
96 | assert.rulesMatch(
97 | $b.queryBuilder('getRules'),
98 | rules4,
99 | 'Should parse NOT SQL function with negated root group'
100 | );
101 |
102 | $b.queryBuilder('setRulesFromSQL', sql5);
103 |
104 | assert.rulesMatch(
105 | $b.queryBuilder('getRules'),
106 | rules5,
107 | 'Should parse NOT SQL function with double negated root group'
108 | );
109 | });
110 |
111 | QUnit.test('Mongo export', function (assert) {
112 | $b.queryBuilder({
113 | filters: basic_filters,
114 | rules: rules,
115 | plugins: ['not-group']
116 | });
117 |
118 | assert.deepEqual(
119 | $b.queryBuilder('getMongo'),
120 | mongo,
121 | 'Should export MongoDB with $nor function'
122 | );
123 |
124 | $b.queryBuilder('reset');
125 |
126 | $b.queryBuilder('setRulesFromMongo', mongo);
127 |
128 | assert.rulesMatch(
129 | $b.queryBuilder('getRules'),
130 | rules,
131 | 'Should parse $nor MongoDB function'
132 | );
133 | });
134 |
135 | var rules = {
136 | condition: 'OR',
137 | not: false,
138 | rules: [{
139 | id: 'name',
140 | operator: 'equal',
141 | value: 'Mistic'
142 | }, {
143 | condition: 'AND',
144 | not: true,
145 | rules: [{
146 | id: 'price',
147 | operator: 'less',
148 | value: 10.25
149 | }, {
150 | id: 'category',
151 | field: 'category',
152 | operator: 'in',
153 | value: ['mo', 'mu']
154 | }]
155 | }]
156 | };
157 |
158 | var sql = 'name = \'Mistic\' OR ( NOT ( price < 10.25 AND category IN(\'mo\', \'mu\') ) ) ';
159 |
160 | var rules2 = {
161 | condition: 'OR',
162 | not: false,
163 | rules: [{
164 | id: 'name',
165 | operator: 'equal',
166 | value: 'Mistic'
167 | }, {
168 | condition: 'AND',
169 | not: true,
170 | rules: [{
171 | id: 'price',
172 | operator: 'less',
173 | value: 10.25
174 | }]
175 | }]
176 | };
177 |
178 | var sql2 = 'name = \'Mistic\' OR ( NOT ( price < 10.25 ) ) ';
179 |
180 | var rules3 = {
181 | condition: 'OR',
182 | not: false,
183 | rules: [{
184 | id: 'name',
185 | operator: 'equal',
186 | value: 'Mistic'
187 | }, {
188 | condition: 'OR',
189 | not: true,
190 | rules: [{
191 | id: 'price',
192 | operator: 'less',
193 | value: 10.25
194 | }, {
195 | id: 'category',
196 | field: 'category',
197 | operator: 'in',
198 | value: ['mo', 'mu']
199 | }]
200 | }]
201 | };
202 |
203 | var sql3 = 'name = \'Mistic\' OR ( NOT ( price < 10.25 OR category IN(\'mo\', \'mu\') ) ) ';
204 |
205 | var rules4 = {
206 | condition: 'AND',
207 | not: true,
208 | rules: [{
209 | id: 'price',
210 | operator: 'less',
211 | value: 10.25
212 | }]
213 | };
214 |
215 | var sql4 = 'NOT ( price < 10.25 )';
216 |
217 | var rules5 = {
218 | condition: 'AND',
219 | not: false,
220 | rules: [{
221 | condition: 'AND',
222 | not: true,
223 | rules: [{
224 | id: 'price',
225 | operator: 'less',
226 | value: 10.25
227 | }]
228 | }, {
229 | condition: 'AND',
230 | not: true,
231 | rules: [{
232 | id: 'price',
233 | operator: 'greater',
234 | value: 20.5
235 | }]
236 | }]
237 | };
238 |
239 | var sql5 = 'NOT ( price < 10.25 ) AND NOT ( price > 20.5 )';
240 |
241 | var mongo = {
242 | "$or": [{
243 | "name": "Mistic"
244 | },
245 | {
246 | "$nor": [{
247 | "$and": [{
248 | "price": {"$lt": 10.25}
249 | }, {
250 | "category": {"$in": ["mo", "mu"]}
251 | }]
252 | }]
253 | }]
254 | };
255 |
256 | });
257 |
--------------------------------------------------------------------------------
/tests/utils.module.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 |
3 | QUnit.module('utils');
4 |
5 | /**
6 | * Test iterateOptions
7 | */
8 | QUnit.test('iterateOptions', function (assert) {
9 | var output;
10 | function callback(a, b, c) {
11 | output.push(a, b, c);
12 | }
13 |
14 | output = [];
15 | Utils.iterateOptions(['one', 'foo', 'bar'], callback);
16 | assert.deepEqual(
17 | output,
18 | ['one', 'one', undefined, 'foo', 'foo', undefined, 'bar', 'bar', undefined],
19 | 'Should iterate simple array'
20 | );
21 |
22 | output = [];
23 | Utils.iterateOptions({1: 'one', 2: 'foo', 3: 'bar'}, callback);
24 | assert.deepEqual(
25 | output,
26 | ['1', 'one', undefined, '2', 'foo', undefined, '3', 'bar', undefined],
27 | 'Should iterate simple hash-map'
28 | );
29 |
30 | output = [];
31 | Utils.iterateOptions([{1: 'one'}, {2: 'foo'}, {3: 'bar'}], callback);
32 | assert.deepEqual(
33 | output,
34 | ['1', 'one', undefined, '2', 'foo', undefined, '3', 'bar', undefined],
35 | 'Should iterate array of one element hash-maps'
36 | );
37 |
38 | output = [];
39 | Utils.iterateOptions([{value: 1, label: 'one', optgroup: 'group'}, {value: 2, label: 'foo'}, {value: 3, label: 'bar', optgroup: 'group'}], callback);
40 | assert.deepEqual(
41 | output,
42 | [1, 'one', 'group', 2, 'foo', undefined, 3, 'bar', 'group'],
43 | 'Should iterate array of hash-maps'
44 | );
45 | });
46 |
47 | /**
48 | * Test groupSort
49 | */
50 | QUnit.test('groupSort', function (assert) {
51 | var input = [
52 | {id: '1'},
53 | {id: '1.1', group: '1'},
54 | {id: '2'},
55 | {id: '2.1', group: '2'},
56 | {id: '1.2', group: '1'},
57 | {id: '2.2', group: '2'},
58 | {id: '3'},
59 | {id: '1.3', group: '1'}
60 | ];
61 |
62 | var output = [
63 | {id: '1'},
64 | {id: '1.1', group: '1'},
65 | {id: '1.2', group: '1'},
66 | {id: '1.3', group: '1'},
67 | {id: '2'},
68 | {id: '2.1', group: '2'},
69 | {id: '2.2', group: '2'},
70 | {id: '3'}
71 | ];
72 |
73 | assert.deepEqual(
74 | Utils.groupSort(input, 'group'),
75 | output,
76 | 'Should sort items by group'
77 | );
78 | });
79 |
80 | /**
81 | * Test fmt
82 | */
83 | QUnit.test('fmt', function (assert) {
84 | assert.equal(
85 | Utils.fmt('{0} is equal to {1}', 1, 'one'),
86 | '1 is equal to one',
87 | 'Should replace arguments'
88 | );
89 |
90 | assert.equal(
91 | Utils.fmt('{0} is equal to {0}', 1),
92 | '1 is equal to 1',
93 | 'Should replace one argument multiple times'
94 | );
95 | });
96 |
97 | /**
98 | * Test changeType
99 | */
100 | QUnit.test('changeType', function (assert) {
101 | assert.ok(
102 | Utils.changeType('10', 'integer') === 10,
103 | '"10" should be parsed as integer'
104 | );
105 |
106 | assert.ok(
107 | Utils.changeType('10.5', 'double') === 10.5,
108 | '"10.5" should be parsed as double'
109 | );
110 |
111 | assert.ok(
112 | Utils.changeType('true', 'boolean') === true,
113 | '"true" should be parsed as boolean'
114 | );
115 | });
116 |
117 | /**
118 | * Test escapeElementId
119 | */
120 | QUnit.test('escapeElementId', function (assert) {
121 | assert.equal(
122 | Utils.escapeElementId('abc123'),
123 | 'abc123',
124 | 'Should not alter id'
125 | );
126 |
127 | var chars = ':.[],';
128 | for (var i = 0; i < chars.length; ++i) {
129 | assert.equal(
130 | Utils.escapeElementId('abc' + chars[i] + '123'),
131 | 'abc\\' + chars[i] + '123',
132 | 'Should escape \'' + chars[i] + '\' in id'
133 | );
134 |
135 | assert.equal(
136 | Utils.escapeElementId('abc\\' + chars[i] + '123'),
137 | 'abc\\' + chars[i] + '123',
138 | 'Should not escape \'\\' + chars[i] + '\' in id'
139 | );
140 |
141 | assert.equal(
142 | Utils.escapeElementId(chars[i] + 'abc123'),
143 | '\\' + chars[i] + 'abc123',
144 | 'Should escape \'' + chars[i] + '\' prefixing id'
145 | );
146 |
147 | assert.equal(
148 | Utils.escapeElementId('\\' + chars[i] + 'abc123'),
149 | '\\' + chars[i] + 'abc123',
150 | 'Should not escape \'\\' + chars[i] + '\' prefixing id'
151 | );
152 |
153 | assert.equal(
154 | Utils.escapeElementId('abc123' + chars[i]),
155 | 'abc123\\' + chars[i],
156 | 'Should escape \'' + chars[i] + '\' trailing in id'
157 | );
158 |
159 | assert.equal(
160 | Utils.escapeElementId('abc123\\' + chars[i]),
161 | 'abc123\\' + chars[i],
162 | 'Should not escape \'\\' + chars[i] + '\' trailing in id'
163 | );
164 | }
165 | });
166 | });
167 |
--------------------------------------------------------------------------------