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