]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
LP#1287370: minor textual cleanup
[working/Evergreen.git] / Open-ILS / web / js / dojo / openils / widget / AutoGrid.js
1 if(!dojo._hasResource['openils.widget.AutoGrid']) {
2     dojo.provide('openils.widget.AutoGrid');
3     dojo.require('dojox.grid.DataGrid');
4     dojo.require("dojox.encoding.base64");
5     dojo.require('dijit.layout.ContentPane');
6     dojo.require('openils.widget.AutoWidget');
7     dojo.require('openils.widget.AutoFieldWidget');
8     dojo.require('openils.widget.EditPane');
9     dojo.require('openils.widget.EditDialog');
10     dojo.require('openils.widget.GridColumnPicker');
11     dojo.require('openils.widget._GridHelperColumns');
12     dojo.require('openils.Util');
13     dojo.require('openils.CGI');
14     dojo.requireLocalization('openils.widget', 'AutoFieldWidget');
15
16     dojo.declare(
17         'openils.widget.AutoGrid',
18         [dojox.grid.DataGrid, openils.widget.AutoWidget, openils.widget._GridHelperColumns],
19         {
20
21             /* if true, pop up an edit dialog when user hits Enter on a give row */
22             editPaneOnSubmit : null,
23             onPostSubmit : null, // called after any CRUD actions are complete
24             createPaneOnSubmit : null,
25             editOnEnter : false, 
26             defaultCellWidth : null,
27             editStyle : 'dialog',
28             editReadOnly : false,
29             suppressFields : null,
30             suppressEditFields : null,
31             suppressFilterFields : null,
32             showColumnPicker : false,
33             columnPersistKey : null,
34             displayLimit : 15,
35             displayOffset : 0,
36             requiredFields : null,
37             hidePaginator : false,
38             showLoadFilter : true,
39             onItemReceived : null,
40             suppressLinkedFields : null, // list of fields whose linked display data should not be fetched from the server
41             urlNavigation : false,
42
43             // When using urlNavigation, this is a stash where the
44             // caller can place arbitrary data to be passed around
45             // between pages.
46             urlUserData : {},
47
48             /* by default, don't show auto-generated (sequence) fields */
49             showSequenceFields : false, 
50
51             startup : function() {
52                 var _this = this;
53                 this.selectionMode = 'single';
54                 this.sequence = openils.widget.AutoGrid.sequence++;
55                 openils.widget.AutoGrid.gridCache[this.sequence] = this;
56                 this.inherited(arguments);
57                 this.initAutoEnv();
58
59                 this.setStructure(this._compileStructure());
60                 this._startupGridHelperColumns();
61                 this.setStructure(this.structure); // required after _startupGridHelper()
62
63                 this.setStore(this.buildAutoStore());
64                 this.cachedQueryOpts = {};
65                 this._showing_create_pane = false;
66                 this.overrideEditWidgets = {};
67                 this.overrideEditWidgetClass = {};
68                 this.overrideWidgetArgs = {};
69                 this.extractUrlOps();
70
71                 this.nls = dojo.i18n.getLocalization('openils.widget', 'AutoFieldWidget');
72
73                 if(this.editOnEnter) 
74                     this._applyEditOnEnter();
75                 else if(this.singleEditStyle) 
76                     this._applySingleEditStyle();
77
78                 if(!this.hidePaginator) {
79                     var self = this;
80                     this.paginator = new dijit.layout.ContentPane();
81
82
83                     var back = dojo.create('a', {
84                         innerHTML : self.nls.BACK,
85                         style : 'padding-right:6px;',
86                         href : 'javascript:void(0);', 
87                         onclick : function() { 
88                             self.cachedQueryOpts.offset = self.displayOffset -= self.displayLimit;
89                             if(self.displayOffset < 0)
90                                 self.cachedQueryOpts.offset = self.displayOffset = 0;
91                             self.refresh();
92                         }
93                     });
94
95                     // TODO: support self.urlNavigation
96                     var forw = dojo.create('a', {
97                         innerHTML : self.nls.NEXT,
98                         style : 'padding-right:6px;',
99                         href : 'javascript:void(0);', 
100                         onclick : function() { 
101                             self.cachedQueryOpts.offset = self.displayOffset += self.displayLimit;
102                             if (self.urlNavigation) {
103                                 self.applyAndExecuteUrlOps(
104                                     self.cachedQueryOpts.offset,
105                                     self.cachedQuerySearch
106                                 );
107                             } else {
108                                 self.refresh();
109                             }
110                         }
111                     });
112
113                     dojo.place(this.paginator.domNode, this.domNode, 'before');
114                     dojo.place(back, this.paginator.domNode);
115                     dojo.place(forw, this.paginator.domNode);
116
117                     if(this.showLoadFilter) {
118                         dojo.require('openils.widget.PCrudFilterDialog');
119                         dojo.place(
120                             dojo.create('a', {
121                                 innerHTML : self.nls.FILTER,
122                                 style : 'padding-right:6px;',
123                                 href : 'javascript:void(0);', 
124                                 onclick : function() { 
125                                     if (!self.filterDialog) {
126
127                                         self.filterDialog = new openils.widget.PCrudFilterDialog(
128                                             {fmClass:self.fmClass, suppressFilterFields:self.suppressFilterFields})
129
130                                         self.filterDialog.onApply = function(filter) {
131                                             if (self.urlNavigation) {
132                                                 self.applyAndExecuteUrlOps(0, filter);
133
134                                             } else {
135                                                 self.cachedQuerySearch = filter;
136                                                 self.resetStore();
137                                                 self.loadAll(
138                                                     dojo.mixin( { offset : 0 }, self.cachedQueryOpts ),
139                                                     self.cachedQuerySearch,
140                                                     true
141                                                 );
142                                             }
143                                         };
144
145                                         self.filterDialog.startup();
146                                     }
147                                     self.filterDialog.show();
148                                 }
149                             }),
150                             this.paginator.domNode
151                         );
152                     }
153
154                     // progress image
155                     this.loadProgressIndicator = dojo.create('img', {
156                         src:'/opac/images/progressbar_green.gif', // TODO configured path
157                         style:'height:16px;width:16px;'
158                     });
159                     dojo.place(this.loadProgressIndicator, this.paginator.domNode);
160                 }
161             },
162
163             // set URL options then jump to the new URL
164             applyAndExecuteUrlOps : function(offset, filter) {
165                 var ops = {
166                     filter : filter, // TODO: load from query cache on offset-only change
167                     offset : offset || 0,
168                     userData : this.urlUserData
169                 }
170
171                 var encoded = dojox.encoding.base64.encode(
172                     js2JSON(ops)
173                     .split("")
174                     .map(function(c) { return c.charCodeAt(0); })
175                 );
176
177                 var cgi = new openils.CGI();
178                 cgi.param('djgridops', encoded);
179                 location.href = cgi.url();
180             },
181
182             // extract options encoded in the URL
183             extractUrlOps : function() {
184                 var ops = new openils.CGI().param('djgridops');
185                 if (!ops) return;
186
187                 ops = JSON2js(
188                     dojox.encoding.base64.decode(ops)
189                     .map(function(b) {return String.fromCharCode(b)})
190                     .join("")
191                 );
192
193                 this.urlOps = ops;
194                 this.urlUserData = ops.userData;
195             },
196
197             hideLoadProgressIndicator : function() {
198                 dojo.style(this.loadProgressIndicator, 'visibility', 'hidden');
199             },
200
201             showLoadProgressIndicator : function() {
202                 dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
203             },
204
205             _compileStructure : function() {
206                 var existing = (this.structure && this.structure[0].cells[0]) ? 
207                     this.structure[0].cells[0] : [];
208                 var fields = [];
209
210                 var self = this;
211                 function pushEntry(entry) {
212                     if(self.suppressFields) {
213                         if(dojo.indexOf(self.suppressFields, entry.field) != -1)
214                             return;
215                     }
216                     if(!entry.get) 
217                         entry.get = openils.widget.AutoGrid.defaultGetter
218                     if(!entry.width && self.defaultCellWidth)
219                         entry.width = self.defaultCellWidth;
220                     fields.push(entry);
221                 }
222
223                 if(!this.fieldOrder) {
224                     /* no order defined, start with any explicit grid fields */
225                     for(var e in existing) {
226                         var entry = existing[e];
227                         var field = this.fmIDL.fields.filter(
228                             function(i){return (i.name == entry.field)})[0];
229                         if(field) entry.name = entry.name || field.label;
230                         pushEntry(entry);
231                     }
232                 }
233
234                 for(var f in this.sortedFieldList) {
235                     var field = this.sortedFieldList[f];
236                     if(!field || field.virtual) continue;
237                     
238                     // field was already added above
239                     if(fields.filter(function(i){return (i.field == field.name)})[0]) 
240                         continue;
241
242                     var entry = existing.filter(function(i){return (i.field == field.name)})[0];
243                     if(entry) {
244                         entry.name = entry.name || field.label;
245                     } else {
246                         // unless specifically requested, hide sequence fields
247                         if(!this.showSequenceFields && field.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
248                             continue; 
249
250                         entry = {field:field.name, name:field.label};
251                     }
252                     pushEntry(entry);
253                 }
254
255                 if(this.fieldOrder) {
256                     /* append any explicit non-IDL grid fields to the end */
257                     for(var e in existing) {
258                         var entry = existing[e];
259                         var field = fields.filter(
260                             function(i){return (i.field == entry.field)})[0];
261                         if(field) continue; // don't duplicate
262                         pushEntry(entry);
263                     }
264                 }
265
266                 return [{cells: [fields]}];
267             },
268
269             /**
270              * @return {Array} List of every fieldmapper object in the data store
271              */
272             getAllObjects : function() {
273                 var objs = [];
274                 var self = this;
275                 this.store.fetch({
276                     onComplete : function(list) {
277                         dojo.forEach(list, 
278                             function(item) {
279                                 objs.push(new fieldmapper[self.fmClass]().fromStoreItem(item));
280                             }
281                         )
282                     }
283                 });
284                 return objs;
285             },
286
287             /**
288              * Deletes the underlying object for all selected rows
289              */
290             deleteSelected : function() {
291                 var items = this.getSelectedItems();
292                 var total = items.length;
293                 var self = this;
294                 dojo.require('openils.PermaCrud');
295                 dojo.forEach(items,
296                     function(item) {
297                         var fmObject = new fieldmapper[self.fmClass]().fromStoreItem(item);
298                         new openils.PermaCrud()['eliminate'](
299                             fmObject, {
300                                 oncomplete : function(r) {
301                                     self.store.deleteItem(item);
302                                     if (--total < 1 && self.onPostSubmit) {
303                                         self.onPostSubmit();
304                                     }
305                                 }
306                             }
307                         );
308                     }
309                 );
310             },
311
312             _applySingleEditStyle : function() {
313                 this.onMouseOverRow = function(e) {};
314                 this.onMouseOutRow = function(e) {};
315                 this.onCellFocus = function(cell, rowIndex) { 
316                     this.selection.deselectAll();
317                     this.selection.select(this.focus.rowIndex);
318                 };
319             },
320
321             /* capture keydown and launch edit dialog on enter */
322             _applyEditOnEnter : function() {
323                 this._applySingleEditStyle();
324
325                 dojo.connect(this, 'onRowDblClick',
326                     function(e) {
327                         if(this.editStyle == 'pane')
328                             this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
329                         else
330                             this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
331                     }
332                 );
333
334                 dojo.connect(this, 'onKeyDown',
335                     function(e) {
336                         if(e.keyCode == dojo.keys.ENTER) {
337                             this.selection.deselectAll();
338                             this.selection.select(this.focus.rowIndex);
339                             if(this.editStyle == 'pane')
340                                 this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
341                             else
342                                 this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
343                         }
344                     }
345                 );
346             },
347
348             _makeEditPane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
349                 var grid = this;
350                 var fmObject = new fieldmapper[this.fmClass]().fromStoreItem(storeItem);
351                 var idents = grid.store.getIdentityAttributes();
352
353                 var pane = new openils.widget.EditPane({
354                     fmObject:fmObject,
355                     hideSaveButton : this.editReadOnly,
356                     readOnly : this.editReadOnly,
357                     overrideWidgets : this.overrideEditWidgets,
358                     overrideWidgetClass : this.overrideEditWidgetClass,
359                     overrideWidgetArgs : this.overrideWidgetArgs,
360                     disableWidgetTest : this.disableWidgetTest,
361                     requiredFields : this.requiredFields,
362                     suppressFields : this.suppressEditFields,
363                     onPostSubmit : function() {
364                         for(var i in fmObject._fields) {
365                             var field = fmObject._fields[i];
366                             if(idents.filter(function(j){return (j == field)})[0])
367                                 continue; // don't try to edit an identifier field
368                             grid.store.setValue(storeItem, field, fmObject[field]());
369                         }
370                         if(grid.onPostUpdate)
371                             grid.onPostUpdate(storeItem, rowIndex);
372                         setTimeout(
373                             function(){
374                                 try { 
375                                     grid.views.views[0].getCellNode(rowIndex, 0).focus(); 
376                                 } catch (E) {}
377                             },200
378                         );
379                         if(onPostSubmit) 
380                             onPostSubmit();
381                         if (grid.onPostSubmit)
382                             grid.onPostSubmit();
383
384                     },
385                     onCancel : function() {
386                         setTimeout(function(){
387                             grid.views.views[0].getCellNode(rowIndex, 0).focus();},200);
388                         if(onCancel) onCancel();
389                     }
390                 });
391
392                 if (typeof this.editPaneOnSubmit == "function")
393                     pane.onSubmit = this.editPaneOnSubmit;
394                 pane.fieldOrder = this.fieldOrder;
395                 pane.mode = 'update';
396                 return pane;
397             },
398
399             _makeCreatePane : function(onPostSubmit, onCancel) {
400                 var grid = this;
401                 var pane = new openils.widget.EditPane({
402                     fmClass : this.fmClass,
403                     overrideWidgets : this.overrideEditWidgets,
404                     overrideWidgetClass : this.overrideEditWidgetClass,
405                     overrideWidgetArgs : this.overrideWidgetArgs,
406                     disableWidgetTest : this.disableWidgetTest,
407                     requiredFields : this.requiredFields,
408                     suppressFields : this.suppressEditFields,
409                     onPostSubmit : function(req, cudResults) {
410                         var fmObject = cudResults[0];
411                         if(grid.onPostCreate)
412                             grid.onPostCreate(fmObject);
413                         if(fmObject) 
414                             grid.store.newItem(fmObject.toStoreItem());
415                         setTimeout(function(){
416                             try {
417                                 grid.selection.select(grid.rowCount-1);
418                                 grid.views.views[0].getCellNode(grid.rowCount-1, 1).focus();
419                             } catch (E) {}
420                         },200);
421                         if(onPostSubmit)
422                             onPostSubmit(fmObject);
423                         if (grid.onPostSubmit)
424                             grid.onPostSubmit();
425                     },
426                     onCancel : function() {
427                         if(onCancel) onCancel();
428                     }
429                 });
430                 if (typeof this.createPaneOnSubmit == "function")
431                     pane.onSubmit = this.createPaneOnSubmit;
432                 pane.fieldOrder = this.fieldOrder;
433                 pane.mode = 'create';
434                 return pane;
435             },
436
437             /**
438              * Creates an EditPane with a copy of the data from the provided store
439              * item for cloning said item
440              * @param {Object} storeItem Dojo data item
441              * @param {Number} rowIndex The Grid row index of the item to be cloned
442              * @param {Function} onPostSubmit Optional callback for post-submit behavior
443              * @param {Function} onCancel Optional callback for clone cancelation
444              * @return {Object} The clone EditPane
445              */
446             _makeClonePane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
447                 var clonePane = this._makeCreatePane(onPostSubmit, onCancel);
448                 var origPane = this._makeEditPane(storeItem, rowIndex);
449                 clonePane.startup();
450                 origPane.startup();
451                 dojo.forEach(origPane.fieldList,
452                     function(field) {
453                         if(field.widget.widget.attr('disabled')) return;
454                         var w = clonePane.fieldList.filter(
455                             function(i) { return (i.name == field.name) })[0];
456                         w.widget.baseWidgetValue(field.widget.widget.attr('value')); // sync widgets
457                         w.widget.onload = function(){w.widget.baseWidgetValue(field.widget.widget.attr('value'))}; // async widgets
458                     }
459                 );
460                 origPane.destroy();
461                 return clonePane;
462             },
463
464
465             _drawEditDialog : function(storeItem, rowIndex) {
466                 var self = this;
467                 var done = function() { self.hideDialog(); };
468                 var pane = this._makeEditPane(storeItem, rowIndex, done, done);
469                 this.editDialog = new openils.widget.EditDialog({editPane:pane});
470                 this.editDialog.startup();
471                 this.editDialog.show();
472                 if(this.onEditPane) this.onEditPane(pane);
473             },
474
475             /**
476              * Generates an EditDialog for object creation and displays it to the user
477              */
478             showCreateDialog : function() {
479                 var self = this;
480                 var done = function() { self.hideDialog(); };
481                 var pane = this._makeCreatePane(done, done);
482                 this.editDialog = new openils.widget.EditDialog({editPane:pane});
483                 this.editDialog.startup();
484                 this.editDialog.show();
485             },
486
487             _drawEditPane : function(storeItem, rowIndex) {
488                 var self = this;
489                 var done = function() { self.hidePane(); };
490                 dojo.style(this.domNode, 'display', 'none');
491                 this.editPane = this._makeEditPane(storeItem, rowIndex, done, done);
492                 this.editPane.startup();
493                 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
494                 if(this.onEditPane) this.onEditPane(this.editPane);
495             },
496
497             showClonePane : function(onPostSubmit) {
498                 var self = this;
499                 var done = function() { self.hidePane(); };
500
501                                     
502                 var row = this.getFirstSelectedRow();
503                 if(!row) return;
504
505                 var postSubmit = (onPostSubmit) ? 
506                     function(result) { onPostSubmit(self.getItem(row), result); self.hidePane(); } :
507                     done;
508
509                 dojo.style(this.domNode, 'display', 'none');
510                 this.editPane = this._makeClonePane(this.getItem(row), row, postSubmit, done);
511                 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
512                 if(this.onEditPane) this.onEditPane(this.editPane);
513             },
514
515             showCreatePane : function() {
516                 if (this._showing_create_pane)
517                     return;
518                 this._showing_create_pane = true;
519
520                 var self = this;
521                 var done = function() {
522                     self._showing_create_pane = false;
523                     self.hidePane();
524                 };
525                 dojo.style(this.domNode, 'display', 'none');
526                 this.editPane = this._makeCreatePane(done, done);
527                 this.editPane.startup();
528                 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
529                 if(this.onEditPane) this.onEditPane(this.editPane);
530             },
531
532             hideDialog : function() {
533                 this.editDialog.hide(); 
534                 this.editDialog.destroy(); 
535                 delete this.editDialog;
536                 this.update();
537             },
538
539             hidePane : function() {
540                 this.domNode.parentNode.removeChild(this.editPane.domNode);
541                 this.editPane.destroy();
542                 delete this.editPane;
543                 dojo.style(this.domNode, 'display', 'block');
544                 this.update();
545             },
546             
547             resetStore : function() {
548                 this.setStore(this.buildAutoStore());
549             },
550
551             refresh : function(opts, search) {
552                 opts = opts || this.cachedQueryOpts;
553                 search = search || this.cachedQuerySearch;
554                 this.resetStore();
555                 if (this.dataLoader)
556                     this.dataLoader()
557                 else
558                     this._loadAll(opts, search);
559             },
560
561             // called after a sort change occurs within the column picker
562             cpSortHandler : function(fields) {
563                 console.log("AutoGrod cpSortHandler(): " + js2JSON(fields));
564                 // user-defined sort handler
565                 if (this.onSortChange) { 
566                     this.onSortChange(fields)
567
568                 // default sort handler
569                 } else { 
570                     if (!this.cachedQueryOpts) 
571                         this.cachedQueryOpts = {};
572                     var order_by = '';
573                     dojo.forEach(fields, function(f) {
574                         if (order_by) order_by += ',';
575                         order_by += f.field + ' ' + f.direction
576                     });
577                     this.cachedQueryOpts.order_by = {};
578                     this.cachedQueryOpts.order_by[this.fmClass] = order_by;
579                     this.refresh();
580                 }
581             },
582
583             loadAll : function(opts, search, filter_triggered) {
584                 var _this = this;
585
586                 // first we have to load the column picker to determine the sort fields.
587                
588                 if(this.showColumnPicker && !this.columnPicker) {
589                     if(!this.columnPersistKey) {
590                         console.error("No columnPersistKey defined");
591                         this.columnPicker = {};
592                     } else {
593                         this.columnPicker = new openils.widget.GridColumnPicker(
594                             openils.User.authtoken, this.columnPersistKey, this);
595                         this.columnPicker.onSortChange = function(fields) {_this.cpSortHandler(fields)};
596                         this.columnPicker.onLoad = function(cpOpts) {
597                             _this.cachedQueryOpts = opts;
598                             _this.cachedQuerySearch = search;
599                             _this.cpSortHandler(cpOpts.sortFields); // calls refresh() -> _loadAll()
600                         };
601                         this.columnPicker.load();
602                         return;
603                     }
604                 }
605
606                 // column picker not wanted or already loaded
607                 this._loadAll(opts, search, filter_triggered);
608             },
609
610             _loadAll : function(opts, search, filter_triggered) {
611                 var self = this;
612
613                 dojo.require('openils.PermaCrud');
614                 if(this.loadProgressIndicator)
615                     dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
616                 opts = dojo.mixin(
617                     {limit : this.displayLimit, offset : this.displayOffset}, 
618                     opts || {}
619                 );
620                 opts = dojo.mixin(opts, {
621                     async : true,
622                     streaming : true,
623                     onresponse : function(r) {
624                         var item = openils.Util.readResponse(r);
625                         if (self.onItemReceived) 
626                             self.onItemReceived(item);
627                         self.store.newItem(item.toStoreItem());
628                     },
629                     oncomplete : function() {
630                         if(self.loadProgressIndicator) 
631                             dojo.style(self.loadProgressIndicator, 'visibility', 'hidden');
632                     }
633                 });
634
635                 this.cachedQuerySearch = search;
636                 this.cachedQueryOpts = opts;
637
638                 // retain the most recent external loadAll 
639                 if (!filter_triggered || !this.preFilterSearch)
640                     this.preFilterSearch = dojo.clone( this.cachedQuerySearch );
641
642                 if (this.urlNavigation) {
643                     if (!this.urlOpsApplied) {
644                         this.urlOpsApplied = true;
645                         // on the first page load, apply the ops from the URL
646                         if (this.urlOps) {
647                             opts.offset = self.displayOffset = this.urlOps.offset;
648                             search = dojo.mixin(search || {}, this.urlOps.filter);
649                         }
650                     } else {
651                         // subsequent calls to loadAll() suggest changing
652                         // filters / paging / etc, propagate new values
653                         // to the new URL
654                         return this.applyAndExecuteUrlOps(
655                             this.displayOffset,
656                             this.cachedQuerySearch
657                         );
658                     }
659                 }
660
661                 if(search)
662                     new openils.PermaCrud().search(this.fmClass, search, opts);
663                 else
664                     new openils.PermaCrud().retrieveAll(this.fmClass, opts);
665             }
666         } 
667     );
668
669     // static ID generater seed
670     openils.widget.AutoGrid.sequence = 0;
671     openils.widget.AutoGrid.gridCache = {};
672
673     openils.widget.AutoGrid.markupFactory = dojox.grid.DataGrid.markupFactory;
674
675     openils.widget.AutoGrid.defaultGetter = function(rowIndex, item) {
676         if(!item) return '';
677         if(!this.grid.overrideWidgetArgs[this.field])
678             this.grid.overrideWidgetArgs[this.field] = {};
679         var val = this.grid.store.getValue(item, this.field);
680         var autoWidget = new openils.widget.AutoFieldWidget(dojo.mixin({
681             fmClass: this.grid.fmClass,
682             fmField: this.field,
683             widgetValue : val,
684             readOnly : true,
685             forceSync : true, // prevents many simultaneous requests for the same data
686             suppressLinkedFields : this.grid.suppressLinkedFields
687         },this.grid.overrideWidgetArgs[this.field]));
688
689         autoWidget.build();
690
691         /*
692         // With proper caching, this should not be necessary to prevent grid render flickering
693         var _this = this;
694         autoWidget.build(
695             function(w, ww) {
696                 try {
697                     var node = _this.grid.getCell(_this.index).view.getCellNode(rowIndex, _this.index);
698                     if(node) 
699                         node.innerHTML = ww.getDisplayString();
700                 } catch(E) {}
701             }
702         );
703         */
704
705         return autoWidget.getDisplayString();
706     };
707
708     openils.widget.AutoGrid.orgUnitGetter = function(rowIndex, item) {
709         if (!item) return "";
710
711         var aou_id = this.grid.store.getValue(item, this.field);
712         if (aou_id)
713             return fieldmapper.aou.findOrgUnit(aou_id).shortname();
714         else
715             return "";
716     };
717 }
718