]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
dojo does not take kindly to numbers when it wants strings
[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
7     dojo.declare('openils.widget.AutoFieldWidget', null, {
8
9         async : false,
10         cache : {},
11         cacheSingle : {},
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          */
26         constructor : function(args) {
27             for(var k in args)
28                 this[k] = args[k];
29
30             // find the field description in the IDL if not provided
31             if(this.fmObject) 
32                 this.fmClass = this.fmObject.classname;
33             this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
34
35             if(!this.idlField) {
36                 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
37                 var fields = this.fmIDL.fields;
38                 for(var f in fields) 
39                     if(fields[f].name == this.fmField)
40                         this.idlField = fields[f];
41             }
42
43             if(!this.idlField) 
44                 throw new Error("AutoFieldWidget could not determine which field to render.  We need more information.");
45
46             this.auth = openils.User.authtoken;
47             if(!this.cache[this.auth]) {
48                 this.cache[this.auth] = {};
49             }
50         },
51
52         /**
53          * Turn the widget-stored value into a value oils understands
54          */
55         getFormattedValue : function() {
56             var value = this.baseWidgetValue();
57             switch(this.idlField.datatype) {
58                 case 'bool':
59                     return (value) ? 't' : 'f'
60                 case 'timestamp':
61                     return dojo.date.stamp.toISOString(value);
62                 case 'int':
63                 case 'float':
64                     if(isNaN(value)) value = 0;
65                 default:
66                     return (value === '') ? null : value;
67             }
68         },
69
70         baseWidgetValue : function(value) {
71             var attr = (this.readOnly) ? 'content' : 'value';
72             if(arguments.length) this.widget.attr(attr, value);
73             return this.widget.attr(attr);
74         },
75         
76         /**
77          * Turn the widget-stored value into something visually suitable
78          */
79         getDisplayString : function() {
80             var value = this.widgetValue;
81             switch(this.idlField.datatype) {
82                 case 'bool':
83                     return (value) ? 'True' : 'False'; // XXX i18n!
84                 case 'timestamp':
85                     dojo.require('dojo.date.locale');
86                     dojo.require('dojo.date.stamp');
87                     var date = dojo.date.stamp.fromISOString(value);
88                     return dojo.date.locale.format(date, {formatLength:'short'});
89                 case 'org_unit':
90                     if(value === null || value === undefined) return '';
91                     return fieldmapper.aou.findOrgUnit(value).shortname();
92                 case 'int':
93                 case 'float':
94                     if(isNaN(value)) value = 0;
95                 default:
96                     if(value === undefined || value === null)
97                         value = '';
98                     return value+'';
99             }
100         },
101
102         build : function(onload) {
103
104             if(this.widget) {
105                 // core widget provided for us, attach and move on
106                 if(this.parentNode) // may already be in the "right" place
107                     this.parentNode.appendChild(this.widget.domNode);
108                 return;
109             }
110             
111             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
112                 this.parentNode = document.createElement('div');
113
114             this.onload = onload;
115             if(this.widgetValue == null)
116                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
117
118             if(this.readOnly) {
119                 dojo.require('dijit.layout.ContentPane');
120                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
121                 this._tryLinkedDisplayField();
122
123             } else if(this.widgetClass) {
124                 dojo.require(this.widgetClass);
125                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
126
127             } else {
128
129                 switch(this.idlField.datatype) {
130                     
131                     case 'id':
132                         dojo.require('dijit.form.TextBox');
133                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
134                         break;
135
136                     case 'org_unit':
137                         this._buildOrgSelector();
138                         break;
139
140                     case 'money':
141                         dojo.require('dijit.form.CurrencyTextBox');
142                         this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
143                         break;
144
145                     case 'int':
146                         dojo.require('dijit.form.NumberTextBox');
147                         this.dijitArgs = dojo.mixin(this.dijitArgs || {}, {constraints:{places:0}});
148                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
149                         break;
150
151                     case 'float':
152                         dojo.require('dijit.form.NumberTextBox');
153                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
154                         break;
155
156                     case 'timestamp':
157                         dojo.require('dijit.form.DateTextBox');
158                         dojo.require('dojo.date.stamp');
159                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
160                         if(this.widgetValue != null) 
161                             this.widgetValue = dojo.date.stamp.fromISOString(this.widgetValue);
162                         break;
163
164                     case 'bool':
165                         dojo.require('dijit.form.CheckBox');
166                         this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
167                         this.widgetValue = openils.Util.isTrue(this.widgetValue);
168                         break;
169
170                     case 'link':
171                         if(this._buildLinkSelector()) break;
172
173                     default:
174                         dojo.require('dijit.form.TextBox');
175                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
176                 }
177             }
178
179             if(!this.async) this._widgetLoaded();
180             return this.widget;
181         },
182
183         // we want to display the value for our widget.  However, instead of displaying
184         // an ID, for exmaple, display the value for the 'selector' field on the object
185         // the ID points to
186         _tryLinkedDisplayField : function(noAsync) {
187
188             if(this.idlField.datatype == 'org_unit')
189                 return false; // we already handle org_units, no need to re-fetch
190
191             var linkInfo = this._getLinkSelector();
192             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
193                 return false;
194             var lclass = linkInfo.linkClass;
195
196             if(lclass == 'aou') {
197                 this.widgetValue = fieldmapper.aou.findOrgUnit(this.widgetValue).shortname();
198                 return;
199             }
200
201             // first try the store cache
202             var self = this;
203             if(this.cache[this.auth][lclass]) {
204                 var store = this.cache[this.auth][lclass];
205                 var query = {};
206                 query[linkInfo.vfield.name] = ''+this.widgetValue;
207                 store.fetch({query:query, onComplete:
208                     function(list) {
209                         self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
210                     }
211                 });
212                 return;
213             }
214
215             // then try the single object cache
216             if(this.cacheSingle[lclass] && this.cacheSingle[lclass][this.widgetValue]) {
217                 this.widgetValue = this.cacheSingle[lclass][this.widgetValue];
218                 return;
219             }
220
221             // if those fail, fetch the linked object
222             dojo.require('openils.PermaCrud');
223             this.async = true;
224             var self = this;
225             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
226                 async : !this.forceSync,
227                 oncomplete : function(r) {
228                     var item = openils.Util.readResponse(r);
229                     if(!self.cacheSingle[lclass])
230                         self.cacheSingle[lclass] = {};
231                     self.widgetValue = item[linkInfo.vfield.selector]();
232                     self.cacheSingle[lclass][self.widgetValue] = item;
233                     self.widget.startup();
234                     self._widgetLoaded();
235                 }
236             });
237         },
238
239         _getLinkSelector : function() {
240             var linkClass = this.idlField['class'];
241             if(this.idlField.reltype != 'has_a')  return false;
242             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
243             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
244
245             var vfield;
246             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
247
248             for(var f in rclassIdl.fields) {
249                 if(this.idlField.key == rclassIdl.fields[f].name) {
250                     vfield = rclassIdl.fields[f];
251                     break;
252                 }
253             }
254
255             if(!vfield) 
256                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
257
258             return {
259                 linkClass : linkClass,
260                 vfield : vfield
261             };
262         },
263
264         _buildLinkSelector : function() {
265             var selectorInfo = this._getLinkSelector();
266             if(!selectorInfo) return false;
267
268             var linkClass = selectorInfo.linkClass;
269             var vfield = selectorInfo.vfield;
270
271             this.async = true;
272
273             if(linkClass == 'pgt')
274                 return this._buildPermGrpSelector();
275             if(linkClass == 'aou')
276                 return this._buildOrgSelector();
277             if(linkClass == 'acpl')
278                 return this._buildCopyLocSelector();
279
280
281             dojo.require('openils.PermaCrud');
282             dojo.require('dojo.data.ItemFileReadStore');
283             dojo.require('dijit.form.FilteringSelect');
284
285             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
286             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
287             this.widget.valueAttr = vfield.name;
288
289             var self = this;
290             var oncomplete = function(list) {
291                 if(list) {
292                     self.widget.store = 
293                         new dojo.data.ItemFileReadStore({data:fieldmapper[linkClass].toStoreData(list)});
294                     self.cache[self.auth][linkClass] = self.widget.store;
295                 } else {
296                     self.widget.store = self.cache[self.auth][linkClass];
297                 }
298                 self.widget.startup();
299                 self._widgetLoaded();
300             };
301
302             if(this.cache[self.auth][linkClass]) {
303                 oncomplete();
304
305             } else {
306                 new openils.PermaCrud().retrieveAll(linkClass, {   
307                     async : !this.forceSync,
308                     oncomplete : function(r) {
309                         var list = openils.Util.readResponse(r, false, true);
310                         oncomplete(list);
311                     }
312                 });
313             }
314
315             return true;
316         },
317
318         /**
319          * For widgets that run asynchronously, provide a callback for finishing up
320          */
321         _widgetLoaded : function(value) {
322             if(this.readOnly) {
323                 this.baseWidgetValue(this.getDisplayString());
324             } else {
325                 this.baseWidgetValue(this.widgetValue);
326                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
327                     this.widget.attr('disabled', true); 
328                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
329                     this.widget.attr('disabled', true); 
330             }
331             if(this.onload)
332                 this.onload(this.widget, this);
333         },
334
335         _buildOrgSelector : function() {
336             dojo.require('fieldmapper.OrgUtils');
337             dojo.require('openils.widget.FilteringTreeSelect');
338             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
339             this.widget.searchAttr = 'shortname';
340             this.widget.labelAttr = 'shortname';
341             this.widget.parentField = 'parent_ou';
342             var user = new openils.User();
343
344             if(this.widgetValue == null) 
345                 this.widgetValue = user.user.ws_ou();
346             
347             // if we have a limit perm, find the relevent orgs (async)
348             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
349                 this.async = true;
350                 var self = this;
351                 user.getPermOrgList(this.orgLimitPerms, 
352                     function(orgList) {
353                         self.widget.tree = orgList;
354                         self.widget.startup();
355                         self._widgetLoaded();
356                     }
357                 );
358
359             } else {
360                 this.widget.tree = fieldmapper.aou.globalOrgTree;
361                 this.widget.startup();
362             }
363         },
364
365         _buildPermGrpSelector : function() {
366             dojo.require('openils.widget.FilteringTreeSelect');
367             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
368             this.widget.searchAttr = 'name';
369
370             if(this.cache.permGrpTree) {
371                 this.widget.tree = this.cache.permGrpTree;
372                 this.widget.startup();
373                 return true;
374             } 
375
376             var self = this;
377             this.async = true;
378             new openils.PermaCrud().retrieveAll('pgt', {
379                 async : !this.forceSync,
380                 oncomplete : function(r) {
381                     var list = openils.Util.readResponse(r, false, true);
382                     if(!list) return;
383                     var map = {};
384                     var root = null;
385                     for(var l in list)
386                         map[list[l].id()] = list[l];
387                     for(var l in list) {
388                         var node = list[l];
389                         var pnode = map[node.parent()];
390                         if(!pnode) {root = node; continue;}
391                         if(!pnode.children()) pnode.children([]);
392                         pnode.children().push(node);
393                     }
394                     self.widget.tree = self.cache.permGrpTree = root;
395                     self.widget.startup();
396                     self._widgetLoaded();
397                 }
398             });
399
400             return true;
401         },
402
403         _buildCopyLocSelector : function() {
404             dojo.require('dijit.form.FilteringSelect');
405             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
406             this.widget.searchAttr = this.widget.labalAttr = 'name';
407             this.widget.valueAttr = 'id';
408
409             if(this.cache.copyLocStore) {
410                 this.widget.store = this.cache.copyLocStore;
411                 this.widget.startup();
412                 this.async = false;
413                 return true;
414             } 
415
416             // my orgs
417             var ws_ou = openils.User.user.ws_ou();
418             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
419             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
420
421             var self = this;
422             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
423                 async : !this.forceSync,
424                 oncomplete : function(r) {
425                     var list = openils.Util.readResponse(r, false, true);
426                     if(!list) return;
427                     self.widget.store = 
428                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
429                     self.cache.copyLocStore = self.widget.store;
430                     self.widget.startup();
431                     self._widgetLoaded();
432                 }
433             });
434
435             return true;
436         }
437     });
438 }
439