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