]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_builder.js
refactored field transforms and operations to standalone widget classes
[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         oilsInitReports();
6         oilsReportBuilderReset();
7         DOM.oils_rpt_table.onclick = 
8                 function(){hideMe(DOM.oils_rpt_column_editor)};
9         //oilsRptBuildCalendars();
10         oilsDrawRptTree(
11                 function() { 
12                         hideMe(DOM.oils_rpt_tree_loading); 
13                         unHideMe(DOM.oils_rpt_table); 
14                 }
15         );
16 }
17
18 function oilsReportBuilderReset() {
19         var n = (oilsRpt) ? oilsRpt.name : "";
20         oilsRpt = new oilsReport();
21         oilsRpt.name = n;
22         oilsRptDisplaySelector  = DOM.oils_rpt_display_selector;
23         oilsRptFilterSelector   = DOM.oils_rpt_filter_selector;
24         removeChildren(oilsRptDisplaySelector);
25         removeChildren(oilsRptFilterSelector);
26         oilsRptDebug();
27         oilsRptResetParams();
28 }
29
30 /*
31 function oilsRptBuildCalendars() {
32         Calendar.setup({
33                 inputField  : "oils_rpt_filter_tform_timestamp_input", // id of the input field
34                 ifFormat    : "%Y-%m-%d", // format of the input field
35                 button      : "oils_rpt_filter_tform_timestamp_cal",  // trigger for the calendar (button ID)
36                 align       : "Tl", // alignment (defaults to "Bl")
37                 singleClick : true
38         });
39         Calendar.setup({
40                 inputField  : "oils_rpt_filter_tform_timestamp_input_2", // id of the input field
41                 ifFormat    : "%Y-%m-%d", // format of the input field
42                 button      : "oils_rpt_filter_tform_timestamp_cal2",  // trigger for the calendar (button ID)
43                 align       : "Tl", // alignment (defaults to "Bl")
44                 singleClick : true
45         });
46 }
47 */
48
49
50
51 /* returns just the column name */
52 function oilsRptPathCol(path) {
53         var parts = path.split(/-/);
54         return parts.pop();
55 }
56
57 /* returns the IDL class of the selected column */
58 function oilsRptPathClass(path) {
59         var parts = path.split(/-/);
60         parts.pop();
61         return parts.pop();
62 }
63
64 /* returns everything prior to the column name */
65 function oilsRptPathRel(path) {
66         var parts = path.split(/-/);
67         parts.pop();
68         return parts.join('-');
69 }
70
71 /* creates a label "path" based on the column path */
72 function oilsRptMakeLabel(path) {
73         var parts = path.split(/-/);
74         var str = '';
75         for( var i = 0; i < parts.length; i++ ) {
76                 if(i%2 == 0) {
77                         if( i == 0 )
78                                 str += oilsIDL[parts[i]].label;
79                 } else {
80                         var f = oilsRptFindField(oilsIDL[parts[i-1]], parts[i]);
81                         str += ":"+f.label;
82                 }
83         }
84         return str;
85 }
86
87
88 /* adds an item to the display window */
89 function oilsAddRptDisplayItem(path, name, tform, params) {
90         if( ! oilsAddSelectorItem(oilsRptDisplaySelector, path, name) ) 
91                 return;
92
93         /* add the selected columns to the report output */
94         name = (name) ? name : oilsRptPathCol(path);
95         var param = oilsRptNextParam();
96
97         /* add this item to the select blob */
98         var sel = {
99                 relation:oilsRptPathRel(path), 
100                 alias:param
101         };
102
103         if( tform ) {
104                 sel.column = {};
105                 if( params ) {
106                         params.unshift(oilsRptPathCol(path));
107                         sel.column[tform] = params;
108                 } else {
109                         sel.column[tform] = oilsRptPathCol(path); 
110                 }
111         } else { sel.column = oilsRptPathCol(path); }
112
113         oilsRpt.def.select.push(sel);
114
115         mergeObjects( oilsRpt.def.from, oilsRptBuildFromClause(path));
116         oilsRpt.params[param] = name;
117         oilsRptDebug();
118 }
119
120 /* takes a column path and builds a from-clause object for the path */
121 function oilsRptBuildFromClause(path) {
122         var parts = path.split(/-/);
123         var obj = {};
124         var tobj = obj;
125         var newpath = "";
126
127         for( var i = 0; i < parts.length; i += 2 ) {
128                 var cls = parts[i];
129                 var col = parts[i+1];
130                 var node = oilsIDL[parts[i]];
131                 var field = oilsRptFindField(node, col);
132                 newpath = (newpath) ? newpath + '-'+ cls : cls;
133
134                 tobj.table = node.table;
135                 tobj.alias = newpath;
136                 _debug('field type is ' + field.type);
137                 if( i == (parts.length - 2) ) break;
138
139                 tobj.join = {};
140                 tobj = tobj.join;
141                 tobj[col] = {};
142                 tobj = tobj[col];
143                 if( field.type == 'link' )
144                         tobj.key = field.key;
145
146                 newpath = newpath + '-'+ col;
147         }
148
149         _debug("built 'from' clause: path="+path+"\n"+formatJSON(js2JSON(obj)));
150         return obj;
151 }
152
153
154 /* removes a specific item from the display window */
155 function oilsDelDisplayItem(val) {
156         oilsDelSelectorItem(oilsRptDisplaySelector, val);
157 }
158
159 /* removes selected items from the display window */
160 function oilsDelSelectedDisplayItems() {
161         var list = oilsDelSelectedItems(oilsRptDisplaySelector);
162
163         /* remove the de-selected columns from the report output */
164         oilsRpt.def.select = grep( oilsRpt.def.select, 
165                 function(i) {
166                         for( var j = 0; j < list.length; j++ ) {
167                                 var d = list[j];
168                                 var col = i.column;
169
170                                 /* if this columsn has a transform, 
171                                         it will be an object { tform => column } */
172                                 if( typeof col != 'string' ) 
173                                         for( var c in col ) col = col[c];
174
175                                 /* if this transform requires params, the column 
176                                         will be the first item in the param set array */
177                                 if( typeof col != 'string' ) col = col[0];
178
179                                 if( oilsRptPathRel(d) == i.relation && oilsRptPathCol(d) == col ) {
180                                         var param = (i.alias) ? i.alias.match(/::PARAM\d*/) : null;
181                                         if( param ) delete oilsRpt.params[param];
182                                         return false;
183                                 }
184                         }
185                         return true;
186                 }
187         );
188
189         if(!oilsRpt.def.select) {
190                 oilsRpt.def.select = [];
191                 oilsReportBuilderReset();
192
193         } else {
194                 for( var j = 0; j < list.length; j++ ) 
195                         /* if there are no items left in the "select" clause for the given 
196                                 relation, trim this relation from the "from" clause */
197                         if(!grep(oilsRpt.def.select,
198                                         function(i){ return (i.relation == oilsRptPathRel(list[j])); })) 
199                                 oilsRptPruneFromClause(oilsRptPathRel(list[j]));
200         }
201
202         oilsRptDebug();
203 }
204
205 /* for each item in the path list, remove the associated data
206         from the "from" clause */
207
208 function oilsRptPruneFromClause(relation, node) {
209         _debug("removing relation from 'from' clause " + relation);
210         if(!node) node = oilsRpt.def.from.join;
211         for( var i in node ) {
212                 if( node[i].alias == relation ) {
213                         if( node[i].join ) {
214                                 /* if we have subtrees, don't delete our tree node */
215                                 return false;
216                         } else {
217                                 delete node[i];
218                                 return true;
219                         } 
220                 } else {
221                         if( node[i].join ) {
222                                 if( oilsRptPruneFromClause(relation, node[i].join ) ) {
223                                         if(oilsRptObjectKeys(node[i].join).length == 0) {
224                                                 delete node[i].join;
225                                                 /* if there are no items in the select clause with a relation matching
226                                                         this nodes alias, we can safely remove this node from the tree */
227                                                 if(!grep(oilsRpt.def.select,function(r){return (r.relation==node[i].alias)}))
228                                                         delete node[i];
229                                                 return true;
230                                         }
231                                 }
232                         }
233                 }
234         }
235         return false;
236 }
237
238 /* adds an item to the display window */
239 function oilsAddRptFilterItem(val) {
240         oilsAddSelectorItem(oilsRptFilterSelector, val);
241 }
242
243 /* removes a specific item from the display window */
244 function oilsDelFilterItem(val) {
245         oilsDelSelectorItem(oilsRptFilterSelector, val);
246 }
247
248 /* removes selected items from the display window */
249 function oilsDelSelectedFilterItems() {
250         oilsDelSelectedItems(oilsRptFilterSelector);
251 }
252
253
254 /* adds an item to the display window */
255 function oilsAddSelectorItem(sel, val, name) {
256         name = (name) ? name : oilsRptMakeLabel(val);
257         _debug("adding selector item "+name+' = ' +val);
258         for( var i = 0; i < sel.options.length; i++ ) {
259                 var opt = sel.options[i];
260                 if( opt.value == val ) return false;
261         }
262         insertSelectorVal( sel, -1, name, val );
263         return true;
264 }
265
266
267 /* removes a specific item from the display window */
268 function oilsDelSelectorItem(sel, val) {
269         _debug("deleting selector item "+val);
270         var opts = sel.options;
271         for( var i = 0; i < opts.length; i++ ) {
272                 var opt = opts[i];
273                 if( opt.value == val )  {
274                         if( i == opts.length - 1 ) 
275                                 opts[i] = null;
276                         else opts[i] = opts[i+1];
277                         return;
278                 }
279         }
280 }
281
282 /* removes selected items from the display window */
283 function oilsDelSelectedItems(sel) {
284         var list = getSelectedList(sel);
285         for( var i = 0; i < list.length; i++ ) 
286                 oilsDelSelectorItem(sel, list[i]);
287         return list;
288 }
289
290
291 /* hides the different field editor tabs */
292 function oilsRptHideEditorDivs() {
293         hideMe(DOM.oils_rpt_tform_div);
294         hideMe(DOM.oils_rpt_filter_div);
295         hideMe(DOM.oils_rpt_agg_filter_div);
296 }
297
298
299 /**
300   This draws the 3-tabbed window containing the transform,
301   filter, and aggregate filter picker window
302   */
303 function oilsRptDrawDataWindow(path) {
304         var col = oilsRptPathCol(path);
305         var cls = oilsRptPathClass(path);
306         var field = oilsRptFindField(oilsIDL[cls], col);
307
308         appendClear(DOM.oils_rpt_editor_window_label, text(oilsRptMakeLabel(path)));
309         appendClear(DOM.oils_rpt_editor_window_datatype, text(field.datatype));
310
311         _debug("setting update data window for column "+col+' on class '+cls);
312
313         var div = DOM.oils_rpt_column_editor;
314         /* set a preliminary top position so the page won't bounce around */
315         div.setAttribute('style','top:'+oilsMouseX+'px');
316
317         /* unhide the div so we can determine the dimensions */
318         unHideMe(div);
319
320         /* don't let them see the floating div until the position is fully determined */
321         div.style.visibility='hidden'; 
322
323         oilsRptDrawTransformWindow(path, col, cls, field);
324         oilsRptDrawFilterWindow(path, col, cls, field);
325
326         //oilsRptSetFilters(field.datatype);
327
328         //oilsRptDoFilterWidgets();
329
330         //DOM.oils_rpt_filter_tform_selector.onchange = oilsRptDoFilterWidgets;
331
332         buildFloatingDiv(div, 600);
333
334         /* now let them see it */
335         div.style.visibility='visible';
336
337         oilsRptSetDataWindowActions(div);
338 }
339
340
341 function oilsRptSetDataWindowActions(div) {
342         /* give the tab links behavior */
343         DOM.oils_rpt_tform_tab.onclick = 
344                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_tform_div)};
345         DOM.oils_rpt_filter_tab.onclick = 
346                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_filter_div)};
347         DOM.oils_rpt_agg_filter_tab.onclick = 
348                 function(){oilsRptHideEditorDivs();unHideMe(DOM.oils_rpt_agg_filter_div)};
349
350         DOM.oils_rpt_tform_tab.onclick();
351         DOM.oils_rpt_column_editor_close_button.onclick = function(){hideMe(div);};
352 }
353
354
355 function oilsRptDrawFilterWindow(path, col, cls, field) {
356         oilsRptCurrentFilterTform = new oilsRptTFormManager(DOM.oils_rpt_filter_tform_table);
357         oilsRptCurrentFilterTform.build(field.datatype, false, true);
358         oilsRptCurrentFilterOpManager = new oilsRptOpManager(DOM.oils_rpt_filter_op_table);
359 }
360
361
362 /* draws the transform window */
363 function oilsRptDrawTransformWindow(path, col, cls, field) {
364         DOM.oils_rpt_tform_label_input.value = oilsRptMakeLabel(path);
365         var dtype = field.datatype;
366
367         DOM.oils_rpt_tform_submit.onclick = 
368                 function(){ 
369                         /*
370                         var tform = oilsRptGetTform(dtype);
371                         _debug('found tform: ' + js2JSON(tform));
372                         var params = getRptTformParams(dtype, tform);
373                         _debug('found tform params: ' + js2JSON(params));
374                         tform = (tform == 'raw') ? null : tform;
375                         */
376
377                         var tform = oilsRptCurrentTform.getCurrentTForm();
378                         oilsAddRptDisplayItem(path, DOM.oils_rpt_tform_label_input.value, tform.value, tform.params ) 
379                 };
380
381
382         DOM.oils_rpt_tform_label_input.focus();
383         DOM.oils_rpt_tform_label_input.select();
384
385         oilsRptCurrentTform = new oilsRptTFormManager(DOM.oils_rpt_tform_table);
386         oilsRptCurrentTform.build(dtype, true, true);
387
388         /*
389         oilsRptHideTformFields();
390         oilsRptUnHideTformFields(dtype);
391         */
392
393         _debug("Building transiform window for datatype "+dtype);
394
395         /*
396         unHideMe($('oils_rpt_tform_'+dtype+'_div'));
397         $('oils_rpt_tform_all_raw').checked = true;
398         */
399 }
400
401 /*
402 function oilsRptHideTformFields() {
403         var rows = DOM.oils_rpt_tform_tbody.childNodes;
404         for( var i = 0; i < rows.length; i++ )
405                 if( rows[i] && rows[i].nodeType == 1 )
406                         hideMe(rows[i]);
407 }
408
409 function oilsRptUnHideTformFields(dtype) {
410         var rows = DOM.oils_rpt_tform_tbody.childNodes;
411         for( var i = 0; i < rows.length; i++ ) {
412                 var row = rows[i]
413                 if( row && row.nodeType == 1 && 
414                         (row.getAttribute('datatype')=='all' 
415                                 || row.getAttribute('datatype') == dtype)) {
416                         unHideMe(row);
417                 }
418         }
419 }
420
421
422 function oilsRptGetTform(datatype) {
423         for( var i in oilsRptTransforms[datatype] ) 
424                 if( $('oils_rpt_tform_'+datatype+'_'+oilsRptTransforms[datatype][i]).checked )
425                         return oilsRptTransforms[datatype][i];
426         for( var i in oilsRptTransforms.all ) 
427                 if( $('oils_rpt_tform_all_'+oilsRptTransforms.all[i]).checked )
428                         return oilsRptTransforms.all[i];
429         return null;
430 }
431 */
432
433
434 /*
435 function getRptTformParams(type, tform) {
436         switch(type) {
437                 case 'string' :
438                         switch(tform) {
439                                 case 'substring' :
440                                         return [
441                                                 DOM.oils_rpt_tform_string_substring_offset.value, 
442                                                 DOM.oils_rpt_tform_string_substring_length.value];
443                         }
444         }
445 }
446 */
447
448
449 /* given a transform selector, this displays the appropriate 
450         transforms for the given datatype.
451         if aggregate is true, is displays the aggregate transforms */
452 /*
453 function oilsRptSetTransforms(sel, dtype, show_agg, show_noagg) {
454         for( var i = 0; i < sel.options.length; i++ ) {
455                 var opt = sel.options[i];
456                 var t = opt.getAttribute('datatype');
457                 if( t && t != dtype ){
458                         hideMe(opt);
459                 } else {
460                         var ag = opt.getAttribute('aggregate');
461                         if( ag && show_agg )
462                                 unHideMe(opt);
463                         else if( ag && ! show_agg )
464                                 hideMe(opt)
465                         else if( !ag && show_noagg )
466                                 unHideMe(opt);
467                         else
468                                 hideMe(opt);
469                 }
470         }
471 }
472 */
473
474
475 /* displays the correct filter-transforms for the given datatype */
476 /*
477 function oilsRptSetFilters(dtype) {
478
479         DOM.oils_rpt_filter_submit.onclick = function() {
480                 var data = oilsRptDoFilterWidgets();
481                 alert(js2JSON(data));
482         }
483
484         var sel = DOM.oils_rpt_filter_tform_selector;
485         for( var i = 0; i < sel.options.length; i++ ) {
486                 var opt = sel.options[i];
487                 _debug(opt.getAttribute('op'));
488                 var t = opt.getAttribute('datatype');
489                 if( t && t != dtype ) hideMe(opt);
490                 else unHideMe(opt);
491         }
492 }
493 */
494
495 /* hides all of the filter widgets */
496 function oilsRptHideFilterWidgets(node) {
497         if(!node)
498                 node = DOM.oils_rpt_filter_tform_widget_td;
499         if( node.nodeType != 1 ) return;
500         if( node.getAttribute('widget') ) {
501                 hideMe(node);
502         } else {
503                 var cs = node.childNodes;
504                 for( var i = 0; cs && i < cs.length; i++ )
505                         oilsRptHideFilterWidgets(cs[i]);
506         }
507 }
508
509 /* what does this need to do? */
510 function oilsRptSetFilterOpActions() {
511 }
512
513
514
515 /* hides/unhides the appropriate widgets and returns the parameter
516         array appropriate for the selected widget */
517 function oilsRptDoFilterWidgets() {
518         filter = getSelectorVal(DOM.oils_rpt_filter_tform_selector);
519         oilsRptHideFilterWidgets();
520         var op = null;
521         var tform = null;
522         var params = null;
523
524         switch(filter) {
525                 
526                 /* generic transforms */
527                 case 'equals':
528                         if(!op) op = 'equals';
529                 case 'like':
530                         if(!op) op = 'like';
531                 case 'ilike':
532                         if(!op) op = 'ilike';
533                 case 'gt':
534                         if(!op) op = '>';
535                 case 'gte':
536                         if(!op) op = '>=';
537                 case 'lt':
538                         if(!op) op = '<';
539                 case 'lte':
540                         if(!op) op = '<=';
541                 case 'in':
542                         if(!op) op = 'in';
543                 case 'not_in':
544                         if(!op) op = 'not in';
545                 case 'between':
546                         if(!op) op = 'between';
547                 case 'not_between':
548                         if(!op) op = 'not between';
549                         unHideMe(DOM.oils_rpt_filter_tform_input);      
550                         params = [DOM.oils_rpt_filter_tform_input.value];
551                         break;
552
553                 /* timestamp transforms */
554                 case 'date_between':
555                         if(!op) op = 'between';
556                 case 'date_not_between':
557                         if(!op) op = 'not between';
558                         tform = 'date';
559                         var d = new Date();
560                         unHideMe(DOM.oils_rpt_filter_tform_date_1);
561                         unHideMe(DOM.oils_rpt_filter_tform_date_2);
562                         unHideMe(DOM.oils_rpt_filter_tform_date_hint);
563                         DOM.oils_rpt_filter_tform_date_1.value = mkYearMonDay();
564                         DOM.oils_rpt_filter_tform_date_2.value = mkYearMonDay();
565                         params = [
566                                 DOM.oils_rpt_filter_tform_date_1.value,
567                                 DOM.oils_rpt_filter_tform_date_2.value
568                         ];
569                         break;
570
571                 case 'dow_between':
572                         op = 'between';
573                         if(!tform) tform = 'dow';
574                 case 'dow_not_between':
575                         if(!op) op = 'not between';
576                         if(!tform) tform = 'dow';
577                         break;
578
579                 case 'dom_between':
580                         op = 'between';
581                         if(!tform) tform = 'dom';
582                 case 'dom_not_between':
583                         if(!op) op = 'not between';
584                         if(!tform) tform = 'dom';
585                         break;
586
587                 case 'month_between':
588                         op = 'between';
589                         if(!tform) tform = 'moy';
590                 case 'month_not_between':
591                         if(!op) op = 'not between';
592                         if(!tform) tform = 'moy';
593                         break;
594
595                 case 'quarter_between':
596                         op = 'between';
597                         if(!tform) tform = 'qoy';
598                 case 'quarter_not_between':
599                         if(!op) op = 'not between';
600                         if(!tform) tform = 'qoy';
601                         break;
602
603                 case 'year_between':
604                         if(!op) op = 'between';
605                         if(!tform) tform = 'year_trunc';
606                 case 'year_not_between':
607                         if(!op) op = 'not between';
608                         if(!tform) tform = 'year_trunc';
609                         break;
610
611                 case 'age_between':
612                         if(!op) op = 'between';
613                         if(!tform) tform = 'age';
614                 case 'age_not_between':
615                         if(!op) op = 'not between';
616                         if(!tform) tform = 'age';
617                         break;
618
619                 /* string transforms */
620                 case 'substring':
621                         if(!tform) tform = 'substring';
622                         break;
623
624                 case 'lower':
625                         if(!op) op = '';
626                         if(!tform) tform = 'dow';
627
628                 case 'upper':
629                         if(!op) op = '';
630                         if(!tform) tform = 'dow';
631
632                 /* numeric transforms */
633                 case 'round':
634                         if(!op) op = '';
635                         if(!tform) tform = 'dow';
636
637                 case 'int':
638                         if(!op) op = '';
639                         if(!tform) tform = 'dow';
640         }
641
642         return { op : op, params : params, tform : tform };
643 }
644
645
646
647