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