]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_widget.js
LP#1329503 View Edit Reports
[working/Evergreen.git] / Open-ILS / web / reports / oils_rpt_widget.js
1 /* --------------------------------------------------------------------- 
2         Represents a set of values, an inputWidget collects data and a 
3         multi-select displays the data and allows the user to remove items
4         --------------------------------------------------------------------- */
5 function oilsRptSetWidget(args) {
6         this.node = args.node;
7         this.inputWidget = new args.inputWidget(args);
8     this.readonly = Boolean(args.readonly);
9         this.dest = elem('select',
10                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
11     this.dest.disabled = this.readonly;
12 }
13
14 oilsRptSetWidget.prototype.draw = function() {
15
16         this.addButton = elem('input',{type:'submit',value:"Add"})
17         this.delButton = elem('input',{type:'submit',value:"Del"})
18         this.addButton.disabled = this.readonly;
19         this.delButton.disabled = this.readonly;
20
21         var obj = this;
22         this.addButton.onclick = function() {
23                 obj.addDisplayItems(obj.inputWidget.getDisplayValue());
24         }
25
26         this.delButton.onclick = function(){obj.removeSelected()};
27
28         removeChildren(this.node);
29         this.inputWidget.draw();
30         this.node.appendChild(elem('br'))
31         this.node.appendChild(this.addButton);
32         this.node.appendChild(this.delButton);
33         this.node.appendChild(elem('br'))
34         this.node.appendChild(this.dest);
35
36     // propagate the values from the input widget into the our display.
37     if (this.inputWidget.seedValue)
38             this.addButton.onclick();
39 }
40
41 oilsRptSetWidget.prototype.addDisplayItems = function(list) {
42         if( list.constructor != Array ) list = [list];
43         for(var i = 0; i < list.length; i++) {
44                 var item = list[i];
45
46                 /* no dupes */
47                 var exists = false;
48                 iterate(this.dest.options, 
49                         function(o){if(o.getAttribute('value') == item.value) {exists = true; return;}});
50                 if(exists) continue;
51
52                 _debug('Inserting SetWidget values ' + js2JSON(item));
53                 insertSelectorVal(this.dest, -1, item.label, this.objToStr(item.value));
54         }
55 }
56
57 oilsRptSetWidget.prototype.removeSelected = function() {
58         oilsDelSelectedItems(this.dest);
59 }
60
61 oilsRptSetWidget.prototype.getValue = function() {
62         var vals = [];
63         var obj = this;
64         iterate(this.dest, function(i){vals.push(obj.strToObj(i.getAttribute('value')))});
65         return vals;
66 }
67
68 oilsRptSetWidget.prototype.objToStr = function(obj) {
69         if( typeof obj == 'string' ) return obj;
70         //return ':'+obj.transform+':'+obj.params[0];
71         var str = ':'+obj.transform;
72         for( var i = 0; i < obj.params.length; i++ ) 
73                 str += ':' + obj.params[i];
74         _debug("objToStr(): built string " + str);
75         return str;
76
77 }
78
79 oilsRptSetWidget.prototype.strToObj = function(str) {
80         if( str.match(/^:.*/) ) {
81                 var parts = str.split(/:/);
82                 _debug("strToObj(): " + str + ' : ' + parts);
83                 parts.shift();
84                 var tform = parts.shift();
85                 //var tform = str.replace(/^:(.*):.*/,'$1');
86                 //var param = str.replace(/^:.*:(.*)/,'$1');
87                 return { transform : tform, params : parts };
88         }
89         return str;
90 }
91
92
93 /* --------------------------------------------------------------------- 
94         represents a widget that has start and end values.  start and end
95         are gathered from start/end widgets
96         --------------------------------------------------------------------- */
97 function oilsRptBetweenWidget(args) {
98         this.node = args.node;
99         this.startWidget = new args.startWidget(args);
100         this.endWidget = new args.endWidget(args);
101 }
102 oilsRptBetweenWidget.prototype.draw = function() {
103         removeChildren(this.node);
104         this.startWidget.draw();
105         this.node.appendChild(elem('hr'));
106         this.node.appendChild(elem('div',
107                 {style:'text-align:center;width:100%;font-weight:bold'},' - And - '));
108         this.node.appendChild(elem('hr'));
109         this.endWidget.draw();
110 }
111 oilsRptBetweenWidget.prototype.getValue = function() {
112         return [
113                 this.startWidget.getValue(),
114                 this.endWidget.getValue()
115         ];
116 }
117
118
119
120
121 /* --------------------------------------------------------------------- 
122         ATOMIC WIDGETS
123         --------------------------------------------------------------------- */
124
125
126 /* --------------------------------------------------------------------- 
127         Atomic text input widget
128         --------------------------------------------------------------------- */
129 function oilsRptTextWidget(args) {
130         this.node = args.node;
131     this.seedValue = args.value;
132         this.dest = elem('input',{type:'text',size:12});
133     this.dest.disabled = Boolean(args.readonly);
134         oilsRptMonitorWidget(this.dest);
135 }
136 oilsRptTextWidget.prototype.draw = function() {
137         this.node.appendChild(this.dest);
138     // TODO: untested
139     if (this.seedValue) {
140         this.dest.value = this.seedValue;
141         this.dest.onchange(); // validation
142     }
143 }
144
145 /* returns the "real" value for the widget */
146 oilsRptTextWidget.prototype.getValue = function() {
147         return this.dest.value;
148 }
149
150 /* returns the label and "real" value for the widget */
151 oilsRptTextWidget.prototype.getDisplayValue = function() {
152         return { label : this.getValue(), value : this.getValue() };
153 }
154
155
156
157 /* --------------------------------------------------------------------- 
158         Atomic bool input widget
159         --------------------------------------------------------------------- */
160 function oilsRptBoolWidget(args) {
161         this.node = args.node;
162     this.seedValue = arg.value;
163         this.selector = elem('select');
164         insertSelectorVal(this.selector, -1,'True','t');
165         insertSelectorVal(this.selector, -1,'False','f');
166     this.selector.disabled = Boolean(args.readonly);
167 }
168
169 oilsRptBoolWidget.prototype.draw = function() {
170         this.node.appendChild(this.selector);
171     if (this.seedValue)  // TODO: untested
172         setSelector(this.selector, this.seedValue);
173 }
174
175 /* returns the "real" value for the widget */
176 oilsRptBoolWidget.prototype.getValue = function() {
177         return getSelectorVal(this.selector);
178 }
179
180 /* returns the label and "real" value for the widget */
181 oilsRptBoolWidget.prototype.getDisplayValue = function() {
182         var val = getSelectorVal(this.selector);
183         var label = 'True';
184         if (val == 'f') labal = 'False';
185         return { label : label, value : val };
186 }
187
188
189 /* If you monitor a text widget with this function, it 
190         will style the input differently to indicate the
191         field needs data.  If a regex is provided, it will
192         style the field differently until the data matches 
193         the regex.  The style comes from OILS_RPT_INVALID_DATA. */
194 function oilsRptMonitorWidget(input, regex) {
195         addCSSClass(input, OILS_RPT_INVALID_DATA);
196         var func = function() {
197                 var val = input.value;
198                 if(!val) {
199                         addCSSClass(input, OILS_RPT_INVALID_DATA);
200                 } else {
201                         if( regex ) {
202                                 if( val && val.match(regex) ) 
203                                         removeCSSClass(input, OILS_RPT_INVALID_DATA);
204                                 else
205                                         addCSSClass(input, OILS_RPT_INVALID_DATA);
206                         } else {
207                                 removeCSSClass(input, OILS_RPT_INVALID_DATA);
208                         }
209                 }
210         }
211
212         input.onkeyup = func;
213         input.onchange = func;
214 }
215
216
217
218
219 /* --------------------------------------------------------------------- 
220         Atomic calendar widget
221         --------------------------------------------------------------------- */
222 function oilsRptCalWidget(args) {
223         this.node = args.node;
224         this.calFormat = args.calFormat;
225         this.input = elem('input',{type:'text',size:12});
226     this.seedValue = args.value;
227     this.input.disabled = Boolean(args.readonly);
228
229         oilsRptMonitorWidget(this.input, args.regex);
230
231         if( args.inputSize ) {
232                 this.input.setAttribute('size',args.inputSize);
233                 this.input.setAttribute('maxlength',args.inputSize);
234         }
235 }
236
237 oilsRptCalWidget.prototype.draw = function() {
238         this.button = DOM.generic_calendar_button.cloneNode(true);
239         this.button.id = oilsNextId();
240         this.input.id = oilsNextId();
241
242         this.node.appendChild(this.button);
243         this.node.appendChild(this.input);
244         unHideMe(this.button);
245
246         _debug('making calendar widget with format ' + this.calFormat);
247
248         Calendar.setup({
249                 inputField      : this.input.id,
250                 ifFormat                : this.calFormat,
251                 button          : this.button.id,
252                 align                   : "Tl", 
253                 singleClick     : true
254         });
255
256     if (this.seedValue) {
257         this.input.value = this.seedValue;
258         this.input.onchange(); // validation
259     }
260 }
261
262 oilsRptCalWidget.prototype.getValue = function() {
263         return this.input.value;
264 }
265
266 oilsRptCalWidget.prototype.getDisplayValue = function() {
267         return { label : this.getValue(), value : this.getValue() };
268 }
269
270
271 /* --------------------------------------------------------------------- 
272         Atomic org widget
273         --------------------------------------------------------------------- */
274 function oilsRptOrgSelector(args) {
275         this.node = args.node;
276     this.seedValue = args.value;
277         this.selector = elem('select',
278                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
279     this.selector.disabled = Boolean(args.readonly);
280 }
281
282 oilsRptOrgSelector.prototype.draw = function(org) {
283         if(!org) org = globalOrgTree;
284         var opt = insertSelectorVal( this.selector, -1, 
285                 org.shortname(), org.id(), null, findOrgDepth(org) );
286         if( org.id() == oilsRptCurrentOrg )
287                 opt.selected = true;
288         
289         /* sometimes we need these choices 
290         if( !isTrue(findOrgType(org.ou_type()).can_have_vols()) )
291                 opt.disabled = true;
292                 */
293
294         if( org.children() ) {
295                 for( var c = 0; c < org.children().length; c++ )
296                         this.draw(org.children()[c]);
297         }
298         this.node.appendChild(this.selector);
299
300     if (this.seedValue) {
301         if (dojo.isArray(this.seedValue)) {
302             for (var i = 0; i < this.selector.options.length; i++) {
303                 var opt = this.selector.options[i];
304                 if (this.seedValue.indexOf(opt.value) > -1)
305                     opt.selected = true;
306             }
307         } else {
308             setSelector(this.selector, this.seedValue);
309         }
310     }
311 }
312
313 oilsRptOrgSelector.prototype.getValue = function() {
314         var vals = [];
315         iterate(this.selector, /* XXX this.selector.options?? */
316                 function(o){
317                         if( o.selected )
318                                 vals.push(o.getAttribute('value'))
319                 }
320         );
321         return vals;
322 }
323
324 oilsRptOrgSelector.prototype.getDisplayValue = function() {
325         var vals = [];
326         iterate(this.selector,
327                 function(o){
328                         if( o.selected )
329                                 vals.push({ label : o.innerHTML, value : o.getAttribute('value')});
330                 }
331         );
332         return vals;
333 }
334
335
336 /* --------------------------------------------------------------------- 
337         Atomic age widget
338         --------------------------------------------------------------------- */
339 function oilsRptAgeWidget(args) {
340         this.node = args.node;
341     this.seedValue = args.value;
342         this.count = elem('select');
343         this.type = elem('select');
344     this.count.disabled = Boolean(args.readonly);
345     this.type.disabled = Boolean(args.readonly);
346 }
347
348 oilsRptAgeWidget.prototype.draw = function() {
349
350         //insertSelectorVal(this.count, -1, ' -- Select One -- ', '');
351         for( var i = 1; i < 25; i++ )
352                 insertSelectorVal(this.count, -1, i, i);
353
354         //insertSelectorVal(this.type, -1, ' -- Select One -- ', '');
355         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_DAYS, 'days');
356         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_MONTHS, 'months');
357         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_YEARS, 'years');
358         this.node.appendChild(this.count);
359         this.node.appendChild(this.type);
360
361     if (this.seedValue) { // TODO: test me
362         var parts = this.seedValue.split(/ /);
363         setSelector(this.count, parts[0]);
364         setSelector(this.type, parts[1]);
365     }
366 }
367
368 oilsRptAgeWidget.prototype.getValue = function() {
369         var count = getSelectorVal(this.count);
370         var type = getSelectorVal(this.type);
371         return count+''+type;
372 }
373
374 oilsRptAgeWidget.prototype.getDisplayValue = function() {
375         var val = { value : this.getValue() };
376         var label = getSelectorVal(this.count) + ' ';
377         for( var i = 0; i < this.type.options.length; i++ ) {
378                 var opt = this.type.options[i];
379                 if( opt.selected )
380                         label += opt.innerHTML;
381         }
382         val.label = label;
383         return val;
384 }
385
386
387
388 /* --------------------------------------------------------------------- 
389         Atomic substring picker
390         --------------------------------------------------------------------- */
391 function oilsRptSubstrWidget(args) {
392         this.node = args.node
393     this.seedValue = args.value;
394         this.data = elem('input',{type:'text',size:12})
395         this.offset = elem('input',{type:'text',size:5})
396         this.length = elem('input',{type:'text',size:5})
397     this.offset.disabled = Boolean(args.readonly);
398     this.length.disabled = Boolean(args.readonly);
399 }
400
401 oilsRptSubstrWidget.prototype.draw = function() {
402         this.node.appendChild(text('string: '))
403         this.node.appendChild(this.data);
404         this.node.appendChild(elem('br'));
405         this.node.appendChild(text('offset: '))
406         this.node.appendChild(this.offset);
407         this.node.appendChild(elem('br'));
408         this.node.appendChild(text('length: '))
409         this.node.appendChild(this.length);
410
411     if (this.seedValue) { 
412         // TODO: unested; substring currently not supported.
413         this.data.value = this.seedValue[0];
414         this.offset.value = this.seedValue[1];
415         this.length.value = this.seedValue[2];
416     }
417 }
418
419 oilsRptSubstrWidget.prototype.getValue = function() {
420         return {
421                 transform : 'substring',
422                 params : [ this.data.value, this.offset.value, this.length.value ]
423         };
424 }
425
426 oilsRptSubstrWidget.prototype.getDisplayValue = function() {
427         return {
428                 label : this.data.value + ' : ' + this.offset.value + ' : ' + this.length.value,
429                 value : this.getValue()
430         };
431 }
432
433
434 /* --------------------------------------------------------------------- 
435         Atomic number picker
436         --------------------------------------------------------------------- */
437 function oilsRptNumberWidget(args) {
438         this.node = args.node;
439     this.seedValue = args.value;
440         this.size = args.size || 32;
441         this.start = args.start;
442         this.selector = elem('select');
443     this.selector.disabled = Boolean(args.readonly);
444 }
445 oilsRptNumberWidget.prototype.draw = function() {
446         //insertSelectorVal(this.selector, -1, ' -- Select One -- ', '');
447         for( var i = this.start; i < (this.size + this.start); i++ )
448                 insertSelectorVal(this.selector, -1, i, i);
449         this.node.appendChild(this.selector);
450         var obj = this;
451
452     if (this.seedValue) // TODO: test me
453         setSelector(this.selector, this.seedValue);
454 }
455
456 oilsRptNumberWidget.prototype.getValue = function() {
457         return getSelectorVal(this.selector);
458 }
459
460 oilsRptNumberWidget.prototype.getDisplayValue = function() {
461         return { label : this.getValue(), value : this.getValue() };
462 }
463
464 function oilsRptNullWidget(args) {
465     this.node = args.node;
466     this.type = args.type;
467 }
468 oilsRptNullWidget.prototype.draw = function() {}
469 oilsRptNullWidget.prototype.getValue = function() {
470     return null;
471 }
472
473 function oilsRptTemplateWidget(args) {
474     this.node = args.node;
475     this.value = args.value;
476 }
477 oilsRptTemplateWidget.prototype.draw = function() {
478     this.node.appendChild(text(''+this.value));
479 }
480
481 /* --------------------------------------------------------------------- 
482         Relative dates widget
483         -------------------------------------------------------------------- */
484 function oilsRptTruncPicker(args) {
485         this.node = args.node;
486         this.type = args.type;
487     this.seedValue = args.value; // TODO: FINISH
488         this.realSpan = elem('span');
489         this.relSpan = elem('span');
490         hideMe(this.relSpan);
491         args.node = this.realSpan;
492         this.calWidget = new oilsRptCalWidget(args);
493         args.node = this.node;
494
495         this.selector = elem('select');
496         insertSelectorVal(this.selector,-1,rpt_strings.WIDGET_REAL_DATE,1);
497         insertSelectorVal(this.selector,-1,rpt_strings.WIDGET_RELATIVE_DATE,2);
498     this.selector.disabled = Boolean(args.readonly);
499
500         this.numberPicker = 
501                 new oilsRptNumberWidget(
502             {node:this.relSpan,size:90,start:1,readonly:args.readonly});
503
504         this.label = 'Day(s)';
505         if(this.type == 'month') this.label = rpt_strings.WIDGET_MONTHS;
506         if(this.type == 'quarter') this.label = rpt_strings.WIDGET_QUARTERS;
507         if(this.type == 'year') this.label = rpt_strings.WIDGET_YEARS;
508         if(this.type == 'date') this.label = rpt_strings.WIDGET_DAYS;
509 }
510
511 oilsRptTruncPicker.prototype.draw = function() {
512         this.node.appendChild(this.selector);
513         this.node.appendChild(this.realSpan);
514         this.node.appendChild(this.relSpan);
515         this.calWidget.draw();
516         this.numberPicker.draw();
517         this.relSpan.appendChild(text(this.label+' ago'));
518
519         var obj = this;
520         this.selector.onchange = function() {
521                 if( getSelectorVal(obj.selector) == 1 ) {
522                         unHideMe(obj.realSpan);
523                         hideMe(obj.relSpan);
524                 } else {
525                         unHideMe(obj.relSpan);
526                         hideMe(obj.realSpan);
527                 }
528         }
529 }
530
531 oilsRptTruncPicker.prototype.getValue = function() {
532         if( getSelectorVal(this.selector) == 2) {
533                 var val = this.numberPicker.getValue();
534                 var tform = 'relative_' + this.type;
535                 return { transform : tform, params : ['-'+val] };
536         }
537         return this.calWidget.getValue();
538 }
539
540 oilsRptTruncPicker.prototype.getDisplayValue = function() {
541         if( getSelectorVal(this.selector) == 2) {
542                 var num = this.numberPicker.getValue();
543                 return { label : num +' '+this.label+' ago', value : this.getValue() };
544         }
545         return this.calWidget.getDisplayValue();
546 }
547
548
549 /* --------------------------------------------------------------------- 
550         Atomic remote object picker
551         --------------------------------------------------------------------- */
552
553 function oilsRptRemoteWidget(args) {
554         this.node       = args.node;
555         this.class      = args.class;
556         this.field      = args.field;
557         this.column = args.column;
558         this.source = elem('select',
559                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
560     this.source.disabled = Boolean(args.readonly);
561 }
562
563 oilsRptRemoteWidget.prototype.draw = function() {
564         var orgcol;
565         iterate(oilsIDL[this.class].fields,
566                 function(i) {
567                         if(i.type == 'link' && i.class == 'aou') 
568                                 orgcol = i.name;
569                 }
570         );
571
572         if(orgcol) _debug("found org column for remote widget: " + orgcol);
573
574         var orgs = [];
575         iterate(oilsRptMyOrgs,function(i){orgs.push(i.id());});
576         var req = new Request(OILS_RPT_MAGIC_FETCH, SESSION, {
577                 hint:this.class,
578                 org_column : orgcol,
579                 org : orgs
580         }); 
581
582         var obj = this;
583         this.node.appendChild(this.source);
584         req.callback(function(r){obj.render(r.getResultObject())});
585         req.send();
586 }
587
588 oilsRptRemoteWidget.prototype.render = function(objs) {
589         var selector = this.field.selector;
590         objs.sort(
591                 function(a,b){
592                         if (a[selector]() > b[selector]())
593                                 return 1;
594                         else
595                                 return -1;
596                 });
597         for( var i = 0; i < objs.length; i++ ) {
598                 var obj = objs[i];
599                 var label = obj[this.field.selector]();
600                 var value = obj[this.column]();
601                 _debug("inserted remote object "+label + ' : ' + value);
602                 insertSelectorVal(this.source, -1, label, value);
603         }
604 }
605
606 oilsRptRemoteWidget.prototype.getDisplayValue = function() {
607         var vals = [];
608         iterate(this.source,
609                 function(o){
610                         if( o.selected )
611                                 vals.push({ label : o.innerHTML, value : o.getAttribute('value')});
612                 }
613         );
614         return vals;
615 }
616
617 oilsRptRemoteWidget.prototype.getValue = function() {
618         var vals = [];
619         iterate(this.source,
620                 function(o){
621                         if( o.selected )
622                                 vals.push(o.getAttribute('value'))
623                 }
624         );
625         return vals;
626 }
627
628
629
630
631 /* --------------------------------------------------------------------- 
632         CUSTOM WIDGETS
633         --------------------------------------------------------------------- */
634
635 /* --------------------------------------------------------------------- 
636         custom my-orgs picker 
637         --------------------------------------------------------------------- */
638 function oilsRptMyOrgsWidget(node, orgid, maxorg) {
639         _debug('fetching my orgs with max org of ' + maxorg);
640         this.node = node;
641         this.orgid = orgid;
642         this.maxorg = maxorg || 1;
643         this.active = true;
644         if( maxorg < 1 ) {
645                 this.node.disabled = true;
646                 this.active = false;
647         }
648 }
649
650 oilsRptMyOrgsWidget.prototype.draw = function() {
651         if(!oilsRptMyOrgs) {
652                 var req = new Request(OILS_RPT_FETCH_ORG_FULL_PATH, this.orgid);
653                 var obj = this;
654                 req.callback(
655                         function(r) { obj.drawWidget(r.getResultObject()); }
656                 );
657                 req.send();
658         } else {
659                 this.drawWidget(oilsRptMyOrgs);
660         }
661 }
662
663 oilsRptMyOrgsWidget.prototype.drawWidget = function(orglist) {
664         var sel = this.node;
665         var started = false;
666         oilsRptMyOrgs = orglist;
667         for( var i = 0; i < orglist.length; i++ ) {
668                 var org = orglist[i];
669                 var opt = insertSelectorVal( this.node, -1, 
670                         org.name(), org.id(), null, findOrgDepth(org) );
671                 if( org.id() == this.orgid )
672                         opt.selected = true;
673                 if(!started) {
674                         if( org.id() == this.maxorg ) 
675                                 started = true;
676                         else opt.disabled = true;
677                 }
678         }
679 }
680
681 oilsRptMyOrgsWidget.prototype.getValue = function() {
682         return getSelectorVal(this.node);
683 }
684
685