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