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