Code

Adding missing files from last commit (r4575) for fixing expressing filters
[roundup.git] / roundup / cgi / KeywordsExpr.py
1 # This module is free software, you may redistribute it
2 # and/or modify under the same terms as Python.
4 WINDOW_CONTENT = '''\
5 <h3>Keyword Expression Editor:</h3>
6 <hr/>
7 <div id="content"></div>
8 <script type="text/javascript">
9 <!--
11 var NOT_OP = "-2";
12 var AND_OP = "-3";
13 var OR_OP  = "-4";
15 var original = "%(original)s";
16 var current = original;
17 var undo = [];
19 var KEYWORDS = [
20     %(keywords)s
21 ];
23 function find_keyword(x) {
24     for (var i = 0; i < KEYWORDS.length; ++i) {
25         if (KEYWORDS[i][0] == x) {
26             return KEYWORDS[i][1];
27         }
28     }
29     return "unknown";
30 }
32 function Equals(x) {
33     this.x = x;
34     this.brackets = false;
36     this.infix = function() {
37         return find_keyword(this.x);
38     }
40     this.postfix = function() {
41         return this.x;
42     }
43 }
45 function Not(x) {
46     this.x = x;
47     this.brackets = false;
49     this.infix = function() {
50         return this.x.brackets 
51             ? "NOT(" + this.x.infix() + ")"
52             : "NOT " + this.x.infix();
53     }
55     this.postfix = function() {
56         return this.x.postfix() + "," + NOT_OP;
57     }
58 }
60 function And(x, y) {
61     this.x = x;
62     this.y = y;
63     this.brackets = true;
65     this.infix = function() {
66         var a = this.x.brackets ? "(" + this.x.infix() + ")" : this.x.infix();
67         var b = this.y.brackets ? "(" + this.y.infix() + ")" : this.y.infix();
68         return a + " AND " + b;
69     }
70     this.postfix = function() {
71         return this.x.postfix() + "," + this.y.postfix() + "," + AND_OP;
72     }
73 }
75 function Or(x, y) {
76     this.x = x;
77     this.y = y;
78     this.brackets = true;
80     this.infix = function() {
81         var a = this.x.brackets ? "(" + this.x.infix() + ")" : this.x.infix();
82         var b = this.y.brackets ? "(" + this.y.infix() + ")" : this.y.infix();
83         return a + " OR " + b;
84     }
86     this.postfix = function() {
87         return this.x.postfix() + "," + this.y.postfix() + "," + OR_OP;
88     }
89 }
91 function trim(s) {
92     return s.replace (/^\s+/, '').replace(/\s+$/, '');
93 }
95 function parse(s) {
96     var operators = s.split(",");
97     var stack = [];
98     for (var i = 0; i < operators.length; ++i) {
99         var operator = trim(operators[i]);
100         if (operator == "") continue;
101         if (operator == NOT_OP) {
102             stack.push(new Not(stack.pop()));
103         }
104         else if (operator == AND_OP) {
105             var a = stack.pop();
106             var b = stack.pop();
107             stack.push(new And(b, a));
108         }
109         else if (operator == OR_OP) {
110             var a = stack.pop();
111             var b = stack.pop();
112             stack.push(new Or(b, a));
113         }
114         else {
115             stack.push(new Equals(operator));
116         }
117     }
118     return stack.length > 0 ? stack.pop() : null;
121 function render_select(handler) {
122     var out = '<select name="keyword" id="keyword"';
123     if (handler != null) {
124         out += ' onchange="' + handler + '"';
125     }
126     out += '>';
127     out += '<option value="-1"><\/option>';
128     for (var i = 0; i < KEYWORDS.length; ++i) {
129         out += '<option value="' + KEYWORDS[i][0] + 
130                '">' + KEYWORDS[i][1] + "<\/option>";
131     }
132     out += '<\/select>';
133     return out;
136 function first_select() {
137     var value = document.getElementById("keyword").value;
138     current = value;
139     set_content();
142 function not_clicked() {
143     var expr = parse(current);
144     if (expr == null) return;
145     undo.push(current);
146     current = expr instanceof Not
147         ? expr.x.postfix()
148         : new Not(expr).postfix();
149     set_content();
152 function not_b_wrap(expr) {
153     var value = document.getElementById("not_b").checked;
154     return value ? new Not(expr) : expr;
157 function and_clicked() {
158     var expr = parse(current);
159     if (expr == null) return;
160     var value = document.getElementById("keyword").value;
161     if (value == "-1") return;
162     undo.push(current);
163     current = new And(expr, not_b_wrap(new Equals(value))).postfix();
164     set_content();
167 function or_clicked() {
168     var expr = parse(current);
169     if (expr == null) return;
170     var value = document.getElementById("keyword").value;
171     if (value == "-1") return;
172     undo.push(current);
173     current = new Or(expr, not_b_wrap(new Equals(value))).postfix();
174     set_content();
177 function undo_clicked() {
178     current = undo.length > 0 
179         ? undo.pop()
180         : original;
181     set_content();
184 function enable_and_or() {
185     var value = document.getElementById("keyword").value;
186     value = value == "-1";
187     document.getElementById("and").disabled = value;
188     document.getElementById("or").disabled = value;
189     document.getElementById("not_b").disabled = value;
192 function create() {
193     var expr = parse(current);
194     var out = "";
195     if (expr == null) {
196         out += "Keyword: ";
197         out += render_select("first_select();");
198     }
199     else {
200         out += '<table><tr>'
201         out += '<td><input type="button" name="not" onclick="not_clicked();" value="NOT"\/><\/td>';
202         out += "<td><tt><strong>" + expr.infix() + "<\/strong><\/tt><\/td>";
203         out += '<td><table>';
204         out += '<tr><td><input type="button" id="and" name="and" onclick="and_clicked();"'
205             +  ' value="AND" disabled="disabled"\/><\/td><\/tr>';
206         out += '<tr><td><input type="button" id="or" name="or" onclick="or_clicked();"'
207             +  ' value="OR" disabled="disabled"\/><\/td><\/tr>';
208         out += '<\/table><\/td>';
209         out += '<td><label for="not_b">NOT<\/label><br/>'
210             +  '<input type="checkbox" name="not_b" id="not_b" disabled="disabled"\/><\/td>';
211         out += '<td>' + render_select("enable_and_or();") + '<\/td>';
212         out += '<\/tr><\/table>'
213     }
214     out += '<hr\/>';
215     if (undo.length > 0 || (undo.length == 0 && current != original)) {
216         out += '<input type="button" onclick="undo_clicked();" value="Undo"\/>';
217     }
218     out += '<input type="button" onclick="modify_main();" value="Apply"\/>'
219         +  '<input type="button" onclick="window.close();" value="Close Window"\/>';
220     return out;
223 function main_content() {
224     var out = '';
225     out += '<input type="hidden" name="%(prop)s" value="' + current + '"\/>';
226     out += parse(current).infix();
227     return out;
230 function modify_main() {
231     main = window.opener.document.getElementById("keywords_%(prop)s");
232     main.innerHTML = main_content();
235 function set_content() {
236     document.getElementById("content").innerHTML = create();
239 set_content();
240 //-->
241 </script>
242 '''
244 def list_nodes(request):
245     prop = request.form.getfirst("property")
246     cls = request.client.db.getclass(prop)
247     items = []
248     for nodeid in cls.getnodeids():
249         l = cls.getnode(nodeid).items()
250         l = dict([x for x in l if len(x) == 2])
251         try:
252             items.append((l['id'], l['name']))
253         except KeyError:
254             pass
255     items.sort(key=lambda x: int(x[0]))
256     return items
258 def items_to_keywords(items):
259     return ',\n    '.join(['["%s", "%s"]' % x for x in items])
260    
262 def render_keywords_expression_editor(request):
263     prop = request.form.getfirst("property")
265     window_content = WINDOW_CONTENT % {
266         'prop'    : prop,
267         'keywords': items_to_keywords(list_nodes(request)),
268         'original': ''
269     }
271     return window_content
273 # vim: set et sts=4 sw=4 :