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