]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/conify/global/config/composite_attr_entry_definition.js
LP#1269911 composite attributes admin UI
[working/Evergreen.git] / Open-ILS / web / js / ui / default / conify / global / config / composite_attr_entry_definition.js
1 dojo.require('dijit.Dialog');
2 dojo.require('dijit.form.Button');
3 dojo.require('dijit.form.FilteringSelect');
4 dojo.require('openils.PermaCrud');
5 dojo.require('openils.widget.AutoFieldWidget');
6
7 var recordAttrDefs = {};// full name => crad map
8 var codedValueMaps = {};// growing cache of id => ccvm
9 var compositeDef;       // the thing what we're building / editing
10 var nodeTree;           // internal composite attrs tree representation
11 var treeIndex = 0;      // internal composit attrs node index
12
13 var localeStrings = {}; // TODO: move to nls file
14 localeStrings.OR = "OR";
15 localeStrings.AND = "AND";
16 localeStrings.NOT = "NOT";
17
18 function drawPage() {
19     console.log('fetching ccvm ' + ccvmId);
20
21     var asyncReqs = 2;
22
23     new openils.PermaCrud().retrieve('ccvm', ccvmId, {
24         flesh : 1, 
25         flesh_fields : {ccvm : ['composite_def', 'ctype']},
26         oncomplete : function(r) {
27             map = openils.Util.readResponse(r);
28
29             // draw the names
30             dojo.byId('attr-def-name').innerHTML = 
31                 map.ctype().label();
32             dojo.byId('coded-value-map-name').innerHTML = 
33                 map.code() + ' / ' + map.value();
34
35             dojo.byId('return-to-ccvm').onclick = function() {
36                 location.href = oilsBasePath + 
37                 '/conify/global/config/coded_value_map/' + 
38                 map.ctype().name();
39             };
40
41             // build a new def if needed
42             compositeDef = map.composite_def();
43             if (!compositeDef) {
44                 compositeDef = new fieldmapper.ccraed();
45                 compositeDef.isnew(true);
46                 compositeDef.coded_value(map.id());
47             }
48             if (!--asyncReqs) drawCompositDef();
49         }
50     });
51
52     new openils.PermaCrud().retrieveAll('crad', {
53         order_by : {crad : ['name']},
54         oncomplete : function(r) {
55             var defs = openils.Util.readResponse(r); 
56             dojo.forEach(defs, function(def) {
57                 recordAttrDefs[def.name()] = def;
58             });
59             if (!--asyncReqs) drawCompositDef();
60         }
61     });
62 }
63
64 var fetchAttrs = [];
65 function drawCompositDef() {
66     var defBlob = JSON2js(compositeDef.definition());
67
68     importNodeTree(null, defBlob);
69
70     if (fetchAttrs.length) {
71         new openils.PermaCrud().search('ccvm', {'-or' : fetchAttrs}, {
72             oncomplete : function(r) {
73                 var maps = openils.Util.readResponse(r);
74                 dojo.forEach(maps, function(map) {
75                     codedValueMaps[map.id()] = map;
76                 });
77                 drawNodeTree();
78             }
79         });
80     } else {
81         drawNodeTree();
82     }
83 }
84
85 // translate the DB-stored tree into a local structure
86 function importNodeTree(pnode, node) {
87     if (!node) return;
88
89     var newnode = {
90         index : treeIndex++,
91         pnode : pnode,
92         children : []
93     }
94
95     if (pnode) {
96         pnode.children.push(newnode);
97     } else {
98         fetchAttrs = [];
99         nodeTree = newnode;
100     }
101
102     if (dojo.isArray(node)) { 
103         newnode.or = true;
104         dojo.forEach(node, function(n) { importNodeTree(newnode, n) });
105
106     } else if (node._not) {
107         newnode.not = true;
108         importNodeTree(newnode, node._not);
109
110     } else if (node._attr) {
111         // list of attrs that we have to fetch for display
112         fetchAttrs.push({'-and' : {ctype : node._attr, code : node._val}});
113
114         newnode.attr = node._attr;
115         newnode.val = node._val;
116
117     } else {
118         newnode.and = true;
119         dojo.forEach(Object.keys(node).sort(), function(key) {
120             importNodeTree(newnode, node[key]);
121         });
122     }
123 }
124
125 function byname(elm, name) {
126     return dojo.query('[name=' + name + ']', elm)[0];
127 }
128 function findccvm(ctype, code) {
129     for (var id in codedValueMaps) {
130         var m = codedValueMaps[id];
131         if (m.code() == code && m.ctype() == ctype) {
132             return m;
133         }
134     }
135     console.error('cannot find ccvm ' + ctype + ' : ' + code);
136 }
137
138 // render the local structure tree in the DOM
139 var nodeTemplate;
140 var nodeTbody;
141 function drawNodeTree(node) {
142
143     if (!nodeTbody) {
144         nodeTbody = dojo.byId('tree-container');
145         nodeTemplate = nodeTbody.removeChild(dojo.byId('node-template'));
146     } 
147
148     var root = false;
149     if (!node) {
150         dojo.empty(nodeTbody);
151         if (!nodeTree) {
152             newTreeBtn.attr('disabled', false);
153             delTreeBtn.attr('disabled', true);
154             return;
155         } else {
156             node = nodeTree;
157             root = true;
158         }
159     }
160
161     newTreeBtn.attr('disabled', true);
162     delTreeBtn.attr('disabled', false);
163
164     var depth = -1;
165     function d(node) {if (node) {depth++; d(node.pnode);}};
166     d(node);
167
168     node.element = nodeTemplate.cloneNode(true);
169     var expression = '';
170
171     var addLink = byname(node.element, 'add-child');
172     var delLink = byname(node.element, 'del-child');
173     addLink.setAttribute('index', node.index);
174     delLink.setAttribute('index', node.index);
175
176     if (node.or) {
177         byname(node.element, 'attr').innerHTML = localeStrings.OR;
178
179     } else if (node.and) {
180         byname(node.element, 'attr').innerHTML = localeStrings.AND;
181
182     } else if (node.not) {
183         byname(node.element, 'attr').innerHTML = localeStrings.NOT;
184
185     } else {
186         dojo.addClass(addLink, 'hidden');
187
188         byname(node.element, 'attr').innerHTML = 
189             recordAttrDefs[node.attr].label() + ' (' + node.attr + ')';
190
191         var map = findccvm(node.attr, node.val);
192         byname(node.element, 'val').innerHTML = 
193             map.value() + ' (' + map.code() + ')';
194
195         dojo.removeClass(
196             dojo.query('.invisible', node.element)[0], 'invisible');
197
198         expression = map.value();
199     }
200
201     nodeTbody.appendChild(node.element);
202
203     var nc = dojo.query('.node-column', node.element)[0];
204     for (var i = 0; i < depth; i++) {
205         nc.insertBefore(dojo.byId('tree-pad').cloneNode(true), nc.firstChild); 
206     }
207
208     if (node.attr) return expression;
209
210     if (node.not) {
211         if (node.children[0]) {
212             expression = localeStrings.NOT + 
213                 ' ' + drawNodeTree(node.children[0]);
214         }
215
216     } else { // AND | OR
217
218         if (!root) expression = '( ';
219         for (var i = 0; i < node.children.length; i++) {
220             expression += drawNodeTree(node.children[i]);
221             if (i == node.children.length - 1) break;
222             expression += ' ' + (node.or ? localeStrings.OR : 
223                 (node.and ? localeStrings.AND : localeStrings.NOT)) + ' ';
224         }
225         if (!root) expression += ' )';
226     }
227
228     if (root) {
229         dojo.byId('tree-expression').innerHTML = expression;
230     }
231
232     return expression;
233 }
234
235 function findNode(index, node) {
236     if (!node) node = nodeTree;
237     if (node.index == index) return node;
238     for (var i = 0; i < node.children.length; i++) {
239         var n = findNode(index, node.children[i]);
240         if (n) return n;
241     }
242 }
243
244 var cradSelector;
245 function buildSelectors() {
246     if (cradSelector) return;
247     cradSelector = new openils.widget.AutoFieldWidget({
248         fmClass : 'crad',
249         selfReference : true,
250         parentNode : 'new-data-crad-selector'
251     });
252     cradSelector.build(function(w, ww) {
253         dojo.connect(w, 'onChange', function(val) { 
254             dojo.byId('new-data-attr').checked = true;
255             new openils.PermaCrud().search('ccvm', {ctype : val}, {
256                 oncomplete : function(r) {
257                     var maps = openils.Util.readResponse(r);
258                     var items = [];
259                     dojo.forEach(maps, function(map) {
260                         codedValueMaps[map.id()] = map;
261                         items.push({name : map.value(), value : map.id()});
262                     });
263                     ccvmSelector.store = new dojo.data.ItemFileReadStore({
264                         data : {
265                             identifier : 'value',
266                             label : 'name',
267                             items : items
268                         }
269                     });
270                     ccvmSelector.startup();
271                 }
272             });
273         });
274     });
275 }
276
277 function addChild(link) {
278     buildSelectors();
279     var ctxNode = link ? findNode(link.getAttribute('index')) : null;
280
281     newDataSubmit.onClick = function(args) {
282         var node = {
283             index : treeIndex++,
284             pnode : ctxNode,
285             children : []
286         };
287
288         if (dojo.byId('new-data-and').checked) {
289             node.and = true;
290         } else if (dojo.byId('new-data-or').checked) {
291             node.or = true;
292         } else if (dojo.byId('new-data-not').checked) {
293             node.not = true;
294         } else {
295             node.attr = cradSelector.widget.attr('value');
296             node.val = codedValueMaps[ccvmSelector.attr('value')].code();
297             if (!node.attr || !node.val) return;
298         }
299
300         newDataDialog.hide();
301
302         // for visual clarity, push the non-boolean children to the front
303         if (ctxNode) {
304             if (node.and || node.or || node.not) {
305                 ctxNode.children.push(node);
306             } else {
307                 ctxNode.children.unshift(node);
308             }
309         } else {
310             // starting a new tree from scratch
311             nodeTree = node; 
312         }
313         drawNodeTree();
314     }
315
316     dojo.byId('new-data-attr').checked = true;
317     newDataDialog.show(); 
318 }
319
320 function delChild(link) {
321     var node = findNode(link.getAttribute('index'));
322
323     if (node.pnode) {
324         for (var i = 0; i < node.pnode.children.length; i++) {
325             var child = node.pnode.children[i];
326             if (child.index == node.index) {
327                 node.pnode.children.splice(i, 1);
328                 break;
329             }
330         }
331     } else {
332         newTreeBtn.attr('disabled', false);
333         delTreeBtn.attr('disabled', true);
334         nodeTree = null;
335     }
336
337     drawNodeTree();
338 }
339
340 function delTree() {
341     nodeTree = null;
342     drawNodeTree(); // resets
343     new openils.PermaCrud().eliminate(compositeDef);
344     compositeDef.isnew(true);
345     compositeDef.definition(null);
346 }
347
348 function saveTree() {
349     var expression = exportTree();
350
351     compositeDef.definition(js2JSON(expression))
352     var pcrud = new openils.PermaCrud();
353     saveTreeBtn.attr('disabled', true);
354
355     var oncomplete = function(r) {
356         openils.Util.readResponse(r);  // pickup any alerts
357         saveTreeBtn.attr('disabled', false);
358         compositeDef.isnew(false);
359     }
360
361     var pfunc = compositeDef.isnew() ? 'create' : 'update';
362     pcrud[pfunc](compositeDef, {oncomplete : oncomplete});
363 }
364
365 function exportTree(node) {
366     if (!node) node = nodeTree;
367
368     if (node.attr) 
369         return {_attr : node.attr, _val : node.val};
370
371     if (node.not)
372         // _not nodes may only have one child
373         return {_not : exportTree(node.children[0])};
374
375     var compiled;
376     for (var i = 0; i < node.children.length; i++) {
377         var child = node.children[i];
378         if (!compiled) compiled = node.or ? [] : {};
379         compiled[i] = exportTree(child);
380     }
381
382     return compiled;
383 }
384
385 openils.Util.addOnLoad(drawPage);