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