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