|
1 | 1 | /*!
|
2 |
| - * jQuery QueryBuilder 1.3.0-SNAPSHOT |
| 2 | + * jQuery QueryBuilder 1.3.0 |
3 | 3 | * Copyright 2014 Damien "Mistic" Sorel (http://www.strangeplanet.fr)
|
4 | 4 | * Licensed under MIT (http://opensource.org/licenses/MIT)
|
5 | 5 | */
|
|
39 | 39 | this.$el = $el;
|
40 | 40 |
|
41 | 41 | this.settings = merge(QueryBuilder.DEFAULTS, options);
|
42 |
| - this.status = { group_id: 0, rule_id: 0, generatedId: false }; |
| 42 | + this.status = { |
| 43 | + group_id: 0, |
| 44 | + rule_id: 0, |
| 45 | + generatedId: false, |
| 46 | + has_optgroup: false |
| 47 | + }; |
43 | 48 |
|
44 | 49 | this.filters = this.settings.filters;
|
45 | 50 | this.lang = this.settings.lang;
|
|
280 | 285 | * @return {object}
|
281 | 286 | */
|
282 | 287 | QueryBuilder.prototype.getRules = function() {
|
283 |
| - this.markRuleAsError(this.$el.find('.rule-container'), false); |
| 288 | + this.clearErrorMarks(); |
284 | 289 |
|
285 | 290 | var $group = this.$el.find('>.rules-group-container'),
|
286 | 291 | that = this;
|
|
300 | 305 | var filterId = that.getRuleFilter($rule);
|
301 | 306 |
|
302 | 307 | if (filterId == '-1') {
|
303 |
| - continue; |
| 308 | + that.markRuleAsError($rule, true); |
| 309 | + that.triggerValidationError('no_filter', $rule, null, null, null); |
| 310 | + return {}; |
304 | 311 | }
|
305 | 312 |
|
306 | 313 | var filter = that.getFilterById(filterId),
|
|
344 | 351 | }
|
345 | 352 |
|
346 | 353 | if (out.rules.length === 0) {
|
| 354 | + that.markRuleAsError($group, true); |
347 | 355 | that.triggerValidationError('empty_group', $group, null, null, null);
|
348 |
| - |
349 | 356 | return {};
|
350 | 357 | }
|
351 | 358 |
|
|
462 | 469 | filter.label = filter.field;
|
463 | 470 | }
|
464 | 471 |
|
| 472 | + that.status.has_optgroup|= !!filter.optgroup; |
| 473 | + if (!filter.optgroup) { |
| 474 | + filter.optgroup = null; |
| 475 | + } |
| 476 | + |
465 | 477 | switch (filter.type) {
|
466 | 478 | case 'string':
|
467 | 479 | filter.internalType = 'string';
|
|
482 | 494 | break;
|
483 | 495 | }
|
484 | 496 | });
|
| 497 | + |
| 498 | + if (this.status.has_optgroup) { |
| 499 | + this.filters.sort(function(a, b) { |
| 500 | + if (a.optgroup === null && b.optgroup === null) { |
| 501 | + return 0; |
| 502 | + } |
| 503 | + if (a.optgroup === null) { |
| 504 | + return 1; |
| 505 | + } |
| 506 | + return a.optgroup.localeCompare(b.optgroup); |
| 507 | + }); |
| 508 | + } |
485 | 509 | };
|
486 | 510 |
|
487 | 511 | /**
|
|
823 | 847 | };
|
824 | 848 |
|
825 | 849 | /**
|
826 |
| - * Add CSS for rule error |
827 |
| - * @param $rule {jQuery} (<li> element) |
| 850 | + * Add 'has-error' class for rule/group error |
| 851 | + * @param $element {jQuery} (<li> or <dl> element) |
828 | 852 | * @param status {bool}
|
829 | 853 | */
|
830 |
| - QueryBuilder.prototype.markRuleAsError = function($rule, status) { |
| 854 | + QueryBuilder.prototype.markRuleAsError = function($element, status) { |
831 | 855 | if (status) {
|
832 |
| - $rule.addClass('has-error'); |
| 856 | + $element.addClass('has-error'); |
833 | 857 | }
|
834 | 858 | else {
|
835 |
| - $rule.removeClass('has-error'); |
| 859 | + $element.removeClass('has-error'); |
836 | 860 | }
|
837 | 861 | };
|
838 | 862 |
|
| 863 | + /** |
| 864 | + * Remove 'has-error' from everythin |
| 865 | + */ |
| 866 | + QueryBuilder.prototype.clearErrorMarks = function() { |
| 867 | + this.$el.find('.has-error').removeClass('has-error'); |
| 868 | + }; |
| 869 | + |
839 | 870 | /**
|
840 | 871 | * Trigger a validation error event with custom params
|
841 | 872 | */
|
|
868 | 899 |
|
869 | 900 | var placeholder, src, isHandle = false;
|
870 | 901 |
|
| 902 | + // only init drag from drag handle |
871 | 903 | this.$el.on('mousedown', '.drag-handle', function(e) {
|
872 | 904 | isHandle = true;
|
873 | 905 | });
|
874 | 906 | this.$el.on('mouseup', '.drag-handle', function(e) {
|
875 | 907 | isHandle = false;
|
876 | 908 | });
|
877 | 909 |
|
| 910 | + // dragstart: create placeholder and hide current element |
878 | 911 | this.$el.on('dragstart', '[draggable]', function(e) {
|
879 | 912 | e.stopPropagation();
|
880 | 913 |
|
|
892 | 925 |
|
893 | 926 | // Chrome glitch (helper invisible if hidden immediately)
|
894 | 927 | setTimeout(function() {
|
895 |
| - src.hide(); |
| 928 | + src.hide(); |
896 | 929 | }, 0);
|
897 | 930 | }
|
898 | 931 | else {
|
899 | 932 | e.preventDefault();
|
900 | 933 | }
|
901 | 934 | });
|
902 | 935 |
|
| 936 | + // dragenter: move the placeholder |
903 | 937 | this.$el.on('dragenter', '[draggable]', function(e) {
|
904 | 938 | e.preventDefault();
|
905 | 939 | e.stopPropagation();
|
906 | 940 |
|
907 | 941 | var target = $(e.target), parent;
|
908 | 942 |
|
| 943 | + // on rule |
909 | 944 | parent = target.closest('.rule-container');
|
910 | 945 | if (parent.length) {
|
911 | 946 | placeholder.detach().insertAfter(parent);
|
912 | 947 | return;
|
913 | 948 | }
|
914 | 949 |
|
| 950 | + // on group header |
| 951 | + parent = target.closest('.rules-group-header'); |
| 952 | + if (parent.length) { |
| 953 | + parent = target.closest('.rules-group-container'); |
| 954 | + placeholder.detach().prependTo(parent.find('.rules-list').eq(0)); |
| 955 | + return; |
| 956 | + } |
| 957 | + |
| 958 | + // on group |
915 | 959 | parent = target.closest('.rules-group-container');
|
916 | 960 | if (parent.length) {
|
917 | 961 | placeholder.detach().appendTo(parent.find('.rules-list').eq(0));
|
918 | 962 | return;
|
919 | 963 | }
|
920 | 964 | });
|
921 | 965 |
|
| 966 | + // dragover: prevent glitches |
922 | 967 | this.$el.on('dragover', '[draggable]', function(e) {
|
923 | 968 | e.preventDefault();
|
924 | 969 | e.stopPropagation();
|
925 | 970 | });
|
926 | 971 |
|
| 972 | + // drop: move current element |
927 | 973 | this.$el.on('drop', function(e) {
|
928 | 974 | e.preventDefault();
|
929 | 975 | e.stopPropagation();
|
930 | 976 |
|
931 | 977 | var target = $(e.target), parent;
|
932 | 978 |
|
| 979 | + // on rule |
933 | 980 | parent = target.closest('.rule-container');
|
934 | 981 | if (parent.length) {
|
935 | 982 | src.detach().insertAfter(parent);
|
936 | 983 | return;
|
937 | 984 | }
|
938 | 985 |
|
| 986 | + // on group header |
| 987 | + parent = target.closest('.rules-group-header'); |
| 988 | + if (parent.length) { |
| 989 | + parent = target.closest('.rules-group-container'); |
| 990 | + src.detach().prependTo(parent.find('.rules-list').eq(0)); |
| 991 | + return; |
| 992 | + } |
| 993 | + |
| 994 | + // on group |
939 | 995 | parent = target.closest('.rules-group-container');
|
940 | 996 | if (parent.length) {
|
941 | 997 | src.detach().appendTo(parent.find('.rules-list').eq(0));
|
942 | 998 | return;
|
943 | 999 | }
|
944 | 1000 | });
|
945 | 1001 |
|
| 1002 | + // dragend: show current element and delete placeholder |
946 | 1003 | this.$el.on('dragend', '[draggable]', function(e) {
|
947 | 1004 | e.preventDefault();
|
948 | 1005 | e.stopPropagation();
|
|
1258 | 1315 | * @return {string}
|
1259 | 1316 | */
|
1260 | 1317 | QueryBuilder.prototype.getRuleFilterSelect = function(rule_id) {
|
| 1318 | + var optgroup = null; |
| 1319 | + |
1261 | 1320 | var h = '<select name="'+ rule_id +'_filter">';
|
1262 | 1321 | h+= '<option value="-1">'+ this.lang.filter_select_placeholder +'</option>';
|
1263 | 1322 |
|
1264 | 1323 | $.each(this.filters, function(i, filter) {
|
| 1324 | + if (optgroup != filter.optgroup) { |
| 1325 | + if (optgroup !== null) h+= '</optgroup>'; |
| 1326 | + optgroup = filter.optgroup; |
| 1327 | + if (optgroup !== null) h+= '<optgroup label="'+ optgroup +'">'; |
| 1328 | + } |
| 1329 | + |
1265 | 1330 | h+= '<option value="'+ filter.id +'">'+ filter.label +'</option>';
|
1266 | 1331 | });
|
1267 | 1332 |
|
| 1333 | + if (optgroup !== null) h+= '</optgroup>'; |
1268 | 1334 | h+= '</select>';
|
1269 | 1335 | return h;
|
1270 | 1336 | };
|
|
0 commit comments