]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_builder.js
added title to selector options so overflow items are visible
[Evergreen.git] / Open-ILS / web / reports / oils_rpt_builder.js
1 /** initializes reports, some basid display settings, 
2   * grabs and builds the IDL tree
3   */
4 function oilsInitReportBuilder() {
5         if(!oilsInitReports()) return false;
6         oilsReportBuilderReset();
7         DOM.oils_rpt_table.onclick = 
8                 function(){hideMe(DOM.oils_rpt_column_editor)};
9         oilsDrawRptTree(
10                 function() { 
11                         hideMe(DOM.oils_rpt_tree_loading); 
12                         unHideMe(DOM.oils_rpt_table); 
13                 }
14         );
15
16         DOM.oils_rpt_builder_save_template.onclick = oilsReportBuilderSave;
17 }
18
19 function oilsReportBuilderReset() {
20         var n = (oilsRpt) ? oilsRpt.name : "";
21         oilsRpt = new oilsReport();
22         oilsRpt.name = n;
23         oilsRptDisplaySelector  = DOM.oils_rpt_display_selector;
24         oilsRptFilterSelector   = DOM.oils_rpt_filter_selector;
25         oilsRptHavingSelector   = DOM.oils_rpt_agg_filter_selector;
26         removeChildren(oilsRptDisplaySelector);
27         removeChildren(oilsRptFilterSelector);
28         removeChildren(oilsRptHavingSelector);
29         //removeChildren(oilsRptOrderBySelector);
30         oilsRptResetParams();
31 }
32
33 function oilsReportBuilderSave() {
34
35         var tmpl = new rt();
36         tmpl.name(DOM.oils_rpt_builder_new_name.value);
37         tmpl.description(DOM.oils_rpt_builder_new_desc.value);
38         tmpl.owner(USER.id());
39         tmpl.folder(new CGI().param('folder'));
40         tmpl.data(js2JSON(oilsRpt.def));
41
42         if(!confirm('Name : '+tmpl.name() + '\nDescription: ' + tmpl.description()+'\nSave Template?'))
43                 return;
44
45         debugFMObject(tmpl);
46         //return; /* XXX */
47
48         var req = new Request(OILS_RPT_CREATE_TEMPLATE, SESSION, tmpl);
49         req.callback(
50                 function(r) {
51                         var res = r.getResultObject();
52                         if( res && res != '0' ) {
53                                 oilsRptAlertSuccess();
54                                 _l('oils_rpt.xhtml');
55                         } 
56                 }
57         );
58         
59         req.send();
60 }
61
62
63
64 /* adds an item to the display window */
65 function oilsAddRptDisplayItem(path, name, tform, params) {
66         if( ! oilsAddSelectorItem(oilsRptDisplaySelector, path, name) ) 
67                 return;
68
69         /* add the selected columns to the report output */
70         name = (name) ? name : oilsRptPathCol(path);
71         if( !tform ) tform = 'Bare';
72
73         var aggregate = oilsRptGetIsAgg(tform);
74
75         /* add this item to the select blob */
76         var sel = {
77                 relation: hex_md5(oilsRptPathRel(path)), 
78                 path : path,
79                 alias:    name,
80                 column:   { transform: tform, colname: oilsRptPathCol(path) }
81         };
82
83         if( params ) sel.column.params = params;
84
85         if(!oilsRptGetIsAgg(tform)) {
86                 var select = [];
87                 var added = false;
88                 for( var i = 0; i < oilsRpt.def.select.length; i++ ) {
89                         var item = oilsRpt.def.select[i];
90                         if( !added && oilsRptGetIsAgg( item.column.transform ) ) {
91                                 select.push(sel);
92                                 added = true;
93                         }
94                         select.push(item);
95                 }
96                 if(!added) select.push(sel);
97                 oilsRpt.def.select = select;
98         } else {
99                 oilsRpt.def.select.push(sel);
100         }
101
102
103         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
104         oilsRptDebug();
105 }
106
107 function oilsRptGetIsAgg(tform) {
108         return OILS_RPT_TRANSFORMS[tform].aggregate;
109         
110         /* DEPRECATED */
111         var sel = $n(DOM.oils_rpt_tform_table,'selector');
112         for( var i = 0; i < sel.options.length; i++ ) {
113                 var opt = sel.options[i];
114                 if( opt.getAttribute('value') == tform )
115                         return opt.getAttribute('aggregate');
116         }
117 }
118
119 /* takes a column path and builds a from-clause object for the path */
120 function oilsRptBuildFromClause(path) {
121
122         /* the path is the full path (relation) from the source 
123                 object to the column in question (e.g. au-home_ou-aou-name)*/
124         var parts = path.split(/-/);
125
126         /* the final from clause */
127         var obj = {}; 
128
129         /* reference to the current position in the from clause */
130         var tobj = obj; 
131
132         var newpath = "";
133
134         /* walk the path, fleshing the from clause as we go */
135         for( var i = 0; i < parts.length; i += 2 ) {
136
137                 var cls = parts[i]; /* class name (id) */
138                 var col = parts[i+1]; /* column name */
139
140                 /* a "node" is a class description from the IDL, it 
141                         contains relevant info, plus a list of "fields",
142                         or column objects */
143                 var node = oilsIDL[cls];
144                 var pkey = oilsRptFindField(node, node.pkey);
145
146                 /* a "field" is a parsed version of a column from the IDL,
147                         contains datatype, column name, etc. */
148                 var field = oilsRptFindField(node, col);
149
150                 /* re-construct the path as we go so 
151                         we know what all we've seen thus far */
152                 newpath = (newpath) ? newpath + '-'+ cls : cls;
153
154                 /* extract relevant info */
155                 tobj.table = node.table;
156                 tobj.path = newpath;
157                 tobj.alias = hex_md5(newpath);
158
159                 _debug('field type is ' + field.type);
160                 if( i == (parts.length - 2) ) break;
161
162                 /* we still have columns left in the path, keep adding join's */
163                 var path_col = col;
164                 if(field.reltype != 'has_a')
165                         col = pkey.name + '-' + col;
166
167                 tobj.join = {};
168                 tobj = tobj.join;
169
170                 tobj[col] = {};
171                 tobj = tobj[col];
172                 if( field.type == 'link' )
173                         tobj.key = field.key;
174
175                 newpath = newpath + '-'+ path_col;
176         }
177
178         _debug("built 'from' clause: path="+path+"\n"+formatJSON(js2JSON(obj)));
179         return obj;
180 }
181
182 function oilsMoveUpDisplayItems() {
183         var sel = oilsRptDisplaySelector;
184         var idx = sel.selectedIndex;
185         if( idx == 0 ) return;
186         var opt = sel.options[idx];
187         sel.options[idx] = null;
188         idx--;
189         var val = opt.getAttribute('value');
190         insertSelectorVal(sel, idx, opt.innerHTML, val);
191         sel.options[idx].selected = true;
192
193         var arr = oilsRpt.def.select;
194         for( var i = 0; i < arr.length; i++ ) {
195                 if( arr[i].path == val ) {
196                         var other = arr[i-1];
197                         arr[i-1] = arr[i];
198                         arr[i] = other;
199                         break;
200                 }
201         }
202         oilsRptDebug();
203 }
204
205 function oilsMoveDownDisplayItems() {
206         var sel = oilsRptDisplaySelector;
207         var idx = sel.selectedIndex;
208         if( idx == sel.options.length - 1 ) return;
209         var opt = sel.options[idx];
210         sel.options[idx] = null;
211         idx++;
212         var val = opt.getAttribute('value');
213         insertSelectorVal(sel, idx, opt.innerHTML, val);
214         sel.options[idx].selected = true;
215
216         var arr = oilsRpt.def.select;
217         for( var i = 0; i < arr.length; i++ ) {
218                 if( arr[i].path == val ) {
219                         var other = arr[i+1];
220                         arr[i+1] = arr[i];
221                         arr[i] = other;
222                         break;
223                 }
224         }
225         oilsRptDebug();
226 }
227
228
229 /* removes a specific item from the display window */
230 /*
231 function oilsDelDisplayItem(val) {
232         oilsDelSelectorItem(oilsRptDisplaySelector, val);
233 }
234 */
235
236 /* removes selected items from the display window */
237 function oilsDelSelectedDisplayItems() {
238         var list = oilsDelSelectedItems(oilsRptDisplaySelector);
239
240         _debug('deleting list: ' + list);
241
242         /* remove the de-selected columns from the report output */
243         oilsRpt.def.select = grep( oilsRpt.def.select, 
244                 function(i) {
245                         for( var j = 0; j < list.length; j++ ) {
246                                 var d = list[j]; /* path */
247                                 var col = i.column;
248
249                                 _debug('in delete, looking at list = '+d+' : col = ' + 
250                                         col.colname + ' : relation = ' + i.relation + ' : encoded = ' + hex_md5(oilsRptPathRel(d)) );
251
252                                 if( hex_md5(oilsRptPathRel(d)) == i.relation && oilsRptPathCol(d) == col.colname ) {
253                                         return false;
254                                 }
255                         }
256                         return true;
257                 }
258         );
259
260         if(!oilsRpt.def.select) oilsRpt.def.select = [];
261
262         oilsRptPruneFromList(list);
263
264         /*
265         for( var j = 0; j < list.length; j++ ) {
266                 debug('seeing if we can prune from clause with relation = ' + hex_md5(oilsRptPathRel(list[j])));
267                 if(     !grep(oilsRpt.def.select,
268                                 function(i){ return (i.relation == hex_md5(oilsRptPathRel(list[j]))); })
269                         && !grep(oilsRpt.def.where,
270                                 function(i){ return (i.relation == hex_md5(oilsRptPathRel(list[j]))); })
271                         && !grep(oilsRpt.def.having,
272                                 function(i){ return (i.relation == hex_md5(oilsRptPathRel(list[j]))); })
273                 ) {
274                         _debug('pruning from clause');
275                         oilsRptPruneFromClause(oilsRptPathRel(list[j]));
276                 }
277         }
278         */
279
280         oilsRptDebug();
281 }
282
283 function oilsRptPruneFromList(pathlist) {
284
285         for( var j = 0; j < pathlist.length; j++ ) {
286                 /* if there are no items left in the "select", "where", or "having" clauses 
287                         for the given relation, trim this relation from the "from" clause */
288                 var path = pathlist[j];
289                 var encrel = hex_md5(oilsRptPathRel(path));
290
291                 debug('seeing if we can prune from clause with relation = ' + encrel +' : path = ' + path);
292
293                 var func = function(i){ return (i.relation == hex_md5(oilsRptPathRel(path))); };
294
295                 if(     !grep(oilsRpt.def.select, func) && 
296                                 !grep(oilsRpt.def.where, func) && 
297                                 !grep(oilsRpt.def.having, func) ) {
298
299                         oilsRptPruneFromClause(oilsRptPathRel(pathlist[j]));
300                 }
301         }
302 }
303
304
305 /* for each item in the path list, remove the associated data
306         from the "from" clause */
307
308 function oilsRptPruneFromClause(relation, node) {
309         _debug("removing relation from 'from' clause: " + relation);
310         if(!node) node = oilsRpt.def.from.join;
311         if(!node) return false;
312
313         for( var i in node ) {
314                 _debug("from prune looking at node "+node[i].path);
315                 // first, descend into the tree, and prune leaves first
316                 if( node[i].join ) {
317                         oilsRptPruneFromClause(relation, node[i].join); 
318                         if(oilsRptObjectKeys(node[i].join).length == 0) 
319                                 delete node[i].join;
320                 }
321         }
322
323         if(!node.join) {
324
325                 var key = oilsRptObjectKeys(node)[0];
326                 var from_alias = node[key].alias;
327                 var func = function(n){ return (n.relation == from_alias)};
328
329                 _debug("pruning from clause with alias "+ from_alias);
330
331                 if(     !grep(oilsRpt.def.select, func) &&
332                                 !grep(oilsRpt.def.where, func) &&
333                                 !grep(oilsRpt.def.having, func) ) {
334
335                         // if we're at an unused empty leaf, remove it
336                         delete node[i];
337                         return true;
338                 }
339         }
340
341         return false;
342 }
343
344 function oilsRptMkFilterTags(path, tform, filter) {
345         var name = oilsRptMakeLabel(path);
346         if(tform) name += ' ('+tform+')';
347         name += ' "' + filter + '"';
348         var epath = path + ':'+filter+':';
349         if(tform) epath += tform;
350
351         return [ name, epath ];
352 }
353
354
355 function oilsAddRptFilterItem(path, tform, filter) {
356         _debug("Adding filter item for "+path+" tform="+tform+" filter="+filter);
357
358         var name = oilsRptMkFilterTags(path, tform, filter);
359         var epath = name[1];
360         name = name[0];
361
362         if( ! oilsAddSelectorItem(oilsRptFilterSelector, epath, name) )
363                 return;
364
365         var where = {
366                 relation: hex_md5(oilsRptPathRel(path)), 
367                 path : path,
368                 column:   { transform: tform, colname: oilsRptPathCol(path) },
369                 condition : {}
370         };
371         if( filter == 'is' || filter == 'is not' )
372                 where.condition[filter] = null;
373         else where.condition[filter] = oilsRptNextParam();
374
375         switch(tform) {
376                 case 'substring' : where.column.params = oilsRptNextParam();
377         }
378
379         oilsRpt.def.where.push(where);
380         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
381         oilsRptDebug();
382 }
383
384
385 function oilsAddRptHavingItem(path, tform, filter) {
386         _debug("Adding filter item for "+path+" tform="+tform+" filter="+filter);
387
388         var name = oilsRptMkFilterTags(path, tform, filter);
389         var epath = name[1];
390         name = name[0];
391
392         if( ! oilsAddSelectorItem(oilsRptHavingSelector, epath, name) )
393                 return;
394
395         var having = {
396                 relation: hex_md5(oilsRptPathRel(path)), 
397                 path : path,
398                 column:   { transform: tform, colname: oilsRptPathCol(path) },
399                 condition : {}
400         };
401         if( filter == 'is' || filter == 'is not' )
402                 having.condition[filter] = null;
403         else having.condition[filter] = oilsRptNextParam();
404
405         switch(tform) {
406                 case 'substring' : having.column.params = oilsRptNextParam();
407         }
408
409         oilsRpt.def.having.push(having);
410         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
411         oilsRptDebug();
412 }
413
414
415
416 function oilsDelSelectedFilterItems() {
417         _oilsDelSelectedFilterItems('where');
418 }
419 function oilsDelSelectedAggFilterItems() {
420         _oilsDelSelectedFilterItems('having');
421 }
422
423 function _oilsDelSelectedFilterItems(type) {
424
425         /* the values in this list are formed:  <path>:<operation>:<transform> */
426         var list = oilsDelSelectedItems(oilsRptFilterSelector);
427
428         for( var i = 0; i < list.length; i++ ) {
429                 var enc_path = list[i];
430                 var data = oilsRptParseFilterEncPath(enc_path);
431                 oilsRpt.def[type] = grep( 
432                         oilsRpt.def[type],
433                         function(f) { 
434                                 return oilsRptFilterDataMatches( 
435                                         f, data.path, data.operation, data.tform );
436                         }
437                 );
438         }
439
440         if(!oilsRpt.def[type]) oilsRpt.def[type] = [];
441         oilsRptPruneFromList(list);
442         oilsRptDebug();
443 }
444
445 function oilsRptParseFilterEncPath(item) {
446         return {
447                 path:           item.replace(/:.*/,''),
448                 operation:      item.replace(/.*:(.*):.*/,'$1'),
449                 tform:  item.replace(/.*?:.*?:(.*)/,'$1')
450         };
451 }
452
453
454 function oilsRptFilterDataMatches(filter, path, operation, tform) {
455         var rel = hex_md5(oilsRptPathRel(path));
456         var col = oilsRptPathCol(path);
457
458         if(     col == filter.column.colname &&
459                         rel == filter.relation &&       
460                         tform == filter.column.transform &&
461                         operation == oilsRptObjectKeys(filter)[0] ) return true;
462
463         return false;
464 }
465
466 /*
467 function oilsRptFilterGrep(flist, filter) {
468
469         for( var j = 0; j < flist.length; j++ ) {
470
471                 var fil = flist[j];
472                 var col = filter.column;
473                 var frel = hex_md5(oilsRptPathRel(fil.path));
474                 var fcol = oilsRptPathCol(fil.path);
475
476                 var op = oilsRptObjectKeys(filter.condition)[0];
477
478                 if(     frel == filter.relation && 
479                                 fcol == col.colname && 
480                                 fil.operation == op &&
481                                 fil.tform == col.transform ) {
482                                 return false;
483                 }
484         }
485         return true;
486 }
487 */
488
489 /* adds an item to the display window */
490 function oilsAddRptAggFilterItem(val) {
491         oilsAddSelectorItem(oilsRptHavingFilterSelector, val);
492 }
493
494 /* removes a specific item from the display window */
495 function oilsDelAggFilterItem(val) {
496         oilsDelSelectorItem(oilsRptHavingFilterSelector, val);
497 }
498
499
500
501 /*
502 function ___oilsDelSelectedAggFilterItems() {
503         var list = oilsDelSelectedItems(oilsRptHavingFilterSelector);
504         oilsRpt.def.having = grep( oilsRpt.def.having, 
505                 function(i) {
506                         for( var j = 0; j < list.length; j++ ) {
507                                 var d = list[j];
508                                 var col = i.column;
509
510                                 if( typeof col != 'string' ) 
511                                         for( var c in col ) col = col[c];
512
513                                 if( typeof col != 'string' ) col = col[0];
514
515                                 if( oilsRptPathRel(d) == i.relation && oilsRptPathCol(d) == col ) {
516                                 */
517                                 //      var param = (i.alias) ? i.alias.match(/::P\d*/) : null;
518                                         /*
519                                         if( param ) delete oilsRpt.params[param];
520                                         return false;
521                                 }
522                         }
523                         return true;
524                 }
525         );
526
527         if(!oilsRpt.def.having) oilsRpt.def.having = [];
528         oilsRptPruneFromList(list);
529         oilsRptDebug();
530 }
531 */
532
533
534 /* adds an item to the display window */
535 function oilsAddSelectorItem(sel, val, name) {
536         name = (name) ? name : oilsRptMakeLabel(val);
537         for( var i = 0; i < sel.options.length; i++ ) {
538                 var opt = sel.options[i];
539                 if( opt.value == val ) return false;
540         }
541         var opt = insertSelectorVal( sel, -1, name, val );
542         opt.setAttribute('title', name);
543         return true;
544 }
545
546
547 /* removes a specific item from the display window */
548 function oilsDelSelectorItem(sel, val) {
549         var opts = sel.options;
550         for( var i = 0; i < opts.length; i++ ) {
551                 var opt = opts[i];
552                 if( opt.value == val )  {
553                         if( i == opts.length - 1 ) 
554                                 opts[i] = null;
555                         else opts[i] = opts[i+1];
556                         return;
557                 }
558         }
559 }
560
561 /* removes selected items from the display window */
562 function oilsDelSelectedItems(sel) {
563         var list = getSelectedList(sel);
564         for( var i = 0; i < list.length; i++ ) 
565                 oilsDelSelectorItem(sel, list[i]);
566         return list;
567 }
568
569
570 /* hides the different field editor tabs */
571 function oilsRptHideEditorDivs() {
572         hideMe(DOM.oils_rpt_tform_div);
573         hideMe(DOM.oils_rpt_filter_div);
574         hideMe(DOM.oils_rpt_agg_filter_div);
575         hideMe(DOM.oils_rpt_order_by_div);
576 }
577
578
579 /**
580   This draws the 3-tabbed window containing the transform,
581   filter, and aggregate filter picker window
582   */
583 function oilsRptDrawDataWindow(path) {
584         var col = oilsRptPathCol(path);
585         var cls = oilsRptPathClass(path);
586         var field = oilsRptFindField(oilsIDL[cls], col);
587
588         appendClear(DOM.oils_rpt_editor_window_label, text(oilsRptMakeLabel(path)));
589         appendClear(DOM.oils_rpt_editor_window_datatype, text(field.datatype));
590
591         _debug("setting update data window for column "+col+' on class '+cls);
592
593         var div = DOM.oils_rpt_column_editor;
594         /* set a preliminary top position so the page won't bounce around */
595         div.setAttribute('style','top:'+oilsMouseX+'px');
596
597         /* unhide the div so we can determine the dimensions */
598         unHideMe(div);
599
600         /* don't let them see the floating div until the position is fully determined */
601         div.style.visibility='hidden'; 
602
603         oilsRptDrawTransformWindow(path, col, cls, field);
604         oilsRptDrawFilterWindow(path, col, cls, field);
605         oilsRptDrawHavingWindow(path, col, cls, field);
606         oilsRptDrawOrderByWindow(path, col, cls, field);
607
608         buildFloatingDiv(div, 600);
609
610         /* now let them see it */
611         div.style.visibility='visible';
612         oilsRptSetDataWindowActions(div);
613 }
614
615
616 function oilsRptSetDataWindowActions(div) {
617         /* give the tab links behavior */
618         DOM.oils_rpt_tform_tab.onclick = 
619                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_tform_div)};
620         DOM.oils_rpt_filter_tab.onclick = 
621                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_filter_div)};
622         DOM.oils_rpt_agg_filter_tab.onclick = 
623                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_agg_filter_div)};
624
625         /*
626         DOM.oils_rpt_order_by_tab.onclick = 
627                 function(){
628                         oilsRptHideEditorDivs();
629                         oilsRptDrawOrderByWindow();
630                         unHideMe(DOM.oils_rpt_order_by_div);
631                         };
632                         */
633
634         DOM.oils_rpt_tform_tab.onclick();
635         DOM.oils_rpt_column_editor_close_button.onclick = function(){hideMe(div);};
636 }
637
638
639 function oilsRptDrawFilterWindow(path, col, cls, field) {
640
641         var tformPicker = new oilsRptTformPicker( {     
642                         node : DOM.oils_rpt_filter_tform_table,
643                         datatype : field.datatype,
644                         non_aggregate : true
645                 }
646         );
647
648         var filterPicker = new oilsRptFilterPicker({
649                         node : DOM.oils_rpt_filter_op_table,
650                         datatype : field.datatype
651                 }
652         );
653
654         DOM.oils_rpt_filter_submit.onclick = function() {
655                 oilsAddRptFilterItem(
656                         path, tformPicker.getSelected(), filterPicker.getSelected());
657         }
658 }
659
660
661 function oilsRptDrawHavingWindow(path, col, cls, field) {
662         var tformPicker = new oilsRptTformPicker( {     
663                         node : DOM.oils_rpt_agg_filter_tform_table,
664                         datatype : field.datatype,
665                         aggregate : true
666                 }
667         );
668
669         var filterPicker = new oilsRptFilterPicker({
670                         node : DOM.oils_rpt_agg_filter_op_table,
671                         datatype : field.datatype
672                 }
673         );
674
675         DOM.oils_rpt_agg_filter_submit.onclick = function() {
676                 oilsAddRptHavingItem(
677                         path, tformPicker.getSelected(), filterPicker.getSelected());
678         }
679 }
680
681 /* draws the transform window */
682 function oilsRptDrawTransformWindow(path, col, cls, field) {
683         DOM.oils_rpt_tform_label_input.value = oilsRptMakeLabel(path);
684         var dtype = field.datatype;
685
686         var tformPicker = new oilsRptTformPicker( {     
687                         node : DOM.oils_rpt_tform_table,
688                         datatype : field.datatype,
689                         non_aggregate : true,
690                         aggregate : true
691                 }
692         );
693
694         DOM.oils_rpt_tform_submit.onclick = 
695                 function(){ 
696                         oilsAddRptDisplayItem(path, 
697                                 DOM.oils_rpt_tform_label_input.value, tformPicker.getSelected() );
698                 };
699
700         DOM.oils_rpt_tform_label_input.focus();
701         DOM.oils_rpt_tform_label_input.select();
702
703         _debug("Building transform window for datatype "+dtype);
704 }
705
706
707 //function oilsRptDrawOrderByWindow(path, col, cls, field) {
708 function oilsRptDrawOrderByWindow() {
709         var sel = DOM.oils_rpt_order_by_selector;
710         removeChildren(sel);
711         DOM.oils_rpt_order_by_submit.onclick = function() {
712                 oilsRptAddOrderBy(getSelectorVal(sel));
713         }
714
715         var cols = oilsRpt.def.select;
716         for( var i = 0; i < cols.length; i++ ) {
717                 var obj = cols[i];
718                 insertSelectorVal(sel, -1, obj.alias, obj.path);
719         }
720 }
721
722 function oilsRptAddOrderBy(path) {
723         var rel = hex_md5(oilsRptPathRel(path));
724         var order_by = oilsRpt.def.order_by;
725
726         /* if this item is already in the order by remove it and overwrite it */
727         order_by = grep(oilsRpt.def.order_by, 
728                 function(i) {return (i.path != path)});
729         
730         if(!order_by) order_by = [];
731
732         /* find the column definition in the select blob */
733         var obj = grep(oilsRpt.def.select,
734                 function(i) {return (i.path == path)});
735         
736         if(!obj) return;
737         obj = obj[0];
738         
739         order_by.push({ 
740                 relation : obj.relation, 
741                 column : obj.column,
742                 direction : getSelectorVal(DOM.oils_rpt_order_by_dir)
743         });
744
745         oilsRpt.def.order_by = order_by;
746         oilsRptDebug();
747 }
748
749
750