/
jquery-query-map.js
130 lines (113 loc) · 4.55 KB
/
jquery-query-map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(function ($) {
/*
Query.
* The comparison operator is stuffed in the operands list because the CSS is beyond me.
%ol.root
%li.connective
.control
.expression
.operator
%ol
%li.comparison
%li.comparison
%li.connective
%li.comparison
.control
.expression
%ol
%li.field
%li.operator
%li.value
*/
$.fn.query_map = function (options) {
if (options.conditions == null || options.conditions == '') delete options.conditions;
var blank = {comparison: {}, connective: {}};
blank.comparison = {field: options.fields[0], comparison: options.comparisons[0], value: ''};
blank.connective[options.connectives[0]] = [blank.comparison];
options = $.extend({blank: blank, conditions: blank.connective}, options);
return this.each(function () {
var root = $('<ol />', {'class': 'root'}).appendTo(this);
$(operator(options)).appendTo(root);
});
function select(options, value) {
var select = $('<select />');
$.each(options, function (i, option) {
select.append($('<option />', {text: option}));
});
select.val(value);
return select;
}
function operator (options) {
var expression = connective(options);
return expression.length > 0 ? expression : comparison(options);
}
function connective (options) {
return $.map(options.connectives, function (name) {
if (!options.conditions[name]) return;
var op = $('<div />', {'class': 'operator'})
.append(select(options.connectives, name), ' of the following rules:');
var operands = $('<ol />', {'class': 'operands'});
$.each(options.conditions[name], function (i, condition) {
operands.append.apply(operands, operator($.extend(options, {conditions: condition})));
});
var expression = $('<div />', {'class': 'expression'}).append(op, operands);
return $('<li />', {'class': 'connective'}).append(control(options), expression);
});
}
function comparison (options) {
var conditions = options.conditions || {};
var field = $('<li />', {'class': 'field'}).append(select(options.fields, conditions.field));
var operator = $('<li />', {'class': 'operator'}).append(select(options.comparisons, conditions.comparison));
var value = $('<li />', {'class': 'value'}).append($('<input />', {type: 'text', val: conditions.value}));
var expression = $('<div />', {'class': 'expression'}).append($('<ol />').append(field, operator, value));
return [$('<li />', {'class': 'comparison'}).append(control(options), expression)];
}
// TODO:
// You can't remove the last condition in an connective.
// You can't remove the root connective.
function control (options) {
var add = $('<button />', {type: 'button', text: '+'}).click(function () {
var comparison = operator($.extend(options, {conditions: options.blank.comparison}));
var sibling = $(this).closest('li');
sibling.after.apply(sibling, comparison);
return false;
});
var del = $('<button />', {type: 'button', text: '-'}).click(function () {
$(this).closest('li').remove();
return false;
});
var sub = $('<button />', {type: 'button', text: '…'}).click(function () {
var connective = operator($.extend(options, {conditions: options.blank.connective}));
var sibling = $(this).closest('li');
sibling.after.apply(sibling, connective);
return false;
});
return $('<div />', {'class': 'control'}).append(add, del, sub);
}
};
$.query_map = {
parse: function (builder, options) {
return builder.query_map(options);
},
encode: function (builder) {
var conditions = {};
var operands = [];
var connective = builder.is('.connective') ? builder : $(builder.find('.connective').get(0));
connective.find('> .expression > ol > li').each(function () {
var op = $(this);
if (op.is('.connective')) {
operands.push($.query_map.encode(op));
}
else if (op.is('.comparison')) {
operands.push({
field: op.find('.field select').val(),
comparison: op.find('.operator select').val(),
value: op.find('.value input').val()
});
}
})
conditions[connective.find('> .expression > .operator select').val()] = operands;
return conditions;
}
};
})(jQuery);