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