]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
Serials: In the holding code mini wizard of the alt serials controls, pre-
[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                         if (this.idlField.datatype == "timestamp")
189                             this.widgetValue = openils.Util.timeStampAsDateObj(
190                                 this.widgetValue
191                             );
192                     } else {
193                         this.widgetValue = this.shove.create;
194                     }
195                     this._widgetLoaded();
196                 } else if (this.widget.attr("value") == null) {
197                     this._widgetLoaded();
198                 }
199                 return;
200             }
201             
202             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
203                 this.parentNode = dojo.create('div');
204
205             this.onload = onload;
206
207             if(this.readOnly) {
208                 dojo.require('dijit.layout.ContentPane');
209                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
210                 if(this.widgetValue !== null)
211                     this._tryLinkedDisplayField();
212
213             } else if(this.widgetClass) {
214                 dojo.require(this.widgetClass);
215                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
216
217             } else {
218
219                 switch(this.idlField.datatype) {
220                     
221                     case 'id':
222                         dojo.require('dijit.form.TextBox');
223                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
224                         break;
225
226                     case 'org_unit':
227                         this._buildOrgSelector();
228                         break;
229
230                     case 'money':
231                         // dojo.require('dijit.form.CurrencyTextBox');
232                         // the CurrencyTextBox dijit is broken in Dojo 1.3; upon upgrading
233                         // to Dojo 1.5 or later, should re-evaluate work-around use of dijit.form.NumberTextBox.
234                         // See https://bugs.launchpad.net/evergreen/+bug/702117
235                         dojo.require('dijit.form.NumberTextBox');
236                         this.dijitArgs = dojo.mixin({constraints:{places:'0,2'}}, this.dijitArgs || {});
237                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
238                         break;
239
240                     case 'int':
241                         dojo.require('dijit.form.NumberTextBox');
242                         this.dijitArgs = dojo.mixin({constraints:{places:0}}, this.dijitArgs || {});
243                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
244                         break;
245
246                     case 'float':
247                         dojo.require('dijit.form.NumberTextBox');
248                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
249                         break;
250
251                     case 'timestamp':
252                         dojo.require('dijit.form.DateTextBox');
253                         dojo.require('dojo.date.stamp');
254                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
255                         if (this.widgetValue != null) {
256                             this.widgetValue = openils.Util.timeStampAsDateObj(
257                                 this.widgetValue
258                             );
259                         }
260                         break;
261
262                     case 'bool':
263                         if(this.ternary) {
264                             dojo.require('dijit.form.FilteringSelect');
265                             var store = new dojo.data.ItemFileReadStore({
266                                 data:{
267                                     identifier : 'value',
268                                     items:[
269                                         {label : openils.widget.AutoFieldWidget.localeStrings.UNSET, value : 'unset'},
270                                         {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
271                                         {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
272                                     ]
273                                 }
274                             });
275                             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
276                             this.widget.searchAttr = this.widget.labelAttr = 'label';
277                             this.widget.valueAttr = 'value';
278                             this.widget.store = store;
279                             this.widget.startup();
280                             this.widgetValue = (this.widgetValue === null) ? 'unset' : 
281                                 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
282                         } else {
283                             dojo.require('dijit.form.CheckBox');
284                             this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
285                             this.widgetValue = openils.Util.isTrue(this.widgetValue);
286                         }
287                         break;
288
289                     case 'link':
290                         if(this._buildLinkSelector()) break;
291
292                     default:
293                         if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
294                             dojo.require('dijit.form.ValidationTextBox');
295                             this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
296                         } else {
297                             dojo.require('dijit.form.TextBox');
298                             this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
299                         }
300                 }
301             }
302
303             if(!this.async) this._widgetLoaded();
304             return this.widget;
305         },
306
307         // we want to display the value for our widget.  However, instead of displaying
308         // an ID, for exmaple, display the value for the 'selector' field on the object
309         // the ID points to
310         _tryLinkedDisplayField : function(noAsync) {
311
312             if(this.idlField.datatype == 'org_unit')
313                 return false; // we already handle org_units, no need to re-fetch
314
315             // user opted to bypass fetching this linked data
316             if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
317                 return false;
318
319             var linkInfo = this._getLinkSelector();
320             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
321                 return false;
322             var lclass = linkInfo.linkClass;
323
324             if(lclass == 'aou') 
325                 return false;
326
327             // first try the store cache
328             var self = this;
329             if(this.cache[this.auth].list[lclass]) {
330                 var store = this.cache[this.auth].list[lclass];
331                 var query = {};
332                 query[linkInfo.vfield.name] = ''+this.widgetValue;
333                 var found = false;
334                 store.fetch({query:query, onComplete:
335                     function(list) {
336                         if(list[0]) {
337                             self.widgetValue = store.getValue(list[0], linkInfo.vfield.selector);
338                             found = true;
339                         }
340                     }
341                 });
342
343                 if(found) return;
344             }
345
346             // then try the single object cache
347             if(this.cache[this.auth].single[lclass] && this.cache[this.auth].single[lclass][this.widgetValue]) {
348                 this.widgetValue = this.cache[this.auth].single[lclass][this.widgetValue];
349                 return;
350             }
351
352             console.log("Fetching sync object " + lclass + " : " + this.widgetValue);
353
354             // if those fail, fetch the linked object
355             this.async = true;
356             var self = this;
357             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
358                 async : !this.forceSync,
359                 oncomplete : function(r) {
360                     var item = openils.Util.readResponse(r);
361                     var newvalue = item[linkInfo.vfield.selector]();
362
363                     if(!self.cache[self.auth].single[lclass])
364                         self.cache[self.auth].single[lclass] = {};
365                     self.cache[self.auth].single[lclass][self.widgetValue] = newvalue;
366
367                     self.widgetValue = newvalue;
368                     self.widget.startup();
369                     self._widgetLoaded();
370                 }
371             });
372         },
373
374         _getLinkSelector : function() {
375             var linkClass = this.idlField['class'];
376             if(this.idlField.reltype != 'has_a')  return false;
377             if(!fieldmapper.IDL.fmclasses[linkClass]) // class neglected by AutoIDL
378                 fieldmapper.IDL.load([linkClass]);
379             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
380             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
381
382             var vfield;
383             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
384
385             for(var f in rclassIdl.fields) {
386                 if(this.idlField.key == rclassIdl.fields[f].name) {
387                     vfield = rclassIdl.fields[f];
388                     break;
389                 }
390             }
391
392             if(!vfield) 
393                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
394
395             return {
396                 linkClass : linkClass,
397                 vfield : vfield
398             };
399         },
400
401         _buildLinkSelector : function() {
402             var self = this;
403             var selectorInfo = this._getLinkSelector();
404             if(!selectorInfo) return false;
405
406             var linkClass = selectorInfo.linkClass;
407             var vfield = selectorInfo.vfield;
408
409             this.async = true;
410
411             if(linkClass == 'pgt')
412                 return this._buildPermGrpSelector();
413             if(linkClass == 'aou')
414                 return this._buildOrgSelector();
415             if(linkClass == 'acpl')
416                 return this._buildCopyLocSelector();
417             if(linkClass == 'acqpro')
418                 return this._buildAutoCompleteSelector(linkClass, vfield.selector);
419
420
421             dojo.require('dojo.data.ItemFileReadStore');
422             dojo.require('dijit.form.FilteringSelect');
423
424             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
425             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
426             this.widget.valueAttr = vfield.name;
427             this.widget.attr('disabled', true);
428
429             var oncomplete = function(list) {
430                 self.widget.attr('disabled', false);
431
432                 if(self.labelFormat) 
433                     self.widget.labelAttr = '_label';
434
435                 if(self.searchFormat)
436                     self.widget.searchAttr = '_search';
437
438                 function formatString(item, formatList) {
439
440                     try {
441
442                         // formatList[1..*] are names of fields.  Pull the field
443                         // values from each object to determine the values for string substitution
444                         var values = [];
445                         var format = formatList[0];
446                         for(var i = 1; i< formatList.length; i++) 
447                             values.push(item[formatList[i]]);
448
449                         return dojo.string.substitute(format, values);
450
451                     } catch(E) {
452                         throw new Error(
453                             "openils.widget.AutoFieldWidget: Invalid formatList ["+formatList+"] : "+E);
454                     }
455
456                 }
457
458                 if(list) {
459                     var storeData = {data:fieldmapper[linkClass].toStoreData(list)};
460
461                     if(self.labelFormat) {
462                         dojo.forEach(storeData.data.items, 
463                             function(item) {
464                                 item._label = formatString(item, self.labelFormat);
465                             }
466                         );
467                     }
468
469                     if(self.searchFormat) {
470                         dojo.forEach(storeData.data.items, 
471                             function(item) {
472                                 item._search = formatString(item, self.searchFormat);
473                             }
474                         );
475                     }
476
477                     self.widget.store = new dojo.data.ItemFileReadStore(storeData);
478                     self.cache[self.auth].list[linkClass] = self.widget.store;
479
480                 } else {
481                     self.widget.store = self.cache[self.auth].list[linkClass];
482                 }
483
484                 self.widget.startup();
485                 self._widgetLoaded();
486             };
487
488             if(!this.noCache && this.cache[self.auth].list[linkClass]) {
489                 oncomplete();
490
491             } else {
492
493                 if(!this.dataLoader && openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass])
494                     this.dataLoader = openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass];
495
496                 if(this.dataLoader) {
497
498                     // caller provided an external function for retrieving the data
499                     this.dataLoader(linkClass, this.searchFilter, oncomplete);
500
501                 } else {
502
503                     var _cb = function(r) {
504                         oncomplete(openils.Util.readResponse(r, false, true));
505                     };
506
507                     if (this.searchFilter) {
508                         new openils.PermaCrud().search(linkClass, this.searchFilter, {
509                             async : !this.forceSync, oncomplete : _cb
510                         });
511                     } else {
512                         new openils.PermaCrud().retrieveAll(linkClass, {
513                             async : !this.forceSync, oncomplete : _cb
514                         });
515                     }
516                 }
517             }
518
519             return true;
520         },
521
522         /**
523          * For widgets that run asynchronously, provide a callback for finishing up
524          */
525         _widgetLoaded : function(value) {
526             
527             if(this.readOnly) {
528
529                 /* -------------------------------------------------------------
530                    when using widgets in a grid, the cell may dissapear, which 
531                    kills the underlying DOM node, which causes this to fail.
532                    For now, back out gracefully and let grid getters use
533                    getDisplayString() instead
534                   -------------------------------------------------------------*/
535                 try { 
536                     this.baseWidgetValue(this.getDisplayString());
537                 } catch (E) {};
538
539             } else {
540
541                 this.baseWidgetValue(this.widgetValue);
542                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && (!this.selfReference && !this.noDisablePkey))
543                     this.widget.attr('disabled', true); 
544                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
545                     this.widget.attr('disabled', true); 
546             }
547             if(this.onload)
548                 this.onload(this.widget, this);
549
550             if(!this.readOnly && (this.idlField.required || (this.dijitArgs && this.dijitArgs.required))) {
551                 // a required dijit is not given any styling to indicate the value
552                 // is invalid until the user has focused the widget then left it with
553                 // invalid data.  This change tells dojo to pretend this focusing has 
554                 // already happened so we can style required widgets during page render.
555                 this.widget._hasBeenBlurred = true;
556                 this.widget.validate();
557             }
558         },
559
560         _buildOrgSelector : function() {
561             dojo.require('fieldmapper.OrgUtils');
562             dojo.require('openils.widget.FilteringTreeSelect');
563             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
564             this.widget.searchAttr = 'shortname';
565             this.widget.labelAttr = 'shortname';
566             this.widget.parentField = 'parent_ou';
567             var user = new openils.User();
568
569             if(this.widgetValue == null && this.orgDefaultsToWs) 
570                 this.widgetValue = user.user.ws_ou();
571             
572             // if we have a limit perm, find the relevent orgs (async)
573             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
574                 this.async = true;
575                 var self = this;
576                 user.getPermOrgList(this.orgLimitPerms, 
577                     function(orgList) {
578                         self.widget.tree = orgList;
579                         self.widget.startup();
580                         self._widgetLoaded();
581                     }
582                 );
583
584             } else {
585                 this.widget.tree = fieldmapper.aou.globalOrgTree;
586                 this.widget.startup();
587             }
588
589             return true;
590         },
591
592         _buildPermGrpSelector : function() {
593             dojo.require('openils.widget.FilteringTreeSelect');
594             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
595             this.widget.disableQuery = this.disableQuery;
596             this.widget.searchAttr = 'name';
597
598             if(this.cache.permGrpTree) {
599                 this.widget.tree = this.cache.permGrpTree;
600                 this.widget.startup();
601                 this._widgetLoaded();
602                 return true;
603             } 
604
605             var self = this;
606             this.async = true;
607             new openils.PermaCrud().retrieveAll('pgt', {
608                 async : !this.forceSync,
609                 oncomplete : function(r) {
610                     var list = openils.Util.readResponse(r, false, true);
611                     if(!list) return;
612                     var map = {};
613                     var root = null;
614                     for(var l in list)
615                         map[list[l].id()] = list[l];
616                     for(var l in list) {
617                         var node = list[l];
618                         var pnode = map[node.parent()];
619                         if(!pnode) {root = node; continue;}
620                         if(!pnode.children()) pnode.children([]);
621                         pnode.children().push(node);
622                     }
623                     self.widget.tree = self.cache.permGrpTree = root;
624                     self.widget.startup();
625                     self._widgetLoaded();
626                 }
627             });
628
629             return true;
630         },
631
632         _buildCopyLocSelector : function() {
633             dojo.require('dijit.form.FilteringSelect');
634             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
635             this.widget.searchAttr = this.widget.labalAttr = 'name';
636             this.widget.valueAttr = 'id';
637
638             if(this.cache.copyLocStore) {
639                 this.widget.store = this.cache.copyLocStore;
640                 this.widget.startup();
641                 this.async = false;
642                 return true;
643             } 
644
645             // my orgs
646             var ws_ou = openils.User.user.ws_ou();
647             var orgs = fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() });
648             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
649
650             var self = this;
651             new openils.PermaCrud().search('acpl', {owning_lib : orgs}, {
652                 async : !this.forceSync,
653                 oncomplete : function(r) {
654                     var list = openils.Util.readResponse(r, false, true);
655                     if(!list) return;
656                     self.widget.store = 
657                         new dojo.data.ItemFileReadStore({data:fieldmapper.acpl.toStoreData(list)});
658                     self.cache.copyLocStore = self.widget.store;
659                     self.widget.startup();
660                     self._widgetLoaded();
661                 }
662             });
663
664             return true;
665         },
666
667         _buildAutoCompleteSelector : function(linkClass, searchAttr) {
668             dojo.require("openils.widget.PCrudAutocompleteBox");
669             dojo.mixin(this.dijitArgs, {
670                 fmclass : linkClass,
671                 searchAttr : searchAttr,
672             });
673             this.widget = new openils.widget.PCrudAutocompleteBox(this.dijitArgs, this.parentNode);
674             this._widgetLoaded();
675             return true;
676         }
677     });
678
679     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
680     openils.widget.AutoFieldWidget.cache = {};
681     openils.widget.AutoFieldWidget.defaultLinkedDataLoader = {};
682
683 }
684