]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
do not promote a number widget's value to '0' when the value is null/isNaN. that...
[working/Evergreen.git] / Open-ILS / web / js / dojo / openils / widget / AutoFieldWidget.js
1 if(!dojo._hasResource['openils.widget.AutoFieldWidget']) {
2     dojo.provide('openils.widget.AutoFieldWidget');
3     dojo.require('openils.Util');
4     dojo.require('openils.User');
5     dojo.require('fieldmapper.IDL');
6     dojo.require('openils.PermaCrud');
7         dojo.requireLocalization("openils.widget", "AutoFieldWidget");
8
9     dojo.declare('openils.widget.AutoFieldWidget', null, {
10
11         async : false,
12
13         /**
14          * args:
15          *  idlField -- Field description object from fieldmapper.IDL.fmclasses
16          *  fmObject -- If available, the object being edited.  This will be used 
17          *      to set the value of the widget.
18          *  fmClass -- Class name (not required if idlField or fmObject is set)
19          *  fmField -- Field name (not required if idlField)
20          *  parentNode -- If defined, the widget will be appended to this DOM node
21          *  dijitArgs -- Optional parameters object, passed directly to the dojo widget
22          *  orgLimitPerms -- If this field defines a set of org units and an orgLimitPerms 
23          *      is defined, the code will limit the org units in the set to those
24          *      allowed by the permission
25          *  selfReference -- The primary purpose of an AutoFieldWidget is to render the value
26          *      or widget for a field on an object (that may or may not link to another object).
27          *      selfReference allows you to sidestep the indirection and create a selector widget
28          *      based purely on an fmClass.  To get a dropdown of all of the 'abc'
29          *      objects, pass in {selfReference : true, fmClass : 'abc'}.  
30          */
31         constructor : function(args) {
32             for(var k in args)
33                 this[k] = args[k];
34
35             // find the field description in the IDL if not provided
36             if(this.fmObject) 
37                 this.fmClass = this.fmObject.classname;
38             this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
39             this.suppressLinkedFields = args.suppressLinkedFields || [];
40
41             if(this.selfReference) {
42                 this.fmField = fieldmapper.IDL.fmclasses[this.fmClass].pkey;
43                 
44                 // create a mock-up of the idlField object.  
45                 this.idlField = {
46                     datatype : 'link',
47                     'class' : this.fmClass,
48                     reltype : 'has_a',
49                     key : this.fmField,
50                     name : this.fmField
51                 };
52
53             } else {
54
55                 if(!this.idlField) {
56                     this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
57                     var fields = this.fmIDL.fields;
58                     for(var f in fields) 
59                         if(fields[f].name == this.fmField)
60                             this.idlField = fields[f];
61                 }
62             }
63
64             if(!this.idlField) 
65                 throw new Error("AutoFieldWidget could not determine which field to render.  We need more information. fmClass=" + 
66                     this.fmClass + ' fmField=' + this.fmField + ' fmObject=' + js2JSON(this.fmObject));
67
68             this.auth = openils.User.authtoken;
69             this.cache = openils.widget.AutoFieldWidget.cache;
70             this.cache[this.auth] = this.cache[this.auth] || {};
71             this.cache[this.auth].single = this.cache[this.auth].single || {};
72             this.cache[this.auth].list = this.cache[this.auth].list || {};
73         },
74
75         /**
76          * Turn the widget-stored value into a value oils understands
77          */
78         getFormattedValue : function() {
79             var value = this.baseWidgetValue();
80             switch(this.idlField.datatype) {
81                 case 'bool':
82                     switch(value) {
83                         case 'true': return 't';
84                         case 'on': return 't';
85                         case 'false' : return 'f';
86                         case 'unset' : return null;
87                         case true : return 't';
88                         default: return 'f';
89                     }
90                 case 'timestamp':
91                     if(!value) return null;
92                     return dojo.date.stamp.toISOString(value);
93                 case 'int':
94                 case 'float':
95                 case 'money':
96                     if(isNaN(value)) value = null;
97                 default:
98                     return (value === '') ? null : value;
99             }
100         },
101
102         baseWidgetValue : function(value) {
103             var attr = (this.readOnly) ? 'content' : 'value';
104             if(arguments.length) this.widget.attr(attr, value);
105             return this.widget.attr(attr);
106         },
107         
108         /**
109          * Turn the widget-stored value into something visually suitable
110          */
111         getDisplayString : function() {
112             var value = this.widgetValue;
113             switch(this.idlField.datatype) {
114                 case 'bool':
115                     switch(value) {
116                         case 'true': return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
117                         case 'false' : return openils.widget.AutoFieldWidget.localeStrings.FALSE;
118                         case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
119                         case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
120                         default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
121                     }
122                 case 'timestamp':
123                     if (!value) return '';
124                     dojo.require('dojo.date.locale');
125                     dojo.require('dojo.date.stamp');
126                     var date = dojo.date.stamp.fromISOString(value);
127                     return dojo.date.locale.format(date, {formatLength:'short'});
128                 case 'org_unit':
129                     if(value === null || value === undefined) return '';
130                     return fieldmapper.aou.findOrgUnit(value).shortname();
131                 case 'int':
132                 case 'float':
133                     if(isNaN(value)) value = 0;
134                 default:
135                     if(value === undefined || value === null)
136                         value = '';
137                     return value+'';
138             }
139         },
140
141         build : function(onload) {
142
143             if(this.widgetValue == null)
144                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
145
146             if(this.widget) {
147                 // core widget provided for us, attach and move on
148                 if(this.parentNode) // may already be in the "right" place
149                     this.parentNode.appendChild(this.widget.domNode);
150                 if(this.widget.attr('value') == null)
151                     this._widgetLoaded();
152                 return;
153             }
154             
155             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
156                 this.parentNode = dojo.create('div');
157
158             this.onload = onload;
159
160             if(this.readOnly) {
161                 dojo.require('dijit.layout.ContentPane');
162                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
163                 if(this.widgetValue !== null)
164                     this._tryLinkedDisplayField();
165
166             } else if(this.widgetClass) {
167                 dojo.require(this.widgetClass);
168                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
169
170             } else {
171
172                 switch(this.idlField.datatype) {
173                     
174                     case 'id':
175                         dojo.require('dijit.form.TextBox');
176                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
177                         break;
178
179                     case 'org_unit':
180                         this._buildOrgSelector();
181                         break;
182
183                     case 'money':
184                         dojo.require('dijit.form.CurrencyTextBox');
185                         this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
186                         break;
187
188                     case 'int':
189                         dojo.require('dijit.form.NumberTextBox');
190                         this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
191                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
192                         break;
193
194                     case 'float':
195                         dojo.require('dijit.form.NumberTextBox');
196                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
197                         break;
198
199                     case 'timestamp':
200                         dojo.require('dijit.form.DateTextBox');
201                         dojo.require('dojo.date.stamp');
202                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
203                         if(this.widgetValue != null) 
204                             this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
205                         break;
206
207                     case 'bool':
208                         if(this.ternary) {
209                             dojo.require('dijit.form.FilteringSelect');
210                             var store = new dojo.data.ItemFileReadStore({
211                                 data:{
212                                     identifier : 'value',
213                                     items:[
214                                         {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
215                                         {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
216                                         {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
217                                     ]
218                                 }
219                             });
220                             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
221                             this.widget.searchAttr = this.widget.labelAttr = 'label';
222                             this.widget.valueAttr = 'value';
223                             this.widget.store = store;
224                             this.widget.startup();
225                             this.widgetValue = (this.widgetValue === null) ? 'unset' : 
226                                 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
227                         } else {
228                             dojo.require('dijit.form.CheckBox');
229                             this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
230                             this.widgetValue = openils.Util.isTrue(this.widgetValue);
231                         }
232                         break;
233
234                     case 'link':
235                         if(this._buildLinkSelector()) break;
236
237                     default:
238                         if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
239                             dojo.require('dijit.form.ValidationTextBox');
240                             this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
241                         } else {
242                             dojo.require('dijit.form.TextBox');
243                             this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
244                         }
245                 }
246             }
247
248             if(!this.async) this._widgetLoaded();
249             return this.widget;
250         },
251
252         // we want to display the value for our widget.  However, instead of displaying
253         // an ID, for exmaple, display the value for the 'selector' field on the object
254         // the ID points to
255         _tryLinkedDisplayField : function(noAsync) {
256
257             if(this.idlField.datatype == 'org_unit')
258                 return false; // we already handle org_units, no need to re-fetch
259
260             // user opted to bypass fetching this linked data
261             if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
262                 return false;
263
264             var linkInfo = this._getLinkSelector();
265             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
266                 return false;
267             var lclass = linkInfo.linkClass;
268
269             if(lclass == 'aou') {
270                 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
271                 return;
272             }
273
274             // first try the store cache
275             var self = this;
276             if(this.cache[this.auth].list[lclass]) {
277                 var store = this.cache[this.auth].list[lclass];
278                 var query = {};
279                 query[linkInfo.vfield.name] = ''+this.widgetValue;
280                 store.fetch({query:query, onComplete:
281                     function(list) {
282                         self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
283                     }
284                 });
285                 return;
286             }
287
288             // then try the single object cache
289             if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
290                 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
291                 return;
292             }
293
294             console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
295
296             // if those fail, fetch the linked object
297             this.async = true;
298             var self = this;
299             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
300                 async : !this.forceSync,
301                 oncomplete : function(r) {
302                     var item = openils.Util.readResponse(r);
303                     var newvalue = item[linkInfo.vfield.selector]();
304
305                     if(!self.cache[self.auth].single[lclass])
306                         self.cache[self.auth].single[lclass] = {};
307                     self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
308
309                     self.widgetValue = newvalue;
310                     self.widget.startup();
311                     self._widgetLoaded();
312                 }
313             });
314         },
315
316         _getLinkSelector : function() {
317             var linkClass = this.idlField['class'];
318             if(this.idlField.reltype != 'has_a')  return false;
319             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
320             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
321
322             var vfield;
323             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
324
325             for(var f in rclassIdl.fields) {
326                 if(this.idlField.key == rclassIdl.fields[f].name) {
327                     vfield = rclassIdl.fields[f];
328                     break;
329                 }
330             }
331
332             if(!vfield) 
333                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
334
335             return {
336                 linkClass : linkClass,
337                 vfield : vfield
338             };
339         },
340
341         _buildLinkSelector : function() {
342             var selectorInfo = this._getLinkSelector();
343             if(!selectorInfo) return false;
344
345             var linkClass = selectorInfo.linkClass;
346             var vfield = selectorInfo.vfield;
347
348             this.async = true;
349
350             if(linkClass == 'pgt')
351                 return this._buildPermGrpSelector();
352             if(linkClass == 'aou')
353                 return this._buildOrgSelector();
354             if(linkClass == 'acpl')
355                 return this._buildCopyLocSelector();
356
357
358             dojo.require('dojo.data.ItemFileReadStore');
359             dojo.require('dijit.form.FilteringSelect');
360
361             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
362             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
363             this.widget.valueAttr = vfield.name;
364
365             var self = this;
366             var oncomplete = function(list) {
367                 if(list) {
368                     self.widget.store = 
369                         new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
370                     self.cache[self.auth].list[linkClass] = self.widget.store;
371                 } else {
372                     self.widget.store = self.cache[self.auth].list[linkClass];
373                 }
374                 self.widget.startup();
375                 self._widgetLoaded();
376             };
377
378             if(this.cache[self.auth].list[linkClass]) {
379                 oncomplete();
380
381             } else {
382                 new openils.PermaCrud().retrieveAll(linkClass, {   
383                     async : !this.forceSync,
384                     oncomplete : function(r) {
385                         var list = openils.Util.readResponse(r, false, true);
386                         oncomplete(list);
387                     }
388                 });
389             }
390
391             return true;
392         },
393
394         /**
395          * For widgets that run asynchronously, provide a callback for finishing up
396          */
397         _widgetLoaded : function(value) {
398             
399             if(this.readOnly) {
400
401                 /* -------------------------------------------------------------
402                    when using widgets in a grid, the cell may dissapear, which 
403                    kills the underlying DOM node, which causes this to fail.
404                    For now, back out gracefully and let grid getters use
405                    getDisplayString() instead
406                   -------------------------------------------------------------*/
407                 try { 
408                     this.baseWidgetValue(this.getDisplayString());
409                 } catch (E) {};
410
411             } else {
412
413                 this.baseWidgetValue(this.widgetValue);
414                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && !this.selfReference)
415                     this.widget.attr('disabled', true); 
416                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
417                     this.widget.attr('disabled', true); 
418             }
419             if(this.onload)
420                 this.onload(this.widget, this);
421         },
422
423         _buildOrgSelector : function() {
424             dojo.require('fieldmapper.OrgUtils');
425             dojo.require('openils.widget.FilteringTreeSelect');
426             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
427             this.widget.searchAttr = 'shortname';
428             this.widget.labelAttr = 'shortname';
429             this.widget.parentField = 'parent_ou';
430             var user = new openils.User();
431
432             if(this.widgetValue == null) 
433                 this.widgetValue = user.user.ws_ou();
434             
435             // if we have a limit perm, find the relevent orgs (async)
436             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
437                 this.async = true;
438                 var self = this;
439                 user.getPermOrgList(this.orgLimitPerms, 
440                     function(orgList) {
441                         self.widget.tree = orgList;
442                         self.widget.startup();
443                         self._widgetLoaded();
444                     }
445                 );
446
447             } else {
448                 this.widget.tree = fieldmapper.aou.globalOrgTree;
449                 this.widget.startup();
450             }
451         },
452
453         _buildPermGrpSelector : function() {
454             dojo.require('openils.widget.FilteringTreeSelect');
455             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
456             this.widget.searchAttr = 'name';
457
458             if(this.cache.permGrpTree) {
459                 this.widget.tree = this.cache.permGrpTree;
460                 this.widget.startup();
461                 return true;
462             } 
463
464             var self = this;
465             this.async = true;
466             new openils.PermaCrud().retrieveAll('pgt', {
467                 async : !this.forceSync,
468                 oncomplete : function(r) {
469                     var list = openils.Util.readResponse(r, false, true);
470                     if(!list) return;
471                     var map = {};
472                     var root = null;
473                     for(var l in list)
474                         map[list[l].id()] = list[l];
475                     for(var l in list) {
476                         var node = list[l];
477                         var pnode = map[node.parent()];
478                         if(!pnode) {root = node; continue;}
479                         if(!pnode.children()) pnode.children([]);
480                         pnode.children().push(node);
481                     }
482                     self.widget.tree = self.cache.permGrpTree = root;
483                     self.widget.startup();
484                     self._widgetLoaded();
485                 }
486             });
487
488             return true;
489         },
490
491         _buildCopyLocSelector : function() {
492             dojo.require('dijit.form.FilteringSelect');
493             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
494             this.widget.searchAttr = this.widget.labalAttr = 'name';
495             this.widget.valueAttr = 'id';
496
497             if(this.cache.copyLocStore) {
498                 this.widget.store = this.cache.copyLocStore;
499                 this.widget.startup();
500                 this.async = false;
501                 return true;
502             } 
503
504             // my orgs
505             var ws_ou = openils.User.user.ws_ou();
506             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
507             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
508
509             var self = this;
510             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
511                 async : !this.forceSync,
512                 oncomplete : function(r) {
513                     var list = openils.Util.readResponse(r, false, true);
514                     if(!list) return;
515                     self.widget.store = 
516                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
517                     self.cache.copyLocStore = self.widget.store;
518                     self.widget.startup();
519                     self._widgetLoaded();
520                 }
521             });
522
523             return true;
524         }
525     });
526
527     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
528     openils.widget.AutoFieldWidget.cache = {};
529 }
530