]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/reports/oils_rpt_widget.js
5f4dc2661d67704d8d436d3b2624bc919861d5c1
[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     if (this.seedValue) {
139         this.dest.value = this.seedValue;
140         this.dest.onchange(); // validation
141     }
142 }
143
144 /* returns the "real" value for the widget */
145 oilsRptTextWidget.prototype.getValue = function() {
146         return this.dest.value;
147 }
148
149 /* returns the label and "real" value for the widget */
150 oilsRptTextWidget.prototype.getDisplayValue = function() {
151         return { label : this.getValue(), value : this.getValue() };
152 }
153
154
155
156 /* --------------------------------------------------------------------- 
157         Atomic bool input widget
158         --------------------------------------------------------------------- */
159 function oilsRptBoolWidget(args) {
160         this.node = args.node;
161     this.seedValue = args.value;
162         this.selector = elem('select');
163         insertSelectorVal(this.selector, -1,'True','t');
164         insertSelectorVal(this.selector, -1,'False','f');
165     this.selector.disabled = Boolean(args.readonly);
166 }
167
168 oilsRptBoolWidget.prototype.draw = function() {
169         this.node.appendChild(this.selector);
170     if (this.seedValue)  
171         setSelector(this.selector, this.seedValue);
172 }
173
174 /* returns the "real" value for the widget */
175 oilsRptBoolWidget.prototype.getValue = function() {
176         return getSelectorVal(this.selector);
177 }
178
179 /* returns the label and "real" value for the widget */
180 oilsRptBoolWidget.prototype.getDisplayValue = function() {
181         var val = getSelectorVal(this.selector);
182         var label = 'True';
183         if (val == 'f') labal = 'False';
184         return { label : label, value : val };
185 }
186
187
188 /* If you monitor a text widget with this function, it 
189         will style the input differently to indicate the
190         field needs data.  If a regex is provided, it will
191         style the field differently until the data matches 
192         the regex.  The style comes from OILS_RPT_INVALID_DATA. */
193 function oilsRptMonitorWidget(input, regex) {
194         addCSSClass(input, OILS_RPT_INVALID_DATA);
195         var func = function() {
196                 var val = input.value;
197                 if(!val) {
198                         addCSSClass(input, OILS_RPT_INVALID_DATA);
199                 } else {
200                         if( regex ) {
201                                 if( val && val.match(regex) ) 
202                                         removeCSSClass(input, OILS_RPT_INVALID_DATA);
203                                 else
204                                         addCSSClass(input, OILS_RPT_INVALID_DATA);
205                         } else {
206                                 removeCSSClass(input, OILS_RPT_INVALID_DATA);
207                         }
208                 }
209         }
210
211         input.onkeyup = func;
212         input.onchange = func;
213 }
214
215
216
217
218 /* --------------------------------------------------------------------- 
219         Atomic calendar widget
220         --------------------------------------------------------------------- */
221 function oilsRptCalWidget(args) {
222         this.node = args.node;
223         this.calFormat = args.calFormat;
224         this.input = elem('input',{type:'text',size:12});
225     this.seedValue = args.value;
226     this.input.disabled = Boolean(args.readonly);
227
228         oilsRptMonitorWidget(this.input, args.regex);
229
230         if( args.inputSize ) {
231                 this.input.setAttribute('size',args.inputSize);
232                 this.input.setAttribute('maxlength',args.inputSize);
233         }
234 }
235
236 oilsRptCalWidget.prototype.draw = function() {
237         this.button = DOM.generic_calendar_button.cloneNode(true);
238         this.button.id = oilsNextId();
239         this.input.id = oilsNextId();
240
241         this.node.appendChild(this.button);
242         this.node.appendChild(this.input);
243         unHideMe(this.button);
244
245         _debug('making calendar widget with format ' + this.calFormat);
246
247         Calendar.setup({
248                 inputField      : this.input.id,
249                 ifFormat                : this.calFormat,
250                 button          : this.button.id,
251                 align                   : "Tl", 
252                 singleClick     : true
253         });
254
255     if (this.seedValue) {
256         this.input.value = this.seedValue;
257         this.input.onchange(); // validation
258     }
259 }
260
261 oilsRptCalWidget.prototype.getValue = function() {
262         return this.input.value;
263 }
264
265 oilsRptCalWidget.prototype.getDisplayValue = function() {
266         return { label : this.getValue(), value : this.getValue() };
267 }
268
269
270 /* --------------------------------------------------------------------- 
271         Atomic org widget
272         --------------------------------------------------------------------- */
273 function oilsRptOrgSelector(args) {
274         this.node = args.node;
275     this.seedValue = args.value;
276         this.selector = elem('select',
277                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
278     this.selector.disabled = Boolean(args.readonly);
279 }
280
281 oilsRptOrgSelector.prototype.draw = function(org) {
282         if(!org) org = globalOrgTree;
283         var opt = insertSelectorVal( this.selector, -1, 
284                 org.shortname(), org.id(), null, findOrgDepth(org) );
285         if( org.id() == oilsRptCurrentOrg && !this.seedValue)
286                 opt.selected = true;
287         
288         /* sometimes we need these choices 
289         if( !isTrue(findOrgType(org.ou_type()).can_have_vols()) )
290                 opt.disabled = true;
291                 */
292
293         if( org.children() ) {
294                 for( var c = 0; c < org.children().length; c++ )
295                         this.draw(org.children()[c]);
296         }
297         this.node.appendChild(this.selector);
298
299     if (this.seedValue) {
300         if (dojo.isArray(this.seedValue)) {
301             for (var i = 0; i < this.selector.options.length; i++) {
302                 var opt = this.selector.options[i];
303                 if (this.seedValue.indexOf(opt.value) > -1)
304                     opt.selected = true;
305             }
306         } else {
307             setSelector(this.selector, this.seedValue);
308         }
309     }
310 }
311
312 oilsRptOrgSelector.prototype.getValue = function() {
313         var vals = [];
314         iterate(this.selector, /* XXX this.selector.options?? */
315                 function(o){
316                         if( o.selected )
317                                 vals.push(o.getAttribute('value'))
318                 }
319         );
320         return vals;
321 }
322
323 oilsRptOrgSelector.prototype.getDisplayValue = function() {
324         var vals = [];
325         iterate(this.selector,
326                 function(o){
327                         if( o.selected )
328                                 vals.push({ label : o.innerHTML, value : o.getAttribute('value')});
329                 }
330         );
331         return vals;
332 }
333
334
335 /* --------------------------------------------------------------------- 
336         Atomic age widget
337         --------------------------------------------------------------------- */
338 function oilsRptAgeWidget(args) {
339         this.node = args.node;
340     this.seedValue = args.value;
341         this.count = elem('select');
342         this.type = elem('select');
343     this.count.disabled = Boolean(args.readonly);
344     this.type.disabled = Boolean(args.readonly);
345 }
346
347 oilsRptAgeWidget.prototype.draw = function() {
348
349         //insertSelectorVal(this.count, -1, ' -- Select One -- ', '');
350         for( var i = 1; i < 25; i++ )
351                 insertSelectorVal(this.count, -1, i, i);
352
353         //insertSelectorVal(this.type, -1, ' -- Select One -- ', '');
354         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_DAYS, 'days');
355         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_MONTHS, 'months');
356         insertSelectorVal(this.type, -1, rpt_strings.WIDGET_YEARS, 'years');
357         this.node.appendChild(this.count);
358         this.node.appendChild(this.type);
359
360     if (this.seedValue) { 
361         // e.g. "2months"
362         var count = this.seedValue.match(/(\d+)([^\d]+)/)[1];
363         var type = this.seedValue.match(/(\d+)([^\d]+)/)[2];
364         setSelector(this.count, count);
365         setSelector(this.type, type);
366     }
367 }
368
369 oilsRptAgeWidget.prototype.getValue = function() {
370         var count = getSelectorVal(this.count);
371         var type = getSelectorVal(this.type);
372         return count+''+type;
373 }
374
375 oilsRptAgeWidget.prototype.getDisplayValue = function() {
376         var val = { value : this.getValue() };
377         var label = getSelectorVal(this.count) + ' ';
378         for( var i = 0; i < this.type.options.length; i++ ) {
379                 var opt = this.type.options[i];
380                 if( opt.selected )
381                         label += opt.innerHTML;
382         }
383         val.label = label;
384         return val;
385 }
386
387
388
389 /* --------------------------------------------------------------------- 
390         Atomic substring picker
391         --------------------------------------------------------------------- */
392 function oilsRptSubstrWidget(args) {
393         this.node = args.node
394     this.seedValue = args.value;
395         this.data = elem('input',{type:'text',size:12})
396         this.offset = elem('input',{type:'text',size:5})
397         this.length = elem('input',{type:'text',size:5})
398     this.offset.disabled = Boolean(args.readonly);
399     this.length.disabled = Boolean(args.readonly);
400 }
401
402 oilsRptSubstrWidget.prototype.draw = function() {
403         this.node.appendChild(text('string: '))
404         this.node.appendChild(this.data);
405         this.node.appendChild(elem('br'));
406         this.node.appendChild(text('offset: '))
407         this.node.appendChild(this.offset);
408         this.node.appendChild(elem('br'));
409         this.node.appendChild(text('length: '))
410         this.node.appendChild(this.length);
411
412     if (this.seedValue) { 
413         // TODO: unested; substring currently not supported.
414         this.data.value = this.seedValue[0];
415         this.offset.value = this.seedValue[1];
416         this.length.value = this.seedValue[2];
417     }
418 }
419
420 oilsRptSubstrWidget.prototype.getValue = function() {
421         return {
422                 transform : 'substring',
423                 params : [ this.data.value, this.offset.value, this.length.value ]
424         };
425 }
426
427 oilsRptSubstrWidget.prototype.getDisplayValue = function() {
428         return {
429                 label : this.data.value + ' : ' + this.offset.value + ' : ' + this.length.value,
430                 value : this.getValue()
431         };
432 }
433
434
435 /* --------------------------------------------------------------------- 
436         Atomic number picker
437         --------------------------------------------------------------------- */
438 function oilsRptNumberWidget(args) {
439         this.node = args.node;
440     this.seedValue = args.value;
441         this.size = args.size || 32;
442         this.start = args.start;
443         this.selector = elem('select');
444     this.selector.disabled = Boolean(args.readonly);
445 }
446 oilsRptNumberWidget.prototype.draw = function() {
447         //insertSelectorVal(this.selector, -1, ' -- Select One -- ', '');
448         for( var i = this.start; i < (this.size + this.start); i++ )
449                 insertSelectorVal(this.selector, -1, i, i);
450         this.node.appendChild(this.selector);
451         var obj = this;
452
453     if (this.seedValue)
454         setSelector(this.selector, this.seedValue);
455 }
456
457 oilsRptNumberWidget.prototype.getValue = function() {
458         return getSelectorVal(this.selector);
459 }
460
461 oilsRptNumberWidget.prototype.getDisplayValue = function() {
462         return { label : this.getValue(), value : this.getValue() };
463 }
464
465 function oilsRptNullWidget(args) {
466     this.node = args.node;
467     this.type = args.type;
468 }
469 oilsRptNullWidget.prototype.draw = function() {}
470 oilsRptNullWidget.prototype.getValue = function() {
471     return null;
472 }
473
474 function oilsRptTemplateWidget(args) {
475     this.node = args.node;
476     this.value = args.value;
477 }
478 oilsRptTemplateWidget.prototype.draw = function() {
479     this.node.appendChild(text(''+this.value));
480 }
481
482 /* --------------------------------------------------------------------- 
483         Relative dates widget
484         -------------------------------------------------------------------- */
485 function oilsRptTruncPicker(args) {
486         this.node = args.node;
487         this.type = args.type;
488     this.seedValue = args.value;
489         this.realSpan = elem('span');
490         this.relSpan = elem('span');
491         hideMe(this.relSpan);
492         args.node = this.realSpan;
493         this.calWidget = new oilsRptCalWidget(args);
494         args.node = this.node;
495
496         this.selector = elem('select');
497         insertSelectorVal(this.selector,-1,rpt_strings.WIDGET_REAL_DATE,1);
498         insertSelectorVal(this.selector,-1,rpt_strings.WIDGET_RELATIVE_DATE,2);
499     this.selector.disabled = Boolean(args.readonly);
500
501         this.numberPicker = 
502                 new oilsRptNumberWidget(
503             {node:this.relSpan,size:90,start:1,readonly:args.readonly});
504
505         this.label = 'Day(s)';
506         if(this.type == 'month') this.label = rpt_strings.WIDGET_MONTHS;
507         if(this.type == 'quarter') this.label = rpt_strings.WIDGET_QUARTERS;
508         if(this.type == 'year') this.label = rpt_strings.WIDGET_YEARS;
509         if(this.type == 'date') this.label = rpt_strings.WIDGET_DAYS;
510 }
511
512 oilsRptTruncPicker.prototype.draw = function() {
513         this.node.appendChild(this.selector);
514         this.node.appendChild(this.realSpan);
515         this.node.appendChild(this.relSpan);
516         this.calWidget.draw();
517         this.numberPicker.draw();
518         this.relSpan.appendChild(text(this.label+' ago'));
519
520         var obj = this;
521         this.selector.onchange = function() {
522                 if( getSelectorVal(obj.selector) == 1 ) {
523                         unHideMe(obj.realSpan);
524                         hideMe(obj.relSpan);
525                 } else {
526                         unHideMe(obj.relSpan);
527                         hideMe(obj.realSpan);
528                 }
529         }
530
531     if (seed = this.seedValue) {
532         if (typeof seed == 'string') {
533             this.calWidget.value = seed;
534         } else {
535             // relative date transform
536             if (seed.transform.match(/relative/)) {
537                 setSelector(this.selector, 2)
538                 setSelector(this.numberPicker.selector, 
539                     Math.abs(seed.params[0]));
540                             unHideMe(this.relSpan);
541                             hideMe(this.realSpan);
542             }
543         }
544     }
545 }
546
547 oilsRptTruncPicker.prototype.getValue = function() {
548         if( getSelectorVal(this.selector) == 2) {
549                 var val = this.numberPicker.getValue();
550                 var tform = 'relative_' + this.type;
551                 return { transform : tform, params : ['-'+val] };
552         }
553         return this.calWidget.getValue();
554 }
555
556 oilsRptTruncPicker.prototype.getDisplayValue = function() {
557         if( getSelectorVal(this.selector) == 2) {
558                 var num = this.numberPicker.getValue();
559                 return { label : num +' '+this.label+' ago', value : this.getValue() };
560         }
561         return this.calWidget.getDisplayValue();
562 }
563
564
565 /* --------------------------------------------------------------------- 
566         Atomic remote object picker
567         --------------------------------------------------------------------- */
568
569 function oilsRptRemoteWidget(args) {
570         this.node       = args.node;
571         this.class      = args.class;
572         this.field      = args.field;
573         this.column = args.column;
574         this.source = elem('select',
575                 {multiple:'multiple','class':'oils_rpt_small_info_selector'});
576     this.source.disabled = Boolean(args.readonly);
577 }
578
579 oilsRptRemoteWidget.prototype.draw = function() {
580         var orgcol;
581         iterate(oilsIDL[this.class].fields,
582                 function(i) {
583                         if(i.type == 'link' && i.class == 'aou') 
584                                 orgcol = i.name;
585                 }
586         );
587
588         if(orgcol) _debug("found org column for remote widget: " + orgcol);
589
590         var orgs = [];
591         iterate(oilsRptMyOrgs,function(i){orgs.push(i.id());});
592         var req = new Request(OILS_RPT_MAGIC_FETCH, SESSION, {
593                 hint:this.class,
594                 org_column : orgcol,
595                 org : orgs
596         }); 
597
598         var obj = this;
599         this.node.appendChild(this.source);
600         req.callback(function(r){obj.render(r.getResultObject())});
601         req.send();
602 }
603
604 oilsRptRemoteWidget.prototype.render = function(objs) {
605         var selector = this.field.selector;
606         objs.sort(
607                 function(a,b){
608                         if (a[selector]() > b[selector]())
609                                 return 1;
610                         else
611                                 return -1;
612                 });
613         for( var i = 0; i < objs.length; i++ ) {
614                 var obj = objs[i];
615                 var label = obj[this.field.selector]();
616                 var value = obj[this.column]();
617                 _debug("inserted remote object "+label + ' : ' + value);
618                 insertSelectorVal(this.source, -1, label, value);
619         }
620 }
621
622 oilsRptRemoteWidget.prototype.getDisplayValue = function() {
623         var vals = [];
624         iterate(this.source,
625                 function(o){
626                         if( o.selected )
627                                 vals.push({ label : o.innerHTML, value : o.getAttribute('value')});
628                 }
629         );
630         return vals;
631 }
632
633 oilsRptRemoteWidget.prototype.getValue = function() {
634         var vals = [];
635         iterate(this.source,
636                 function(o){
637                         if( o.selected )
638                                 vals.push(o.getAttribute('value'))
639                 }
640         );
641         return vals;
642 }
643
644
645
646
647 /* --------------------------------------------------------------------- 
648         CUSTOM WIDGETS
649         --------------------------------------------------------------------- */
650
651 /* --------------------------------------------------------------------- 
652         custom my-orgs picker 
653         --------------------------------------------------------------------- */
654 function oilsRptMyOrgsWidget(node, orgid, maxorg) {
655         _debug('fetching my orgs with max org of ' + maxorg);
656         this.node = node;
657         this.orgid = orgid;
658         this.maxorg = maxorg || 1;
659         this.active = true;
660         if( maxorg < 1 ) {
661                 this.node.disabled = true;
662                 this.active = false;
663         }
664 }
665
666 oilsRptMyOrgsWidget.prototype.draw = function() {
667         if(!oilsRptMyOrgs) {
668                 var req = new Request(OILS_RPT_FETCH_ORG_FULL_PATH, this.orgid);
669                 var obj = this;
670                 req.callback(
671                         function(r) { obj.drawWidget(r.getResultObject()); }
672                 );
673                 req.send();
674         } else {
675                 this.drawWidget(oilsRptMyOrgs);
676         }
677 }
678
679 oilsRptMyOrgsWidget.prototype.drawWidget = function(orglist) {
680         var sel = this.node;
681         var started = false;
682         oilsRptMyOrgs = orglist;
683         for( var i = 0; i < orglist.length; i++ ) {
684                 var org = orglist[i];
685                 var opt = insertSelectorVal( this.node, -1, 
686                         org.name(), org.id(), null, findOrgDepth(org) );
687                 if( org.id() == this.orgid )
688                         opt.selected = true;
689                 if(!started) {
690                         if( org.id() == this.maxorg ) 
691                                 started = true;
692                         else opt.disabled = true;
693                 }
694         }
695 }
696
697 oilsRptMyOrgsWidget.prototype.getValue = function() {
698         return getSelectorVal(this.node);
699 }
700
701