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