]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoFieldWidget.js
LP1615805 No inputs after submit in patron search (AngularJS)
[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.require('dojo.data.ItemFileReadStore');
8         dojo.requireLocalization("openils.widget", "AutoFieldWidget");
9
10     dojo.declare('openils.widget.AutoFieldWidget', null, {
11
12         async : false,
13
14         /**
15          * args:
16          *  idlField -- Field description object from fieldmapper.IDL.fmclasses
17          *  fmObject -- If available, the object being edited.  This will be used 
18          *      to set the value of the widget.
19          *  fmClass -- Class name (not required if idlField or fmObject is set)
20          *  fmField -- Field name (not required if idlField)
21          *  parentNode -- If defined, the widget will be appended to this DOM node
22          *  dijitArgs -- Optional parameters object, passed directly to the dojo widget
23          *  orgLimitPerms -- If this field defines a set of org units and an orgLimitPerms 
24          *      is defined, the code will limit the org units in the set to those
25          *      allowed by the permission
26          *  orgDefaultsToWs -- If this is an org unit field and the widget has no value,
27          *      set the value equal to the users's workstation org unit.  Othwerwise, leave it null
28          *  selfReference -- The primary purpose of an AutoFieldWidget is to render the value
29          *      or widget for a field on an object (that may or may not link to another object).
30          *      selfReference allows you to sidestep the indirection and create a selector widget
31          *      based purely on an fmClass.  To get a dropdown of all of the 'abc'
32          *      objects, pass in {selfReference : true, fmClass : 'abc'}.  
33          *  labelFormat -- For widgets that are displayed as remote object filtering selects,
34          *      this provides a mechanism for overriding the label format in the filtering select.
35          *      It must be an array, whose first value is a format string, compliant with
36          *      dojo.string.substitute.  The remaining array items are the arguments to the format
37          *      represented as field names on the remote linked object.
38          *      E.g.
39          *      labelFormat : [ '${0} (${1})', 'obj_field_1', 'obj_field_2' ]
40          *      Note: this does not control the final display value.  Only values in the drop-down.
41          *      See searchFormat for controlling the display value
42          *  searchFormat -- This format controls the structure of the search attribute which
43          *      controls the text used during type-ahead searching and the displayed value in 
44          *      the filtering select.  See labelFormat for the structure.  
45          *  dataLoader : Bypass the default PermaCrud linked data fetcher and use this function instead.
46          *      Function arguments are (link class name, search filter, callback)
47          *      The fetched objects should be passed to the callback as an array
48          *  disableQuery : dojo.data query passed to FilteringTreeSelect-based widgets to disable
49          *      (but leave visible) certain options.  
50          *  useWriteStore : tells AFW to use a dojo.data.ItemFileWriteStore instead of a ReadStore for
51          *      data stores created with dynamic data.  This allows the caller to add/remove items from 
52          *      the store.
53          */
54         constructor : function(args) {
55             for(var k in args)
56                 this[k] = args[k];
57
58             if (!this.dijitArgs) {
59                 this.dijitArgs = {};
60             }
61             this.dijitArgs['scrollOnFocus'] = false;
62
63
64             // find the field description in the IDL if not provided
65             if(this.fmObject) 
66                 this.fmClass = this.fmObject.classname;
67             this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
68
69             if(this.fmClass && !this.fmIDL) {
70                 fieldmapper.IDL.load([this.fmClass]);
71                 this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
72             }
73
74             this.suppressLinkedFields = args.suppressLinkedFields || [];
75
76             if(this.selfReference) {
77                 this.fmField = fieldmapper.IDL.fmclasses[this.fmClass].pkey;
78                 
79                 // create a mock-up of the idlField object.  
80                 this.idlField = {
81                     datatype : 'link',
82                     'class' : this.fmClass,
83                     reltype : 'has_a',
84                     key : this.fmField,
85                     name : this.fmField
86                 };
87
88             } else {
89
90                 if(!this.idlField) {
91                     this.fmIDL = fieldmapper.IDL.fmclasses[this.fmClass];
92                     var fields = this.fmIDL.fields;
93                     for(var f in fields) 
94                         if(fields[f].name == this.fmField)
95                             this.idlField = fields[f];
96                 }
97             }
98
99             if(!this.idlField) 
100                 throw new Error("AutoFieldWidget could not determine which " +
101                     "field to render.  We need more information. fmClass=" + 
102                     this.fmClass + ' fmField=' + this.fmField + ' fmObject=' + js2JSON(this.fmObject));
103
104             this.auth = openils.User.authtoken;
105             this.cache = openils.widget.AutoFieldWidget.cache;
106             this.cache[this.auth] = this.cache[this.auth] || {};
107             this.cache[this.auth].single = this.cache[this.auth].single || {};
108             this.cache[this.auth].list = this.cache[this.auth].list || {};
109
110             if (this.useWriteStore) {
111                 dojo.require('dojo.data.ItemFileWriteStore');
112                 this.storeConstructor = dojo.data.ItemFileWriteStore;
113             } else {
114                 this.storeConstructor = dojo.data.ItemFileReadStore;
115             }
116         },
117
118         /**
119          * Turn the widget-stored value into a value oils understands
120          */
121         getFormattedValue : function() {
122             var value = this.baseWidgetValue();
123             switch(this.idlField.datatype) {
124                 case 'bool':
125                     switch(value) {
126                         case 'true': return 't';
127                         case 'on': return 't';
128                         case 'false' : return 'f';
129                         case 'unset' : return null;
130                         case true : return 't';
131                         default: return 'f';
132                     }
133                 case 'timestamp':
134                     if(!value) return null;
135                     return dojo.date.stamp.toISOString(value);
136                 case 'int':
137                 case 'float':
138                 case 'money':
139                     if(isNaN(value)) value = null;
140                 default:
141                     return (value === '') ? null : value;
142             }
143         },
144
145         baseWidgetValue : function(value) {
146             var attr = (this.readOnly) ? 'content' : 'value';
147             if(arguments.length) this.widget.attr(attr, value);
148             return this.widget.attr(attr);
149         },
150         
151         /**
152          * Turn the widget-stored value into something visually suitable
153          */
154         getDisplayString : function() {
155             var value = this.widgetValue;
156             if(this.inherits) {
157                 switch(value) {
158                     case null :
159                     case undefined :
160                     case 'unset' :
161                         return openils.widget.AutoFieldWidget.localeStrings.INHERITED;
162                 }
163             }
164             switch(this.idlField.datatype) {
165                 case 'bool':
166                     switch(value) {
167                         case 't': 
168                         case 'true': 
169                             return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
170                         case 'f' : 
171                         case 'false' : 
172                             return openils.widget.AutoFieldWidget.localeStrings.FALSE;
173                         case  null :
174                         case 'unset' : return openils.widget.AutoFieldWidget.localeStrings.UNSET;
175                         case true : return openils.widget.AutoFieldWidget.localeStrings.TRUE; 
176                         default: return openils.widget.AutoFieldWidget.localeStrings.FALSE;
177                     }
178                 case 'timestamp':
179                     if (!value) return '';
180                     return openils.Util.timeStamp(
181                         value, {"formatLength": "short"}
182                     );
183                 case 'org_unit':
184                     if(value === null || value === undefined) return '';
185                     return fieldmapper.aou.findOrgUnit(value).shortname();
186                 case 'int':
187                 case 'float':
188                     if(isNaN(value)) value = 0;
189                 default:
190                     if(value === undefined || value === null)
191                         value = '';
192                     return value+'';
193             }
194         },
195
196         isRequired : function() {
197             return (
198                 !this.readOnly && (
199                     this.idlField.required || (
200                         this.dijitArgs && (
201                             this.dijitArgs.required || this.dijitArgs.regExp
202                         )
203                     )
204                 )
205             );
206         },
207
208         build : function(onload) {
209
210             if(this.widgetValue == null)
211                 this.widgetValue = (this.fmObject) ? this.fmObject[this.idlField.name]() : null;
212
213             if(this.widget) {
214                 // core widget provided for us, attach and move on
215                 if(this.parentNode) // may already be in the "right" place
216                     this.parentNode.appendChild(this.widget.domNode);
217                 if (this.shove) {
218                     if (this.shove.mode == "update") {
219                         if (this.idlField.datatype == "timestamp")
220                             this.widgetValue = openils.Util.timeStampAsDateObj(
221                                 this.widgetValue
222                             );
223                     } else {
224                         this.widgetValue = this.shove.create;
225                     }
226                     this._widgetLoaded();
227                 } else if (this.widget.attr("value") == null) {
228                     this._widgetLoaded();
229                 }
230                 return;
231             }
232             
233             if(!this.parentNode) // give it somewhere to live so that dojo won't complain
234                 this.parentNode = dojo.create('div');
235
236             this.onload = onload;
237
238             if(this.readOnly) {
239                 dojo.require('dijit.layout.ContentPane');
240                 this.widget = new dijit.layout.ContentPane(this.dijitArgs, this.parentNode);
241                 if(this.widgetValue !== null)
242                     this._tryLinkedDisplayField();
243
244             } else if(this.widgetClass) {
245                 dojo.require(this.widgetClass);
246                 eval('this.widget = new ' + this.widgetClass + '(this.dijitArgs, this.parentNode);');
247
248             } else {
249
250                 switch(this.idlField.datatype) {
251                     
252                     case 'id':
253                         dojo.require('dijit.form.TextBox');
254                         this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
255                         break;
256
257                     case 'org_unit':
258                         this._buildOrgSelector();
259                         break;
260
261                     case 'money':
262                         // dojo.require('dijit.form.CurrencyTextBox');
263                         // the CurrencyTextBox dijit is broken in Dojo 1.3; upon upgrading
264                         // to Dojo 1.5 or later, should re-evaluate work-around use of dijit.form.NumberTextBox.
265                         // See https://bugs.launchpad.net/evergreen/+bug/702117
266                         dojo.require('dijit.form.NumberTextBox');
267                         this.dijitArgs = dojo.mixin({constraints:{places:'0,2'}}, this.dijitArgs || {});
268                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
269                         break;
270
271                     case 'int':
272                         dojo.require('dijit.form.NumberTextBox');
273                         this.dijitArgs = dojo.mixin({constraints:{places:0}}, this.dijitArgs || {});
274                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
275                         break;
276
277                     case 'float':
278                         dojo.require('dijit.form.NumberTextBox');
279                         this.widget = new dijit.form.NumberTextBox(this.dijitArgs, this.parentNode);
280                         break;
281
282                     case 'timestamp':
283                         dojo.require('dijit.form.DateTextBox');
284                         dojo.require('dojo.date.stamp');
285                         if(!this.dijitArgs.constraints) {
286                             this.dijitArgs.constraints = {};
287                         }
288                         if(!this.dijitArgs.constraints.datePattern) {
289                             var user = new openils.User().user;
290                             if(user.ws_ou()) {
291                                 var datePattern = fieldmapper.aou.fetchOrgSettingDefault(user.ws_ou(), 'format.date');
292                                 if(datePattern) this.dijitArgs.constraints.datePattern = datePattern.value;
293                             }
294                         }
295                         this.widget = new dijit.form.DateTextBox(this.dijitArgs, this.parentNode);
296                         if (this.widgetValue != null) {
297                             this.widgetValue = openils.Util.timeStampAsDateObj(
298                                 this.widgetValue
299                             );
300                         }
301                         break;
302
303                     case 'bool':
304                         if(this.ternary || this.inherits) {
305                             dojo.require('dijit.form.FilteringSelect');
306                             var store = new dojo.data.ItemFileReadStore({
307                                 data:{
308                                     identifier : 'value',
309                                     items:[
310                                         {label : (this.inherits ? openils.widget.AutoFieldWidget.localeStrings.INHERITED : openils.widget.AutoFieldWidget.localeStrings.UNSET), value : 'unset'},
311                                         {label : openils.widget.AutoFieldWidget.localeStrings.TRUE, value : 'true'},
312                                         {label : openils.widget.AutoFieldWidget.localeStrings.FALSE, value : 'false'}
313                                     ]
314                                 }
315                             });
316                             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
317                             this.widget.searchAttr = this.widget.labelAttr = 'label';
318                             this.widget.valueAttr = 'value';
319                             this.widget.store = store;
320                             this.widget.startup();
321                             this.widgetValue = (this.widgetValue === null) ? 'unset' : 
322                                 (openils.Util.isTrue(this.widgetValue)) ? 'true' : 'false';
323                         } else {
324                             dojo.require('dijit.form.CheckBox');
325                             this.widget = new dijit.form.CheckBox(this.dijitArgs, this.parentNode);
326                             this.widgetValue = openils.Util.isTrue(this.widgetValue);
327                         }
328                         break;
329
330                     case 'link':
331                         if(this._buildLinkSelector()) break;
332
333                     default:
334                         if(this.dijitArgs && (this.dijitArgs.required || this.dijitArgs.regExp)) {
335                             dojo.require('dijit.form.ValidationTextBox');
336                             this.widget = new dijit.form.ValidationTextBox(this.dijitArgs, this.parentNode);
337                         } else {
338                             dojo.require('dijit.form.TextBox');
339                             this.widget = new dijit.form.TextBox(this.dijitArgs, this.parentNode);
340                         }
341                 }
342             }
343
344             if(!this.async) this._widgetLoaded();
345             return this.widget;
346         },
347
348         // we want to display the value for our widget.  However, instead of displaying
349         // an ID, for exmaple, display the value for the 'selector' field on the object
350         // the ID points to
351         _tryLinkedDisplayField : function(noAsync) {
352
353             if(this.idlField.datatype == 'org_unit')
354                 return false; // we already handle org_units, no need to re-fetch
355
356             // user opted to bypass fetching this linked data
357             if(this.suppressLinkedFields.indexOf(this.idlField.name) > -1)
358                 return false;
359
360             var linkInfo = this._getLinkSelector();
361             if(!(linkInfo && linkInfo.vfield && linkInfo.vfield.selector)) 
362                 return false;
363             var lclass = linkInfo.linkClass;
364
365             if(lclass == 'aou') 
366                 return false;
367
368             // first try the store cache
369             var self = this;
370             if(this.cache[this.auth].list[lclass]) {
371                 var store = this.cache[this.auth].list[lclass];
372                 var query = {};
373                 query[linkInfo.vfield.name] = ''+this.widgetValue;
374                 var found = false;
375                 store.fetch({query:query, onComplete:
376                     function(list) {
377                         if(list[0]) {
378                             var item = list[0];
379                             if(self.labelFormat) {
380                                 self.widgetValue = self._applyLabelFormat(item, self.labelFormat);
381                             } else {
382                                 self.widgetValue = store.getValue(item, linkInfo.vfield.selector);
383                             }
384                             found = true;
385                         }
386                     }
387                 });
388
389                 if(found) return;
390             }
391
392             // then try the single object cache
393             var item;
394             if(this.cache[this.auth].single[lclass] && (
395                 item = this.cache[this.auth].single[lclass][this.widgetValue]) ) {
396
397                 this.widgetValue = (this.labelFormat) ? 
398                     this._applyLabelFormat(item.toStoreItem(), this.labelFormat) :
399                     item[linkInfo.vfield.selector]();
400
401                 return;
402             }
403
404             console.log("Fetching linked object " + lclass + " : " + this.widgetValue);
405
406             // if those fail, fetch the linked object
407             this.async = true;
408             var self = this;
409             new openils.PermaCrud().retrieve(lclass, this.widgetValue, {   
410                 async : !this.forceSync,
411                 oncomplete : function(r) {
412                     var item = openils.Util.readResponse(r);
413
414                     // cache the true object under its real value
415                     if(!self.cache[self.auth].single[lclass])
416                         self.cache[self.auth].single[lclass] = {};
417                     self.cache[self.auth].single[lclass][self.widgetValue] = item;
418
419                     self.widgetValue = (self.labelFormat) ? 
420                         self._applyLabelFormat(item.toStoreItem(), self.labelFormat) :
421                         item[linkInfo.vfield.selector]();
422
423                     self.widget.startup();
424                     self._widgetLoaded();
425                 }
426             });
427         },
428
429         _getLinkSelector : function() {
430             var linkClass = this.idlField['class'];
431             if(this.idlField.reltype != 'has_a')  return false;
432             if(!fieldmapper.IDL.fmclasses[linkClass]) // class neglected by AutoIDL
433                 fieldmapper.IDL.load([linkClass]);
434             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud) return false;
435             if(!fieldmapper.IDL.fmclasses[linkClass].permacrud.retrieve) return false;
436
437             var vfield;
438             var rclassIdl = fieldmapper.IDL.fmclasses[linkClass];
439
440             for(var f in rclassIdl.fields) {
441                 if(this.idlField.key == rclassIdl.fields[f].name) {
442                     vfield = rclassIdl.fields[f];
443                     break;
444                 }
445             }
446
447             if(!vfield) 
448                 throw new Error("'" + linkClass + "' has no '" + this.idlField.key + "' field!");
449
450             return {
451                 linkClass : linkClass,
452                 vfield : vfield
453             };
454         },
455
456         _applyLabelFormat : function (item, formatList) {
457
458             try {
459
460                 // formatList[1..*] are names of fields.  Pull the field
461                 // values from each object to determine the values for string substitution
462                 var values = [];
463                 var format = formatList[0];
464                 for(var i = 1; i< formatList.length; i++) 
465                     values.push(item[formatList[i]]);
466
467                 return dojo.string.substitute(format, values);
468
469             } catch(E) {
470                 throw new Error(
471                     "openils.widget.AutoFieldWidget: Invalid formatList ["+formatList+"] : "+E);
472             }
473         },
474
475         _buildLinkSelector : function() {
476             var self = this;
477             var selectorInfo = this._getLinkSelector();
478             if(!selectorInfo) return false;
479
480             var linkClass = selectorInfo.linkClass;
481             var vfield = selectorInfo.vfield;
482
483             this.async = true;
484
485             if(linkClass == 'pgt')
486                 return this._buildPermGrpSelector();
487             if(linkClass == 'aou')
488                 return this._buildOrgSelector();
489             if(linkClass == 'acpl'){
490                 var self2 = this;
491                 var orgs = [];
492                 /* -----------------------------------------------------
493                 When the copy location dropdown is in a link context
494                 we need to expand the list to the provided permission 
495                 branches
496                 ------------------------------------------------------*/
497                 if(this.orgLimitPerms){ 
498                 var buildCopyLocSelector = this._buildCopyLocSelector;
499                 new openils.User().getPermOrgList(
500                     self2.orgLimitPerms,
501                     function(orgsi) {                   
502                         orgs = orgs.concat(orgsi);      
503                         buildCopyLocSelector(orgs,self2);
504                     },
505                     true, true // descendants, id_list
506                 );
507                 return true;
508                 }
509             else                                
510                 return this._buildCopyLocSelector(orgs,self);
511             }
512             if(linkClass == 'acqpro')
513                 return this._buildAutoCompleteSelector(linkClass, vfield.selector);
514
515
516             dojo.require('dojo.data.ItemFileReadStore');
517             dojo.require('dijit.form.FilteringSelect');
518
519             this.widget = new dijit.form.FilteringSelect(this.dijitArgs, this.parentNode);
520             this.widget.searchAttr = this.widget.labelAttr = vfield.selector || vfield.name;
521             this.widget.valueAttr = vfield.name;
522             this.widget.attr('disabled', true);
523
524             var oncomplete = function(list) {
525                 self.widget.attr('disabled', false);
526
527                 if(self.labelFormat) 
528                     self.widget.labelAttr = '_label';
529
530                 if(self.searchFormat)
531                     self.widget.searchAttr = '_search';
532
533                 if(list) {
534                     var storeData = {data:fieldmapper[linkClass].toStoreData(list)};
535
536                     if(self.labelFormat) {
537                         dojo.forEach(storeData.data.items, 
538                             function(item) {
539                                 item._label = self._applyLabelFormat(item, self.labelFormat);
540                             }
541                         );
542                     }
543
544                     if(self.searchFormat) {
545                         dojo.forEach(storeData.data.items, 
546                             function(item) {
547                                 item._search = self._applyLabelFormat(item, self.searchFormat);
548                             }
549                         );
550                     }
551
552                     self.widget.store = new self.storeConstructor(storeData);
553                     self.cache[self.auth].list[linkClass] = self.widget.store;
554
555                 } else {
556                     self.widget.store = self.cache[self.auth].list[linkClass];
557                 }
558
559                 self.widget.startup();
560                 self._widgetLoaded();
561             };
562
563             if(!this.noCache && this.cache[self.auth].list[linkClass]) {
564                 oncomplete();
565
566             } else {
567
568                 if(!this.dataLoader && openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass])
569                     this.dataLoader = openils.widget.AutoFieldWidget.defaultLinkedDataLoader[linkClass];
570
571                 if(this.dataLoader) {
572
573                     // caller provided an external function for retrieving the data
574                     this.dataLoader(linkClass, this.searchFilter, oncomplete);
575
576                 } else {
577
578                     var _cb = function(r) {
579                         oncomplete(openils.Util.readResponse(r, false, true));
580                     };
581
582                     /* XXX LFW: I want to uncomment the following three lines that refer to ob, but haven't had the time to properly test. */
583
584                     //var ob = {};
585                     //ob[linkClass] = vfield.selector || vfield.name;
586
587                     this.searchOptions = dojo.mixin(
588                         {
589                             async : !this.forceSync,
590                             oncomplete : _cb
591                             //order_by : ob
592                         }, this.searchOptions
593                     );
594
595                     if (this.searchFilter) {
596                         new openils.PermaCrud().search(linkClass, this.searchFilter, this.searchOptions);
597                     } else {
598                         new openils.PermaCrud().retrieveAll(linkClass, this.searchOptions);
599                     }
600                 }
601             }
602
603             return true;
604         },
605
606         /**
607          * For widgets that run asynchronously, provide a callback for finishing up
608          */
609         _widgetLoaded : function(value) {
610             
611             if(this.readOnly) {
612
613                 /* -------------------------------------------------------------
614                    when using widgets in a grid, the cell may dissapear, which 
615                    kills the underlying DOM node, which causes this to fail.
616                    For now, back out gracefully and let grid getters use
617                    getDisplayString() instead
618                   -------------------------------------------------------------*/
619                 try { 
620                     this.baseWidgetValue(this.getDisplayString());
621                 } catch (E) {};
622
623             } else {
624
625                 this.baseWidgetValue(this.widgetValue);
626                 if(this.idlField.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence && (!this.selfReference && !this.noDisablePkey))
627                     this.widget.attr('disabled', true); 
628                 if(this.disableWidgetTest && this.disableWidgetTest(this.idlField.name, this.fmObject))
629                     this.widget.attr('disabled', true); 
630             }
631             if(this.onload)
632                 this.onload(this.widget, this);
633
634             if(!this.readOnly && (this.idlField.required || (this.dijitArgs && this.dijitArgs.required))) {
635                 // a required dijit is not given any styling to indicate the value
636                 // is invalid until the user has focused the widget then left it with
637                 // invalid data.  This change tells dojo to pretend this focusing has 
638                 // already happened so we can style required widgets during page render.
639                 this.widget._hasBeenBlurred = true;
640                 if(this.widget.validate)
641                     this.widget.validate();
642             }
643         },
644
645         _buildOrgSelector : function() {
646             dojo.require('fieldmapper.OrgUtils');
647             dojo.require('openils.widget.FilteringTreeSelect');
648             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
649             this.widget.searchAttr = this.searchAttr || 'shortname';
650             this.widget.labelAttr = this.searchAttr || 'shortname';
651             this.widget.parentField = 'parent_ou';
652             var user = new openils.User();
653
654             if(this.widgetValue == null && this.orgDefaultsToWs) 
655                 this.widgetValue = user.user.ws_ou();
656             
657             // if we have a limit perm, find the relevent orgs (async)
658             if(this.orgLimitPerms && this.orgLimitPerms.length > 0) {
659                 this.async = true;
660                 var self = this;
661                 user.getPermOrgList(this.orgLimitPerms, 
662                     function(orgList) {
663                         self.widget.tree = orgList;
664                         self.widget.startup();
665                         self._widgetLoaded();
666                     }
667                 );
668
669             } else {
670                 this.widget.tree = fieldmapper.aou.globalOrgTree;
671                 this.widget.startup();
672             }
673
674             return true;
675         },
676
677         _buildPermGrpSelector : function() {
678             dojo.require('openils.widget.FilteringTreeSelect');
679             this.widget = new openils.widget.FilteringTreeSelect(this.dijitArgs, this.parentNode);
680             this.widget.disableQuery = this.disableQuery;
681             this.widget.searchAttr = 'name';
682
683             if(this.cache.permGrpTree) {
684                 this.widget.tree = this.cache.permGrpTree;
685                 this.widget.startup();
686                 this._widgetLoaded();
687                 return true;
688             } 
689
690             var self = this;
691             this.async = true;
692             new openils.PermaCrud().retrieveAll('pgt', {
693                 async : !this.forceSync,
694                 oncomplete : function(r) {
695                     var list = openils.Util.readResponse(r, false, true);
696                     if(!list) return;
697                     var map = {};
698                     var root = null;
699                     for(var l in list)
700                         map[list[l].id()] = list[l];
701                     for(var l in list) {
702                         var node = list[l];
703                         var pnode = map[node.parent()];
704                         if(!pnode) {root = node; continue;}
705                         if(!pnode.children()) pnode.children([]);
706                         pnode.children().push(node);
707                     }
708                     self.widget.tree = self.cache.permGrpTree = root;
709                     self.widget.startup();
710                     self._widgetLoaded();
711                 }
712             });
713
714             return true;
715         },
716
717         _buildCopyLocSelector : function(orgs,self) {
718             dojo.require('dijit.form.FilteringSelect');
719             self.widget = new dijit.form.FilteringSelect(self.dijitArgs, self.parentNode);
720             self.widget.searchAttr = self.widget.labalAttr = 'name';
721             self.widget.valueAttr = 'id';
722             
723             // my orgs
724             var ws_ou = openils.User.user.ws_ou();
725             orgs = orgs.concat(fieldmapper.aou.findOrgUnit(ws_ou).orgNodeTrail().map(function (i) { return i.id() }));
726             orgs = orgs.concat(fieldmapper.aou.descendantNodeList(ws_ou).map(function (i) { return i.id() }));
727
728             var search = {owning_lib : orgs, deleted : 'f'};
729
730             if(self.cache.copyLocStore) {
731                 var store = self.cache.copyLocStore;
732                 var allGood = false;
733                 var locIds = [];
734
735                 // make sure the copy location the caller cares 
736                 // about (our value) is present in the cache.
737                 // if not, fetch the list, adding our value to
738                 // the set of locations to fetch.
739
740                 var allGood = false;
741                 if (self.widgetValue) {
742                 
743                     store.fetch({
744                         onComplete : function(list) {
745                             dojo.forEach(list, function(item) {
746                                 var id = store.getValue(item, 'id');
747                                 if (id == self.widgetValue)
748                                     allGood = true;
749                                 locIds.push(id);
750                             });
751                         }
752                     });
753
754                 } else {
755                     allGood = true;
756                 }
757
758                 if (allGood) {
759                     self.widget.store = self.cache.copyLocStore;
760                     self.widget.startup();
761                     self.async = false;
762                     return true;
763
764                 } else {
765                     // cached IDs plus id of self.widgetValue;
766                     locIds.push(self.widgetValue);
767                     search = {id : locIds, deleted: 'f'};
768                 }
769             } 
770
771
772             new openils.PermaCrud().search('acpl', search, {                    
773                 async : !self.forceSync,
774                 order_by : {"acpl": "name"},
775                 oncomplete : function(r) {
776                     var list = openils.Util.readResponse(r, false, true);
777                     if(!list) return;
778
779                     // if we are including any copy locations outside our org
780                     // unit scope, tag them with a context org unit to prevent
781                     // confusion caused by having multiple like-named entries
782                     dojo.forEach(list, function(loc) {                                  
783                         if (orgs.length>1) {
784                             loc.name(loc.name() + ' (' + 
785                                 fieldmapper.aou.findOrgUnit(loc.owning_lib()).shortname() + ')');
786                         }
787                     });
788
789                     self.widget.store = 
790                         new self.storeConstructor({data:fieldmapper.acpl.toStoreData(list)});
791                     self.cache.copyLocStore = self.widget.store;
792                     self.widget.startup();
793                     self._widgetLoaded();
794                 }
795             });
796
797             return true;
798         },
799
800         _buildAutoCompleteSelector : function(linkClass, searchAttr) {
801             dojo.require("openils.widget.PCrudAutocompleteBox");
802             dojo.mixin(this.dijitArgs, {
803                 fmclass : linkClass,
804                 searchAttr : searchAttr,
805             });
806             this.widget = new openils.widget.PCrudAutocompleteBox(this.dijitArgs, this.parentNode);
807             this._widgetLoaded();
808             return true;
809         }
810     });
811
812     openils.widget.AutoFieldWidget.localeStrings = dojo.i18n.getLocalization("openils.widget", "AutoFieldWidget");
813     openils.widget.AutoFieldWidget.cache = {};
814     openils.widget.AutoFieldWidget.defaultLinkedDataLoader = {};
815
816 }
817