]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
AutoFieldWidget linked class autoloaders, etc.
[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          *  orgDefaultsToWs -- If this is an org unit field and the widget has no value,
26          *      set the value equal to the users's workstation org unit.  Othwerwise, leave it null
27          *  selfReference -- The primary purpose of an AutoFieldWidget is to render the value
28          *      or widget for a field on an object (that may or may not link to another object).
29          *      selfReference allows you to sidestep the indirection and create a selector widget
30          *      based purely on an fmClass.  To get a dropdown of all of the 'abc'
31          *      objects, pass in {selfReference : true, fmClass : 'abc'}.  
32          *  labelFormat -- For widgets that are displayed as remote object filtering selects,
33          *      this provides a mechanism for overriding the label format in the filtering select.
34          *      It must be an array, whose first value is a format string, compliant with
35          *      dojo.string.substitute.  The remaining array items are the arguments to the format
36          *      represented as field names on the remote linked object.
37          *      E.g.
38          *      labelFormat : [ '${0} (${1})', 'obj_field_1', 'obj_field_2' ]
39          *      Note: this does not control the final display value.  Only values in the drop-down.
40          *      See searchFormat for controlling the display value
41          *  searchFormat -- This format controls the structure of the search attribute which
42          *      controls the text used during type-ahead searching and the displayed value in 
43          *      the filtering select.  See labelFormat for the structure.  
44          *  dataLoader : Bypass the default PermaCrud linked data fetcher and use this function instead.
45          *      Function arguments are (link class name, search filter, callback)
46          *      The fetched objects should be passed to the callback as an array
47          *  disableQuery : dojo.data query passed to FilteringTreeSelect-based widgets to disable
48          *      (but leave visible) certain options.  
49          */
50         constructor : function(args) {
51             for(var k in args)
52                 this[k] = args[k];
53
54             if (!this.dijitArgs) {
55                 this.dijitArgs = {};
56             }
57             this.dijitArgs['scrollOnFocus'] = false;
58
59
60             // find the field description in the IDL if not provided
61             if(this.fmObject) 
62                 this.fmClass = this.fmObject.classname;
63             this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
64
65             if(this.fmClass && !this.fmIDL) {
66                 fieldmapper.IDL.load([this.fmClass]);
67                 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
68             }
69
70             this.suppressLinkedFields = args.suppressLinkedFields || [];
71
72             if(this.selfReference) {
73                 this.fmField = fieldmapper.IDL.fmclasses[this.fmClass].pkey;
74                 
75                 // create a mock-up of the idlField object.  
76                 this.idlField = {
77                     datatype : 'link',
78                     'class' : this.fmClass,
79                     reltype : 'has_a',
80                     key : this.fmField,
81                     name : this.fmField
82                 };
83
84             } else {
85
86                 if(!this.idlField) {
87                     this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
88                     var fields = this.fmIDL.fields;
89                     for(var f in fields) 
90                         if(fields[f].name == this.fmField)
91                             this.idlField = fields[f];
92                 }
93             }
94
95             if(!this.idlField) 
96                 throw new Error("AutoFieldWidget could not determine which " +
97                     "field to render.  We need more information. fmClass=" + 
98                     this.fmClass + ' fmField=' + this.fmField + ' fmObject=' + js2JSON(this.fmObject));
99
100             this.auth = openils.User.authtoken;
101             this.cache = openils.widget.AutoFieldWidget.cache;
102             this.cache[this.auth] = this.cache[this.auth] || {};
103             this.cache[this.auth].single = this.cache[this.auth].single || {};
104             this.cache[this.auth].list = this.cache[this.auth].list || {};
105         },
106
107         /**
108          * Turn the widget-stored value into a value oils understands
109          */
110         getFormattedValue : function() {
111             var value = this.baseWidgetValue();
112             switch(this.idlField.datatype) {
113                 case 'bool':
114                     switch(value) {
115                         case 'true': return 't';
116                         case 'on': return 't';
117                         case 'false' : return 'f';
118                         case 'unset' : return null;
119                         case true : return 't';
120                         default: return 'f';
121                     }
122                 case 'timestamp':
123                     if(!value) return null;
124                     return dojo.date.stamp.toISOString(value);
125                 case 'int':
126                 case 'float':
127                 case 'money':
128                     if(isNaN(value)) value = null;
129                 default:
130                     return (value === '') ? null : value;
131             }
132         },
133
134         baseWidgetValue : function(value) {
135             var attr = (this.readOnly) ? 'content' : 'value';
136             if(arguments.length) this.widget.attr(attr, value);
137             return this.widget.attr(attr);
138         },
139         
140         /**
141          * Turn the widget-stored value into something visually suitable
142          */
143         getDisplayString : function() {
144             var value = this.widgetValue;
145             switch(this.idlField.datatype) {
146                 case 'bool':
147                     switch(value) {
148                         case 't': 
149                         case 'true': 
150                             return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
151                         case 'f' : 
152                         case 'false' : 
153                             return openils.widget.AutoFieldWidget.localeStrings.FALSE;
154                         case  null :
155                         case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
156                         case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
157                         default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
158                     }
159                 case 'timestamp':
160                     if (!value) return '';
161                     return openils.Util.timeStamp(
162                         value, {"formatLength": "short"}
163                     );
164                 case 'org_unit':
165                     if(value === null || value === undefined) return '';
166                     return fieldmapper.aou.findOrgUnit(value).shortname();
167                 case 'int':
168                 case 'float':
169                     if(isNaN(value)) value = 0;
170                 default:
171                     if(value === undefined || value === null)
172                         value = '';
173                     return value+'';
174             }
175         },
176
177         build : function(onload) {
178
179             if(this.widgetValue == null)
180                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
181
182             if(this.widget) {
183                 // core widget provided for us, attach and move on
184                 if(this.parentNode) // may already be in the "right" place
185                     this.parentNode.appendChild(this.widget.domNode);
186                 if (this.shove) {
187                     if (this.shove.mode == "update")
188                         this.widget.attr("value", this.widgetValue);
189                     else
190                         this.widgetValue = this.shove.create;
191                     this._widgetLoaded();
192                 } else if (this.widget.attr("value") == null) {
193                     this._widgetLoaded();
194                 }
195                 return;
196             }
197             
198             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
199                 this.parentNode = dojo.create('div');
200
201             this.onload = onload;
202
203             if(this.readOnly) {
204                 dojo.require('dijit.layout.ContentPane');
205                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
206                 if(this.widgetValue !== null)
207                     this._tryLinkedDisplayField();
208
209             } else if(this.widgetClass) {
210                 dojo.require(this.widgetClass);
211                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
212
213             } else {
214
215                 switch(this.idlField.datatype) {
216                     
217                     case 'id':
218                         dojo.require('dijit.form.TextBox');
219                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
220                         break;
221
222                     case 'org_unit':
223                         this._buildOrgSelector();
224                         break;
225
226                     case 'money':
227                         dojo.require('dijit.form.CurrencyTextBox');
228                         this.widget = new dijit.form.CurrencyTextBox(this.dijitArgs, this.parentNode);
229                         break;
230
231                     case 'int':
232                         dojo.require('dijit.form.NumberTextBox');
233                         this.dijitArgs = dojo.mixin({constraints:{places:0}}, this.dijitArgs || {});
234                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
235                         break;
236
237                     case 'float':
238                         dojo.require('dijit.form.NumberTextBox');
239                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
240                         break;
241
242                     case 'timestamp':
243                         dojo.require('dijit.form.DateTextBox');
244                         dojo.require('dojo.date.stamp');
245                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
246                         if (this.widgetValue != null) {
247                             this.widgetValue = openils.Util.timeStampAsDateObj(
248                                 this.widgetValue
249                             );
250                         }
251                         break;
252
253                     case 'bool':
254                         if(this.ternary) {
255                             dojo.require('dijit.form.FilteringSelect');
256                             var store = new dojo.data.ItemFileReadStore({
257                                 data:{
258                                     identifier : 'value',
259                                     items:[
260                                         {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
261                                         {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
262                                         {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
263                                     ]
264                                 }
265                             });
266                             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
267                             this.widget.searchAttr = this.widget.labelAttr = 'label';
268                             this.widget.valueAttr = 'value';
269                             this.widget.store = store;
270                             this.widget.startup();
271                             this.widgetValue = (this.widgetValue === null) ? 'unset' : 
272                                 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
273                         } else {
274                             dojo.require('dijit.form.CheckBox');
275                             this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
276                             this.widgetValue = openils.Util.isTrue(this.widgetValue);
277                         }
278                         break;
279
280                     case 'link':
281                         if(this._buildLinkSelector()) break;
282
283                     default:
284                         if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
285                             dojo.require('dijit.form.ValidationTextBox');
286                             this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
287                         } else {
288                             dojo.require('dijit.form.TextBox');
289                             this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
290                         }
291                 }
292             }
293
294             if(!this.async) this._widgetLoaded();
295             return this.widget;
296         },
297
298         // we want to display the value for our widget.  However, instead of displaying
299         // an ID, for exmaple, display the value for the 'selector' field on the object
300         // the ID points to
301         _tryLinkedDisplayField : function(noAsync) {
302
303             if(this.idlField.datatype == 'org_unit')
304                 return false; // we already handle org_units, no need to re-fetch
305
306             // user opted to bypass fetching this linked data
307             if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
308                 return false;
309
310             var linkInfo = this._getLinkSelector();
311             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
312                 return false;
313             var lclass = linkInfo.linkClass;
314
315             if(lclass == 'aou') 
316                 return false;
317
318             // first try the store cache
319             var self = this;
320             if(this.cache[this.auth].list[lclass]) {
321                 var store = this.cache[this.auth].list[lclass];
322                 var query = {};
323                 query[linkInfo.vfield.name] = ''+this.widgetValue;
324                 var found = false;
325                 store.fetch({query:query, onComplete:
326                     function(list) {
327                         if(list[0]) {
328                             self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
329                             found = true;
330                         }
331                     }
332                 });
333
334                 if(found) return;
335             }
336
337             // then try the single object cache
338             if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
339                 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
340                 return;
341             }
342
343             console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
344
345             // if those fail, fetch the linked object
346             this.async = true;
347             var self = this;
348             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
349                 async : !this.forceSync,
350                 oncomplete : function(r) {
351                     var item = openils.Util.readResponse(r);
352                     var newvalue = item[linkInfo.vfield.selector]();
353
354                     if(!self.cache[self.auth].single[lclass])
355                         self.cache[self.auth].single[lclass] = {};
356                     self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
357
358                     self.widgetValue = newvalue;
359                     self.widget.startup();
360                     self._widgetLoaded();
361                 }
362             });
363         },
364
365         _getLinkSelector : function() {
366             var linkClass = this.idlField['class'];
367             if(this.idlField.reltype != 'has_a')  return false;
368             if(!fieldmapper.IDL.fmclasses[linkClass]) // class neglected by AutoIDL
369                 fieldmapper.IDL.load([linkClass]);
370             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
371             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
372
373             var vfield;
374             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
375
376             for(var f in rclassIdl.fields) {
377                 if(this.idlField.key == rclassIdl.fields[f].name) {
378                     vfield = rclassIdl.fields[f];
379                     break;
380                 }
381             }
382
383             if(!vfield) 
384                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
385
386             return {
387                 linkClass : linkClass,
388                 vfield : vfield
389             };
390         },
391
392         _buildLinkSelector : function() {
393             var self = this;
394             var selectorInfo = this._getLinkSelector();
395             if(!selectorInfo) return false;
396
397             var linkClass = selectorInfo.linkClass;
398             var vfield = selectorInfo.vfield;
399
400             this.async = true;
401
402             if(linkClass == 'pgt')
403                 return this._buildPermGrpSelector();
404             if(linkClass == 'aou')
405                 return this._buildOrgSelector();
406             if(linkClass == 'acpl')
407                 return this._buildCopyLocSelector();
408
409
410             dojo.require('dojo.data.ItemFileReadStore');
411             dojo.require('dijit.form.FilteringSelect');
412
413             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
414             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
415             this.widget.valueAttr = vfield.name;
416             this.widget.attr('disabled', true);
417
418             var oncomplete = function(list) {
419                 self.widget.attr('disabled', false);
420
421                 if(self.labelFormat) 
422                     self.widget.labelAttr = '_label';
423
424                 if(self.searchFormat)
425                     self.widget.searchAttr = '_search';
426
427                 function formatString(item, formatList) {
428
429                     try {
430
431                         // formatList[1..*] are names of fields.  Pull the field
432                         // values from each object to determine the values for string substitution
433                         var values = [];
434                         var format = formatList[0];
435                         for(var i = 1; i< formatList.length; i++) 
436                             values.push(item[formatList[i]]);
437
438                         return dojo.string.substitute(format, values);
439
440                     } catch(E) {
441                         throw new Error(
442                             "openils.widget.AutoFieldWidget: Invalid formatList ["+formatList+"] : "+E);
443                     }
444
445                 }
446
447                 if(list) {
448                     var storeData = {data:fieldmapper[linkClass].toStoreData(list)};
449
450                     if(self.labelFormat) {
451                         dojo.forEach(storeData.data.items, 
452                             function(item) {
453                                 item._label = formatString(item, self.labelFormat);
454                             }
455                         );
456                     }
457
458                     if(self.searchFormat) {
459                         dojo.forEach(storeData.data.items, 
460                             function(item) {
461                                 item._search = formatString(item, self.searchFormat);
462                             }
463                         );
464                     }
465
466                     self.widget.store = new dojo.data.ItemFileReadStore(storeData);
467                     self.cache[self.auth].list[linkClass] = self.widget.store;
468
469                 } else {
470                     self.widget.store = self.cache[self.auth].list[linkClass];
471                 }
472
473                 self.widget.startup();
474                 self._widgetLoaded();
475             };
476
477             if(!this.noCache && this.cache[self.auth].list[linkClass]) {
478                 oncomplete();
479
480             } else {
481
482                 if(!this.dataLoader && openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass])
483                     this.dataLoader = openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass];
484
485                 if(this.dataLoader) {
486
487                     // caller provided an external function for retrieving the data
488                     this.dataLoader(linkClass, this.searchFilter, oncomplete);
489
490                 } else {
491
492                     var _cb = function(r) {
493                         oncomplete(openils.Util.readResponse(r, false, true));
494                     };
495
496                     if (this.searchFilter) {
497                         new openils.PermaCrud().search(linkClass, this.searchFilter, {
498                             async : !this.forceSync, oncomplete : _cb
499                         });
500                     } else {
501                         new openils.PermaCrud().retrieveAll(linkClass, {
502                             async : !this.forceSync, oncomplete : _cb
503                         });
504                     }
505                 }
506             }
507
508             return true;
509         },
510
511         /**
512          * For widgets that run asynchronously, provide a callback for finishing up
513          */
514         _widgetLoaded : function(value) {
515             
516             if(this.readOnly) {
517
518                 /* -------------------------------------------------------------
519                    when using widgets in a grid, the cell may dissapear, which 
520                    kills the underlying DOM node, which causes this to fail.
521                    For now, back out gracefully and let grid getters use
522                    getDisplayString() instead
523                   -------------------------------------------------------------*/
524                 try { 
525                     this.baseWidgetValue(this.getDisplayString());
526                 } catch (E) {};
527
528             } else {
529
530                 this.baseWidgetValue(this.widgetValue);
531                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && (!this.selfReference && !this.noDisablePkey))
532                     this.widget.attr('disabled', true); 
533                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
534                     this.widget.attr('disabled', true); 
535             }
536             if(this.onload)
537                 this.onload(this.widget, this);
538
539             if(!this.readOnly && (this.idlField.required || (this.dijitArgs && this.dijitArgs.required))) {
540                 // a required dijit is not given any styling to indicate the value
541                 // is invalid until the user has focused the widget then left it with
542                 // invalid data.  This change tells dojo to pretend this focusing has 
543                 // already happened so we can style required widgets during page render.
544                 this.widget._hasBeenBlurred = true;
545                 this.widget.validate();
546             }
547         },
548
549         _buildOrgSelector : function() {
550             dojo.require('fieldmapper.OrgUtils');
551             dojo.require('openils.widget.FilteringTreeSelect');
552             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
553             this.widget.searchAttr = 'shortname';
554             this.widget.labelAttr = 'shortname';
555             this.widget.parentField = 'parent_ou';
556             var user = new openils.User();
557
558             if(this.widgetValue == null && this.orgDefaultsToWs) 
559                 this.widgetValue = user.user.ws_ou();
560             
561             // if we have a limit perm, find the relevent orgs (async)
562             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
563                 this.async = true;
564                 var self = this;
565                 user.getPermOrgList(this.orgLimitPerms, 
566                     function(orgList) {
567                         self.widget.tree = orgList;
568                         self.widget.startup();
569                         self._widgetLoaded();
570                     }
571                 );
572
573             } else {
574                 this.widget.tree = fieldmapper.aou.globalOrgTree;
575                 this.widget.startup();
576             }
577
578             return true;
579         },
580
581         _buildPermGrpSelector : function() {
582             dojo.require('openils.widget.FilteringTreeSelect');
583             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
584             this.widget.disableQuery = this.disableQuery;
585             this.widget.searchAttr = 'name';
586
587             if(this.cache.permGrpTree) {
588                 this.widget.tree = this.cache.permGrpTree;
589                 this.widget.startup();
590                 this._widgetLoaded();
591                 return true;
592             } 
593
594             var self = this;
595             this.async = true;
596             new openils.PermaCrud().retrieveAll('pgt', {
597                 async : !this.forceSync,
598                 oncomplete : function(r) {
599                     var list = openils.Util.readResponse(r, false, true);
600                     if(!list) return;
601                     var map = {};
602                     var root = null;
603                     for(var l in list)
604                         map[list[l].id()] = list[l];
605                     for(var l in list) {
606                         var node = list[l];
607                         var pnode = map[node.parent()];
608                         if(!pnode) {root = node; continue;}
609                         if(!pnode.children()) pnode.children([]);
610                         pnode.children().push(node);
611                     }
612                     self.widget.tree = self.cache.permGrpTree = root;
613                     self.widget.startup();
614                     self._widgetLoaded();
615                 }
616             });
617
618             return true;
619         },
620
621         _buildCopyLocSelector : function() {
622             dojo.require('dijit.form.FilteringSelect');
623             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
624             this.widget.searchAttr = this.widget.labalAttr = 'name';
625             this.widget.valueAttr = 'id';
626
627             if(this.cache.copyLocStore) {
628                 this.widget.store = this.cache.copyLocStore;
629                 this.widget.startup();
630                 this.async = false;
631                 return true;
632             } 
633
634             // my orgs
635             var ws_ou = openils.User.user.ws_ou();
636             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
637             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
638
639             var self = this;
640             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
641                 async : !this.forceSync,
642                 oncomplete : function(r) {
643                     var list = openils.Util.readResponse(r, false, true);
644                     if(!list) return;
645                     self.widget.store = 
646                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
647                     self.cache.copyLocStore = self.widget.store;
648                     self.widget.startup();
649                     self._widgetLoaded();
650                 }
651             });
652
653             return true;
654         }
655     });
656
657     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
658     openils.widget.AutoFieldWidget.cache = {};
659     openils.widget.AutoFieldWidget.defaultLinkedDataLoader = {};
660
661     /* Custom provider-as-link-class fetcher.  Fitler is ignored.  
662      * All viewable providers are retrieved.
663      */
664     openils.widget.AutoFieldWidget.defaultLinkedDataLoader.acqpro = 
665             function(linkClass, fitler, oncomplete) { 
666
667         fieldmapper.standardRequest(
668             ['open-ils.acq', 'open-ils.acq.provider.org.retrieve'],
669             {
670                 async : true,
671                 params : [openils.User.authtoken],
672                 oncomplete : function(r) {
673                     var resp;
674                     var list = [];
675                     while(resp = r.recv()) {
676                         var pro = resp.content();
677                         if(pro) list.push(pro);
678                     }
679                     oncomplete(list);
680                 }
681             }
682         );
683     }
684 }
685