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;
119 }
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;
134 }
136 function first_select() {
137 var value = document.getElementById("keyword").value;
138 current = value;
139 set_content();
140 }
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();
150 }
152 function not_b_wrap(expr) {
153 var value = document.getElementById("not_b").checked;
154 return value ? new Not(expr) : expr;
155 }
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();
165 }
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();
175 }
177 function undo_clicked() {
178 current = undo.length > 0
179 ? undo.pop()
180 : original;
181 set_content();
182 }
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;
190 }
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;
221 }
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;
228 }
230 function modify_main() {
231 main = window.opener.document.getElementById("keywords_%(prop)s");
232 main.innerHTML = main_content();
233 }
235 function set_content() {
236 document.getElementById("content").innerHTML = create();
237 }
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])
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 :