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