]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/dojo/openils/widget/AutoGrid.js
added optional list of fields whose linked display value should not be fetched
[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             editOnEnter : false, 
19             defaultCellWidth : null,
20             editStyle : 'dialog',
21             suppressFields : null,
22             hideSelector : false,
23             selectorWidth : '1.5',
24             showColumnPicker : false,
25             columnPickerPrefix : null,
26             displayLimit : 15,
27             displayOffset : 0,
28             showPaginator : false,
29             suppressLinkedFields : null, // list of fields whose linked display data should not be fetched from the server
30
31             /* by default, don't show auto-generated (sequence) fields */
32             showSequenceFields : false, 
33
34             startup : function() {
35                 this.selectionMode = 'single';
36                 this.sequence = openils.widget.AutoGrid.sequence++;
37                 openils.widget.AutoGrid.gridCache[this.sequence] = this;
38                 this.inherited(arguments);
39                 this.initAutoEnv();
40                 this.attr('structure', this._compileStructure());
41                 this.setStore(this.buildAutoStore());
42                 this.cachedQueryOpts = {};
43
44                 if(this.showColumnPicker) {
45                     if(!this.columnPickerPrefix) {
46                         console.error("No columnPickerPrefix defined");
47                     } else {
48                         var picker = new openils.widget.GridColumnPicker(
49                             openils.User.authtoken, this.columnPickerPrefix, this);
50                         if(openils.User.authtoken) {
51                             picker.load();
52                         } else {
53                             openils.Util.addOnLoad(function() { picker.load() });
54                         }
55                     }
56                 }
57
58                 this.overrideEditWidgets = {};
59                 this.overrideEditWidgetClass = {};
60
61                 if(this.editOnEnter) 
62                     this._applyEditOnEnter();
63                 else if(this.singleEditStyle) 
64                     this._applySingleEditStyle();
65
66                 if(!this.hideSelector) {
67                     dojo.connect(this, 'onHeaderCellClick', 
68                         function(e) {
69                             if(e.cell.index == 0)
70                                 this.toggleSelectAll();
71                         }
72                     );
73                 }
74
75                 if(this.showPaginator) {
76                     var self = this;
77                     this.paginator = new dijit.layout.ContentPane();
78
79
80                     var back = dojo.create('a', {
81                         innerHTML : 'Back', 
82                         style : 'padding-right:6px;',
83                         href : 'javascript:void(0);', 
84                         onclick : function() { 
85                             self.resetStore();
86                             self.cachedQueryOpts.offset = self.displayOffset -= self.displayLimit;
87                             if(self.dataLoader)
88                                 self.dataLoader()
89                             else
90                                 self.loadAll(self.cachedQueryOpts, self.cachedQuerySearch);
91                         }
92                     });
93
94                     var forw = dojo.create('a', {
95                         innerHTML : 'Next', 
96                         style : 'padding-right:6px;',
97                         href : 'javascript:void(0);', 
98                         onclick : function() { 
99                             self.resetStore();
100                             self.cachedQueryOpts.offset = self.displayOffset += self.displayLimit;
101                             if(self.dataLoader)
102                                 self.dataLoader()
103                             else
104                                 self.loadAll(self.cachedQueryOpts, self.cachedQuerySearch);
105                         }
106                     });
107
108                     dojo.place(this.paginator.domNode, this.domNode, 'before');
109                     dojo.place(back, this.paginator.domNode);
110                     dojo.place(forw, this.paginator.domNode);
111                     this.loadProgressIndicator = dojo.create('img', {
112                         src:'/opac/images/progressbar_green.gif',
113                         style:'height:16px;width:16px;'
114                     });
115                     dojo.place(this.loadProgressIndicator, this.paginator.domNode);
116                 }
117             },
118
119             hideLoadProgressIndicator : function() {
120                 dojo.style(this.loadProgressIndicator, 'visibility', 'hidden');
121             },
122
123             showLoadProgressIndicator : function() {
124                 dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
125             },
126
127             /* Don't allow sorting on the selector column */
128             canSort : function(rowIdx) {
129                 if(rowIdx == 1 && !this.hideSelector)
130                     return false;
131                 return true;
132             },
133
134             _compileStructure : function() {
135                 var existing = (this.structure && this.structure[0].cells[0]) ? 
136                     this.structure[0].cells[0] : [];
137                 var fields = [];
138
139                 var self = this;
140                 function pushEntry(entry) {
141                     if(self.suppressFields) {
142                         if(dojo.indexOf(self.suppressFields, entry.field) != -1)
143                             return;
144                     }
145                     if(!entry.get) 
146                         entry.get = openils.widget.AutoGrid.defaultGetter
147                     if(!entry.width && self.defaultCellWidth)
148                         entry.width = self.defaultCellWidth;
149                     fields.push(entry);
150                 }
151
152                 if(!this.hideSelector) {
153                     // insert the selector column
154                     pushEntry({
155                         field : '+selector',
156                         formatter : function(rowIdx) { return self._formatRowSelectInput(rowIdx); },
157                         get : function(rowIdx, item) { if(item) return rowIdx; },
158                         width : this.selectorWidth,
159                         name : '&#x2713',
160                         nonSelectable : true
161                     });
162                 }
163
164
165                 if(!this.fieldOrder) {
166                     /* no order defined, start with any explicit grid fields */
167                     for(var e in existing) {
168                         var entry = existing[e];
169                         var field = this.fmIDL.fields.filter(
170                             function(i){return (i.name == entry.field)})[0];
171                         if(field) entry.name = entry.name || field.label;
172                         pushEntry(entry);
173                     }
174                 }
175
176                 for(var f in this.sortedFieldList) {
177                     var field = this.sortedFieldList[f];
178                     if(!field || field.virtual) continue;
179                     
180                     // field was already added above
181                     if(fields.filter(function(i){return (i.field == field.name)})[0]) 
182                         continue;
183
184                     var entry = existing.filter(function(i){return (i.field == field.name)})[0];
185                     if(entry) {
186                         entry.name = field.label;
187                     } else {
188                         // unless specifically requested, hide sequence fields
189                         if(!this.showSequenceFields && field.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
190                             continue; 
191
192                         entry = {field:field.name, name:field.label};
193                     }
194                     pushEntry(entry);
195                 }
196
197                 if(this.fieldOrder) {
198                     /* append any explicit non-IDL grid fields to the end */
199                     for(var e in existing) {
200                         var entry = existing[e];
201                         var field = fields.filter(
202                             function(i){return (i.field == entry.field)})[0];
203                         if(field) continue; // don't duplicate
204                         pushEntry(entry);
205                     }
206                 }
207
208                 return [{cells: [fields]}];
209             },
210
211             toggleSelectAll : function() {
212                 var selected = this.getSelectedRows();
213                 for(var i = 0; i < this.rowCount; i++) {
214                     if(selected[0])
215                         this.deSelectRow(i);
216                     else
217                         this.selectRow(i);
218                 }
219             },
220
221             getSelectedRows : function() {
222                 var rows = []; 
223                 dojo.forEach(
224                     dojo.query('[name=autogrid.selector]', this.domNode),
225                     function(input) {
226                         if(input.checked)
227                             rows.push(input.getAttribute('row'));
228                     }
229                 );
230                 return rows;
231             },
232
233             getFirstSelectedRow : function() {
234                 return this.getSelectedRows()[0];
235             },
236
237             getSelectedItems : function() {
238                 var items = [];
239                 var self = this;
240                 dojo.forEach(this.getSelectedRows(), function(idx) { items.push(self.getItem(idx)); });
241                 return items;
242             },
243
244             selectRow : function(rowIdx) {
245                 var inputs = dojo.query('[name=autogrid.selector]', this.domNode);
246                 for(var i = 0; i < inputs.length; i++) {
247                     if(inputs[i].getAttribute('row') == rowIdx) {
248                         if(!inputs[i].disabled)
249                             inputs[i].checked = true;
250                         break;
251                     }
252                 }
253             },
254
255             deSelectRow : function(rowIdx) {
256                 var inputs = dojo.query('[name=autogrid.selector]', this.domNode);
257                 for(var i = 0; i < inputs.length; i++) {
258                     if(inputs[i].getAttribute('row') == rowIdx) {
259                         inputs[i].checked = false;
260                         break;
261                     }
262                 }
263             },
264
265             getAllObjects : function() {
266                 var objs = [];
267                 var self = this;
268                 this.store.fetch({
269                     onComplete : function(list) {
270                         dojo.forEach(list, 
271                             function(item) {
272                                 objs.push(new fieldmapper[self.fmClass]().fromStoreItem(item));
273                             }
274                         )
275                     }
276                 });
277                 return objs;
278             },
279
280             deleteSelected : function() {
281                 var items = this.getSelectedItems();
282                 var total = items.length;
283                 var self = this;
284                 dojo.require('openils.PermaCrud');
285                 var pcrud = new openils.PermaCrud();
286                 dojo.forEach(items,
287                     function(item) {
288                         var fmObject = new fieldmapper[self.fmClass]().fromStoreItem(item);
289                         pcrud['delete'](fmObject, {oncomplete : function(r) { self.store.deleteItem(item) }});
290                     }
291                 );
292             },
293
294             _formatRowSelectInput : function(rowIdx) {
295                 if(rowIdx === null || rowIdx === undefined) return '';
296                 var s = "<input type='checkbox' name='autogrid.selector' row='" + rowIdx + "'";
297                 if(this.disableSelectorForRow && this.disableSelectorForRow(rowIdx)) 
298                     s += " disabled='disabled'";
299                 return s + "/>";
300             },
301
302             _applySingleEditStyle : function() {
303                 this.onMouseOverRow = function(e) {};
304                 this.onMouseOutRow = function(e) {};
305                 this.onCellFocus = function(cell, rowIndex) { 
306                     this.selection.deselectAll();
307                     this.selection.select(this.focus.rowIndex);
308                 };
309             },
310
311             /* capture keydown and launch edit dialog on enter */
312             _applyEditOnEnter : function() {
313                 this._applySingleEditStyle();
314
315                 dojo.connect(this, 'onRowDblClick',
316                     function(e) {
317                         if(this.editStyle == 'pane')
318                             this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
319                         else
320                             this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
321                     }
322                 );
323
324                 dojo.connect(this, 'onKeyDown',
325                     function(e) {
326                         if(e.keyCode == dojo.keys.ENTER) {
327                             this.selection.deselectAll();
328                             this.selection.select(this.focus.rowIndex);
329                             if(this.editStyle == 'pane')
330                                 this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
331                             else
332                                 this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
333                         }
334                     }
335                 );
336             },
337
338             _makeEditPane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
339                 var grid = this;
340                 var fmObject = new fieldmapper[this.fmClass]().fromStoreItem(storeItem);
341                 var idents = grid.store.getIdentityAttributes();
342
343                 var pane = new openils.widget.EditPane({
344                     fmObject:fmObject,
345                     overrideWidgets : this.overrideEditWidgets,
346                     overrideWidgetClass : this.overrideEditWidgetClass,
347                     disableWidgetTest : this.disableWidgetTest,
348                     onPostSubmit : function() {
349                         for(var i in fmObject._fields) {
350                             var field = fmObject._fields[i];
351                             if(idents.filter(function(j){return (j == field)})[0])
352                                 continue; // don't try to edit an identifier field
353                             grid.store.setValue(storeItem, field, fmObject[field]());
354                         }
355                         if(self.onPostUpdate)
356                             self.onPostUpdate(storeItem, rowIndex);
357                         setTimeout(
358                             function(){
359                                 try { 
360                                     grid.views.views[0].getCellNode(rowIndex, 0).focus(); 
361                                 } catch (E) {}
362                             },200
363                         );
364                         if(onPostSubmit) onPostSubmit();
365                     },
366                     onCancel : function() {
367                         setTimeout(function(){
368                             grid.views.views[0].getCellNode(rowIndex, 0).focus();},200);
369                         if(onCancel) onCancel();
370                     }
371                 });
372
373                 pane.fieldOrder = this.fieldOrder;
374                 pane.mode = 'update';
375                 return pane;
376             },
377
378             _makeCreatePane : function(onPostSubmit, onCancel) {
379                 var grid = this;
380                 var pane = new openils.widget.EditPane({
381                     fmClass : this.fmClass,
382                     overrideWidgets : this.overrideEditWidgets,
383                     overrideWidgetClass : this.overrideEditWidgetClass,
384                     disableWidgetTest : this.disableWidgetTest,
385                     onPostSubmit : function(r) {
386                         var fmObject = openils.Util.readResponse(r);
387                         if(grid.onPostCreate)
388                             grid.onPostCreate(fmObject);
389                         if(fmObject) 
390                             grid.store.newItem(fmObject.toStoreItem());
391                         setTimeout(function(){
392                             try {
393                                 grid.selection.select(grid.rowCount-1);
394                                 grid.views.views[0].getCellNode(grid.rowCount-1, 1).focus();
395                             } catch (E) {}
396                         },200);
397                         if(onPostSubmit)
398                             onPostSubmit();
399                     },
400                     onCancel : function() {
401                         if(onCancel) onCancel();
402                     }
403                 });
404                 pane.fieldOrder = this.fieldOrder;
405                 pane.mode = 'create';
406                 return pane;
407             },
408
409             // .startup() is called within
410             _makeClonePane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
411                 var clonePane = this._makeCreatePane(onPostSubmit, onCancel);
412                 var origPane = this._makeEditPane(storeItem, rowIndex);
413                 clonePane.startup();
414                 origPane.startup();
415                 dojo.forEach(origPane.fieldList,
416                     function(field) {
417                         if(field.widget.widget.attr('disabled')) return;
418                         var w = clonePane.fieldList.filter(
419                             function(i) { return (i.name == field.name) })[0];
420                         w.widget.baseWidgetValue(field.widget.widgetValue); // sync widgets
421                         w.widget.onload = function(){w.widget.baseWidgetValue(field.widget.widgetValue)}; // async widgets
422                     }
423                 );
424                 origPane.destroy();
425                 return clonePane;
426             },
427
428
429             _drawEditDialog : function(storeItem, rowIndex) {
430                 var self = this;
431                 var done = function() { self.hideDialog(); };
432                 var pane = this._makeEditPane(storeItem, rowIndex, done, done);
433                 this.editDialog = new openils.widget.EditDialog({editPane:pane});
434                 this.editDialog.startup();
435                 this.editDialog.show();
436             },
437
438             showCreateDialog : function() {
439                 var self = this;
440                 var done = function() { self.hideDialog(); };
441                 var pane = this._makeCreatePane(done, done);
442                 this.editDialog = new openils.widget.EditDialog({editPane:pane});
443                 this.editDialog.startup();
444                 this.editDialog.show();
445             },
446
447             _drawEditPane : function(storeItem, rowIndex) {
448                 var self = this;
449                 var done = function() { self.hidePane(); };
450                 dojo.style(this.domNode, 'display', 'none');
451                 this.editPane = this._makeEditPane(storeItem, rowIndex, done, done);
452                 this.editPane.startup();
453                 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
454                 if(this.onEditPane) this.onEditPane(this.editPane);
455             },
456
457             showClonePane : function() {
458                 var self = this;
459                 var done = function() { self.hidePane(); };
460                 var row = this.getFirstSelectedRow();
461                 if(!row) return;
462                 dojo.style(this.domNode, 'display', 'none');
463                 this.editPane = this._makeClonePane(this.getItem(row), row, done, done);
464                 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
465                 if(this.onEditPane) this.onEditPane(this.editPane);
466             },
467
468             showCreatePane : function() {
469                 var self = this;
470                 var done = function() { self.hidePane(); };
471                 dojo.style(this.domNode, 'display', 'none');
472                 this.editPane = this._makeCreatePane(done, done);
473                 this.editPane.startup();
474                 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
475                 if(this.onEditPane) this.onEditPane(this.editPane);
476             },
477
478             hideDialog : function() {
479                 this.editDialog.hide(); 
480                 this.editDialog.destroy(); 
481                 delete this.editDialog;
482                 this.update();
483             },
484
485             hidePane : function() {
486                 this.domNode.parentNode.removeChild(this.editPane.domNode);
487                 this.editPane.destroy();
488                 delete this.editPane;
489                 dojo.style(this.domNode, 'display', 'block');
490                 this.update();
491             },
492             
493             resetStore : function() {
494                 this.setStore(this.buildAutoStore());
495             },
496
497             loadAll : function(opts, search) {
498                 dojo.require('openils.PermaCrud');
499                 if(this.loadProgressIndicator)
500                     dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
501                 var self = this;
502                 opts = dojo.mixin(
503                     {limit : this.displayLimit, offset : this.displayOffset}, 
504                     opts || {}
505                 );
506                 opts = dojo.mixin(opts, {
507                     async : true,
508                     streaming : true,
509                     onresponse : function(r) {
510                         var item = openils.Util.readResponse(r);
511                         self.store.newItem(item.toStoreItem());
512                     },
513                     oncomplete : function() {
514                         if(self.loadProgressIndicator) 
515                             dojo.style(self.loadProgressIndicator, 'visibility', 'hidden');
516                     }
517                 });
518
519                 this.cachedQuerySearch = search;
520                 this.cachedQueryOpts = opts;
521                 if(search)
522                     new openils.PermaCrud().search(this.fmClass, search, opts);
523                 else
524                     new openils.PermaCrud().retrieveAll(this.fmClass, opts);
525             }
526         } 
527     );
528
529     // static ID generater seed
530     openils.widget.AutoGrid.sequence = 0;
531     openils.widget.AutoGrid.gridCache = {};
532
533     openils.widget.AutoGrid.markupFactory = dojox.grid.DataGrid.markupFactory;
534
535     openils.widget.AutoGrid.defaultGetter = function(rowIndex, item) {
536         if(!item) return '';
537         var val = this.grid.store.getValue(item, this.field);
538         var autoWidget = new openils.widget.AutoFieldWidget({
539             fmClass: this.grid.fmClass,
540             fmField: this.field,
541             widgetValue : val,
542             readOnly : true,
543             suppressLinkedFields : this.grid.suppressLinkedFields
544         });
545
546         var _this = this;
547         autoWidget.build(
548             function(w, ww) {
549                 var node = _this.grid.getCell(_this.index).view.getCellNode(rowIndex, _this.index);
550                 if(node) {
551                     node.innerHTML = ww.getDisplayString();
552                 }
553             }
554         );
555
556         return autoWidget.getDisplayString();
557     }
558 }
559