]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
eec578d989f9b594a5e7866528940eee8f56ffe6
[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             if(linkClass == 'acqpro')
409                 return this._buildAutoCompleteSelector(linkClass, vfield.selector);
410
411
412             dojo.require('dojo.data.ItemFileReadStore');
413             dojo.require('dijit.form.FilteringSelect');
414
415             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
416             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
417             this.widget.valueAttr = vfield.name;
418             this.widget.attr('disabled', true);
419
420             var oncomplete = function(list) {
421                 self.widget.attr('disabled', false);
422
423                 if(self.labelFormat) 
424                     self.widget.labelAttr = '_label';
425
426                 if(self.searchFormat)
427                     self.widget.searchAttr = '_search';
428
429                 function formatString(item, formatList) {
430
431                     try {
432
433                         // formatList[1..*] are names of fields.  Pull the field
434                         // values from each object to determine the values for string substitution
435                         var values = [];
436                         var format = formatList[0];
437                         for(var i = 1; i< formatList.length; i++) 
438                             values.push(item[formatList[i]]);
439
440                         return dojo.string.substitute(format, values);
441
442                     } catch(E) {
443                         throw new Error(
444                             "openils.widget.AutoFieldWidget: Invalid formatList ["+formatList+"] : "+E);
445                     }
446
447                 }
448
449                 if(list) {
450                     var storeData = {data:fieldmapper[linkClass].toStoreData(list)};
451
452                     if(self.labelFormat) {
453                         dojo.forEach(storeData.data.items, 
454                             function(item) {
455                                 item._label = formatString(item, self.labelFormat);
456                             }
457                         );
458                     }
459
460                     if(self.searchFormat) {
461                         dojo.forEach(storeData.data.items, 
462                             function(item) {
463                                 item._search = formatString(item, self.searchFormat);
464                             }
465                         );
466                     }
467
468                     self.widget.store = new dojo.data.ItemFileReadStore(storeData);
469                     self.cache[self.auth].list[linkClass] = self.widget.store;
470
471                 } else {
472                     self.widget.store = self.cache[self.auth].list[linkClass];
473                 }
474
475                 self.widget.startup();
476                 self._widgetLoaded();
477             };
478
479             if(!this.noCache && this.cache[self.auth].list[linkClass]) {
480                 oncomplete();
481
482             } else {
483
484                 if(!this.dataLoader && openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass])
485                     this.dataLoader = openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass];
486
487                 if(this.dataLoader) {
488
489                     // caller provided an external function for retrieving the data
490                     this.dataLoader(linkClass, this.searchFilter, oncomplete);
491
492                 } else {
493
494                     var _cb = function(r) {
495                         oncomplete(openils.Util.readResponse(r, false, true));
496                     };
497
498                     if (this.searchFilter) {
499                         new openils.PermaCrud().search(linkClass, this.searchFilter, {
500                             async : !this.forceSync, oncomplete : _cb
501                         });
502                     } else {
503                         new openils.PermaCrud().retrieveAll(linkClass, {
504                             async : !this.forceSync, oncomplete : _cb
505                         });
506                     }
507                 }
508             }
509
510             return true;
511         },
512
513         /**
514          * For widgets that run asynchronously, provide a callback for finishing up
515          */
516         _widgetLoaded : function(value) {
517             
518             if(this.readOnly) {
519
520                 /* -------------------------------------------------------------
521                    when using widgets in a grid, the cell may dissapear, which 
522                    kills the underlying DOM node, which causes this to fail.
523                    For now, back out gracefully and let grid getters use
524                    getDisplayString() instead
525                   -------------------------------------------------------------*/
526                 try { 
527                     this.baseWidgetValue(this.getDisplayString());
528                 } catch (E) {};
529
530             } else {
531
532                 this.baseWidgetValue(this.widgetValue);
533                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && (!this.selfReference && !this.noDisablePkey))
534                     this.widget.attr('disabled', true); 
535                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
536                     this.widget.attr('disabled', true); 
537             }
538             if(this.onload)
539                 this.onload(this.widget, this);
540
541             if(!this.readOnly && (this.idlField.required || (this.dijitArgs && this.dijitArgs.required))) {
542                 // a required dijit is not given any styling to indicate the value
543                 // is invalid until the user has focused the widget then left it with
544                 // invalid data.  This change tells dojo to pretend this focusing has 
545                 // already happened so we can style required widgets during page render.
546                 this.widget._hasBeenBlurred = true;
547                 this.widget.validate();
548             }
549         },
550
551         _buildOrgSelector : function() {
552             dojo.require('fieldmapper.OrgUtils');
553             dojo.require('openils.widget.FilteringTreeSelect');
554             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
555             this.widget.searchAttr = 'shortname';
556             this.widget.labelAttr = 'shortname';
557             this.widget.parentField = 'parent_ou';
558             var user = new openils.User();
559
560             if(this.widgetValue == null && this.orgDefaultsToWs) 
561                 this.widgetValue = user.user.ws_ou();
562             
563             // if we have a limit perm, find the relevent orgs (async)
564             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
565                 this.async = true;
566                 var self = this;
567                 user.getPermOrgList(this.orgLimitPerms, 
568                     function(orgList) {
569                         self.widget.tree = orgList;
570                         self.widget.startup();
571                         self._widgetLoaded();
572                     }
573                 );
574
575             } else {
576                 this.widget.tree = fieldmapper.aou.globalOrgTree;
577                 this.widget.startup();
578             }
579
580             return true;
581         },
582
583         _buildPermGrpSelector : function() {
584             dojo.require('openils.widget.FilteringTreeSelect');
585             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
586             this.widget.disableQuery = this.disableQuery;
587             this.widget.searchAttr = 'name';
588
589             if(this.cache.permGrpTree) {
590                 this.widget.tree = this.cache.permGrpTree;
591                 this.widget.startup();
592                 this._widgetLoaded();
593                 return true;
594             } 
595
596             var self = this;
597             this.async = true;
598             new openils.PermaCrud().retrieveAll('pgt', {
599                 async : !this.forceSync,
600                 oncomplete : function(r) {
601                     var list = openils.Util.readResponse(r, false, true);
602                     if(!list) return;
603                     var map = {};
604                     var root = null;
605                     for(var l in list)
606                         map[list[l].id()] = list[l];
607                     for(var l in list) {
608                         var node = list[l];
609                         var pnode = map[node.parent()];
610                         if(!pnode) {root = node; continue;}
611                         if(!pnode.children()) pnode.children([]);
612                         pnode.children().push(node);
613                     }
614                     self.widget.tree = self.cache.permGrpTree = root;
615                     self.widget.startup();
616                     self._widgetLoaded();
617                 }
618             });
619
620             return true;
621         },
622
623         _buildCopyLocSelector : function() {
624             dojo.require('dijit.form.FilteringSelect');
625             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
626             this.widget.searchAttr = this.widget.labalAttr = 'name';
627             this.widget.valueAttr = 'id';
628
629             if(this.cache.copyLocStore) {
630                 this.widget.store = this.cache.copyLocStore;
631                 this.widget.startup();
632                 this.async = false;
633                 return true;
634             } 
635
636             // my orgs
637             var ws_ou = openils.User.user.ws_ou();
638             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
639             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
640
641             var self = this;
642             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
643                 async : !this.forceSync,
644                 oncomplete : function(r) {
645                     var list = openils.Util.readResponse(r, false, true);
646                     if(!list) return;
647                     self.widget.store = 
648                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
649                     self.cache.copyLocStore = self.widget.store;
650                     self.widget.startup();
651                     self._widgetLoaded();
652                 }
653             });
654
655             return true;
656         },
657
658         _buildAutoCompleteSelector : function(linkClass, searchAttr) {
659             dojo.require("openils.widget.PCrudAutocompleteBox");
660             dojo.mixin(this.dijitArgs, {
661                 fmclass : linkClass,
662                 searchAttr : searchAttr,
663             });
664             this.widget = new openils.widget.PCrudAutocompleteBox(this.dijitArgs, this.parentNode);
665             this._widgetLoaded();
666             return true;
667         }
668     });
669
670     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
671     openils.widget.AutoFieldWidget.cache = {};
672     openils.widget.AutoFieldWidget.defaultLinkedDataLoader = {};
673
674     /* Custom provider-as-link-class fetcher.  Fitler is ignored.  
675      * All viewable providers are retrieved.
676      */
677     openils.widget.AutoFieldWidget.defaultLinkedDataLoader.acqpro = 
678             function(linkClass, fitler, oncomplete) { 
679
680         fieldmapper.standardRequest(
681             ['open-ils.acq', 'open-ils.acq.provider.org.retrieve'],
682             {
683                 async : true,
684                 params : [openils.User.authtoken],
685                 oncomplete : function(r) {
686                     var resp;
687                     var list = [];
688                     while(resp = r.recv()) {
689                         var pro = resp.content();
690                         if(pro) list.push(pro);
691                     }
692                     oncomplete(list);
693                 }
694             }
695         );
696     }
697 }
698