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');
13 'openils.widget.AutoGrid',
14 [dojox.grid.DataGrid, openils.widget.AutoWidget],
17 /* if true, pop up an edit dialog when user hits Enter on a give row */
19 defaultCellWidth : null,
21 suppressFields : null,
23 selectorWidth : '1.5',
24 showColumnPicker : false,
25 columnPickerPrefix : null,
28 showPaginator : false,
29 showLoadFilter : false,
30 suppressLinkedFields : null, // list of fields whose linked display data should not be fetched from the server
32 /* by default, don't show auto-generated (sequence) fields */
33 showSequenceFields : false,
35 startup : function() {
36 this.selectionMode = 'single';
37 this.sequence = openils.widget.AutoGrid.sequence++;
38 openils.widget.AutoGrid.gridCache[this.sequence] = this;
39 this.inherited(arguments);
41 this.attr('structure', this._compileStructure());
42 this.setStore(this.buildAutoStore());
43 this.cachedQueryOpts = {};
45 if(this.showColumnPicker) {
46 if(!this.columnPickerPrefix) {
47 console.error("No columnPickerPrefix defined");
49 var picker = new openils.widget.GridColumnPicker(
50 openils.User.authtoken, this.columnPickerPrefix, this);
51 if(openils.User.authtoken) {
54 openils.Util.addOnLoad(function() { picker.load() });
59 this.overrideEditWidgets = {};
60 this.overrideEditWidgetClass = {};
63 this._applyEditOnEnter();
64 else if(this.singleEditStyle)
65 this._applySingleEditStyle();
67 if(!this.hideSelector) {
68 dojo.connect(this, 'onHeaderCellClick',
71 this.toggleSelectAll();
76 if(this.showPaginator) {
78 this.paginator = new dijit.layout.ContentPane();
81 var back = dojo.create('a', {
82 innerHTML : 'Back', // TODO i18n
83 style : 'padding-right:6px;',
84 href : 'javascript:void(0);',
85 onclick : function() {
87 self.cachedQueryOpts.offset = self.displayOffset -= self.displayLimit;
91 self.loadAll(self.cachedQueryOpts, self.cachedQuerySearch);
95 var forw = dojo.create('a', {
96 innerHTML : 'Next', // TODO i18n
97 style : 'padding-right:6px;',
98 href : 'javascript:void(0);',
99 onclick : function() {
101 self.cachedQueryOpts.offset = self.displayOffset += self.displayLimit;
105 self.loadAll(self.cachedQueryOpts, self.cachedQuerySearch);
109 dojo.place(this.paginator.domNode, this.domNode, 'before');
110 dojo.place(back, this.paginator.domNode);
111 dojo.place(forw, this.paginator.domNode);
113 if(this.showLoadFilter) {
114 dojo.require('openils.widget.PCrudFilterDialog');
117 innerHTML : 'Filter', // TODO i18n
118 style : 'padding-right:6px;',
119 href : 'javascript:void(0);',
120 onclick : function() {
121 var dialog = new openils.widget.PCrudFilterDialog({fmClass:self.fmClass})
122 dialog.onApply = function(filter) {
124 self.loadAll(self.cachedQueryOpts, filter);
130 this.paginator.domNode
135 this.loadProgressIndicator = dojo.create('img', {
136 src:'/opac/images/progressbar_green.gif', // TODO configured path
137 style:'height:16px;width:16px;'
139 dojo.place(this.loadProgressIndicator, this.paginator.domNode);
143 hideLoadProgressIndicator : function() {
144 dojo.style(this.loadProgressIndicator, 'visibility', 'hidden');
147 showLoadProgressIndicator : function() {
148 dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
151 /* Don't allow sorting on the selector column */
152 canSort : function(rowIdx) {
153 if(rowIdx == 1 && !this.hideSelector)
158 _compileStructure : function() {
159 var existing = (this.structure && this.structure[0].cells[0]) ?
160 this.structure[0].cells[0] : [];
164 function pushEntry(entry) {
165 if(self.suppressFields) {
166 if(dojo.indexOf(self.suppressFields, entry.field) != -1)
170 entry.get = openils.widget.AutoGrid.defaultGetter
171 if(!entry.width && self.defaultCellWidth)
172 entry.width = self.defaultCellWidth;
176 if(!this.hideSelector) {
177 // insert the selector column
180 formatter : function(rowIdx) { return self._formatRowSelectInput(rowIdx); },
181 get : function(rowIdx, item) { if(item) return rowIdx; },
182 width : this.selectorWidth,
189 if(!this.fieldOrder) {
190 /* no order defined, start with any explicit grid fields */
191 for(var e in existing) {
192 var entry = existing[e];
193 var field = this.fmIDL.fields.filter(
194 function(i){return (i.name == entry.field)})[0];
195 if(field) entry.name = entry.name || field.label;
200 for(var f in this.sortedFieldList) {
201 var field = this.sortedFieldList[f];
202 if(!field || field.virtual) continue;
204 // field was already added above
205 if(fields.filter(function(i){return (i.field == field.name)})[0])
208 var entry = existing.filter(function(i){return (i.field == field.name)})[0];
210 entry.name = field.label;
212 // unless specifically requested, hide sequence fields
213 if(!this.showSequenceFields && field.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
216 entry = {field:field.name, name:field.label};
221 if(this.fieldOrder) {
222 /* append any explicit non-IDL grid fields to the end */
223 for(var e in existing) {
224 var entry = existing[e];
225 var field = fields.filter(
226 function(i){return (i.field == entry.field)})[0];
227 if(field) continue; // don't duplicate
232 return [{cells: [fields]}];
235 toggleSelectAll : function() {
236 var selected = this.getSelectedRows();
237 for(var i = 0; i < this.rowCount; i++) {
245 getSelectedRows : function() {
248 dojo.query('[name=autogrid.selector]', this.domNode),
251 rows.push(input.getAttribute('row'));
257 getFirstSelectedRow : function() {
258 return this.getSelectedRows()[0];
261 getSelectedItems : function() {
264 dojo.forEach(this.getSelectedRows(), function(idx) { items.push(self.getItem(idx)); });
268 selectRow : function(rowIdx) {
269 var inputs = dojo.query('[name=autogrid.selector]', this.domNode);
270 for(var i = 0; i < inputs.length; i++) {
271 if(inputs[i].getAttribute('row') == rowIdx) {
272 if(!inputs[i].disabled)
273 inputs[i].checked = true;
279 deSelectRow : function(rowIdx) {
280 var inputs = dojo.query('[name=autogrid.selector]', this.domNode);
281 for(var i = 0; i < inputs.length; i++) {
282 if(inputs[i].getAttribute('row') == rowIdx) {
283 inputs[i].checked = false;
289 getAllObjects : function() {
293 onComplete : function(list) {
296 objs.push(new fieldmapper[self.fmClass]().fromStoreItem(item));
304 deleteSelected : function() {
305 var items = this.getSelectedItems();
306 var total = items.length;
308 dojo.require('openils.PermaCrud');
309 var pcrud = new openils.PermaCrud();
312 var fmObject = new fieldmapper[self.fmClass]().fromStoreItem(item);
313 pcrud['delete'](fmObject, {oncomplete : function(r) { self.store.deleteItem(item) }});
318 _formatRowSelectInput : function(rowIdx) {
319 if(rowIdx === null || rowIdx === undefined) return '';
320 var s = "<input type='checkbox' name='autogrid.selector' row='" + rowIdx + "'";
321 if(this.disableSelectorForRow && this.disableSelectorForRow(rowIdx))
322 s += " disabled='disabled'";
326 _applySingleEditStyle : function() {
327 this.onMouseOverRow = function(e) {};
328 this.onMouseOutRow = function(e) {};
329 this.onCellFocus = function(cell, rowIndex) {
330 this.selection.deselectAll();
331 this.selection.select(this.focus.rowIndex);
335 /* capture keydown and launch edit dialog on enter */
336 _applyEditOnEnter : function() {
337 this._applySingleEditStyle();
339 dojo.connect(this, 'onRowDblClick',
341 if(this.editStyle == 'pane')
342 this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
344 this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
348 dojo.connect(this, 'onKeyDown',
350 if(e.keyCode == dojo.keys.ENTER) {
351 this.selection.deselectAll();
352 this.selection.select(this.focus.rowIndex);
353 if(this.editStyle == 'pane')
354 this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
356 this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
362 _makeEditPane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
364 var fmObject = new fieldmapper[this.fmClass]().fromStoreItem(storeItem);
365 var idents = grid.store.getIdentityAttributes();
367 var pane = new openils.widget.EditPane({
369 overrideWidgets : this.overrideEditWidgets,
370 overrideWidgetClass : this.overrideEditWidgetClass,
371 disableWidgetTest : this.disableWidgetTest,
372 onPostSubmit : function() {
373 for(var i in fmObject._fields) {
374 var field = fmObject._fields[i];
375 if(idents.filter(function(j){return (j == field)})[0])
376 continue; // don't try to edit an identifier field
377 grid.store.setValue(storeItem, field, fmObject[field]());
379 if(self.onPostUpdate)
380 self.onPostUpdate(storeItem, rowIndex);
384 grid.views.views[0].getCellNode(rowIndex, 0).focus();
388 if(onPostSubmit) onPostSubmit();
390 onCancel : function() {
391 setTimeout(function(){
392 grid.views.views[0].getCellNode(rowIndex, 0).focus();},200);
393 if(onCancel) onCancel();
397 pane.fieldOrder = this.fieldOrder;
398 pane.mode = 'update';
402 _makeCreatePane : function(onPostSubmit, onCancel) {
404 var pane = new openils.widget.EditPane({
405 fmClass : this.fmClass,
406 overrideWidgets : this.overrideEditWidgets,
407 overrideWidgetClass : this.overrideEditWidgetClass,
408 disableWidgetTest : this.disableWidgetTest,
409 onPostSubmit : function(r) {
410 var fmObject = openils.Util.readResponse(r);
411 if(grid.onPostCreate)
412 grid.onPostCreate(fmObject);
414 grid.store.newItem(fmObject.toStoreItem());
415 setTimeout(function(){
417 grid.selection.select(grid.rowCount-1);
418 grid.views.views[0].getCellNode(grid.rowCount-1, 1).focus();
424 onCancel : function() {
425 if(onCancel) onCancel();
428 pane.fieldOrder = this.fieldOrder;
429 pane.mode = 'create';
433 // .startup() is called within
434 _makeClonePane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
435 var clonePane = this._makeCreatePane(onPostSubmit, onCancel);
436 var origPane = this._makeEditPane(storeItem, rowIndex);
439 dojo.forEach(origPane.fieldList,
441 if(field.widget.widget.attr('disabled')) return;
442 var w = clonePane.fieldList.filter(
443 function(i) { return (i.name == field.name) })[0];
444 w.widget.baseWidgetValue(field.widget.widgetValue); // sync widgets
445 w.widget.onload = function(){w.widget.baseWidgetValue(field.widget.widgetValue)}; // async widgets
453 _drawEditDialog : function(storeItem, rowIndex) {
455 var done = function() { self.hideDialog(); };
456 var pane = this._makeEditPane(storeItem, rowIndex, done, done);
457 this.editDialog = new openils.widget.EditDialog({editPane:pane});
458 this.editDialog.startup();
459 this.editDialog.show();
462 showCreateDialog : function() {
464 var done = function() { self.hideDialog(); };
465 var pane = this._makeCreatePane(done, done);
466 this.editDialog = new openils.widget.EditDialog({editPane:pane});
467 this.editDialog.startup();
468 this.editDialog.show();
471 _drawEditPane : function(storeItem, rowIndex) {
473 var done = function() { self.hidePane(); };
474 dojo.style(this.domNode, 'display', 'none');
475 this.editPane = this._makeEditPane(storeItem, rowIndex, done, done);
476 this.editPane.startup();
477 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
478 if(this.onEditPane) this.onEditPane(this.editPane);
481 showClonePane : function() {
483 var done = function() { self.hidePane(); };
484 var row = this.getFirstSelectedRow();
486 dojo.style(this.domNode, 'display', 'none');
487 this.editPane = this._makeClonePane(this.getItem(row), row, done, done);
488 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
489 if(this.onEditPane) this.onEditPane(this.editPane);
492 showCreatePane : function() {
494 var done = function() { self.hidePane(); };
495 dojo.style(this.domNode, 'display', 'none');
496 this.editPane = this._makeCreatePane(done, done);
497 this.editPane.startup();
498 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
499 if(this.onEditPane) this.onEditPane(this.editPane);
502 hideDialog : function() {
503 this.editDialog.hide();
504 this.editDialog.destroy();
505 delete this.editDialog;
509 hidePane : function() {
510 this.domNode.parentNode.removeChild(this.editPane.domNode);
511 this.editPane.destroy();
512 delete this.editPane;
513 dojo.style(this.domNode, 'display', 'block');
517 resetStore : function() {
518 this.setStore(this.buildAutoStore());
521 loadAll : function(opts, search) {
522 dojo.require('openils.PermaCrud');
523 if(this.loadProgressIndicator)
524 dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
527 {limit : this.displayLimit, offset : this.displayOffset},
530 opts = dojo.mixin(opts, {
533 onresponse : function(r) {
534 var item = openils.Util.readResponse(r);
535 self.store.newItem(item.toStoreItem());
537 oncomplete : function() {
538 if(self.loadProgressIndicator)
539 dojo.style(self.loadProgressIndicator, 'visibility', 'hidden');
543 this.cachedQuerySearch = search;
544 this.cachedQueryOpts = opts;
546 new openils.PermaCrud().search(this.fmClass, search, opts);
548 new openils.PermaCrud().retrieveAll(this.fmClass, opts);
553 // static ID generater seed
554 openils.widget.AutoGrid.sequence = 0;
555 openils.widget.AutoGrid.gridCache = {};
557 openils.widget.AutoGrid.markupFactory = dojox.grid.DataGrid.markupFactory;
559 openils.widget.AutoGrid.defaultGetter = function(rowIndex, item) {
561 var val = this.grid.store.getValue(item, this.field);
562 var autoWidget = new openils.widget.AutoFieldWidget({
563 fmClass: this.grid.fmClass,
567 forceSync : true, // prevents many simultaneous requests for the same data
568 suppressLinkedFields : this.grid.suppressLinkedFields
574 // With proper caching, this should not be necessary to prevent grid render flickering
579 var node = _this.grid.getCell(_this.index).view.getCellNode(rowIndex, _this.index);
581 node.innerHTML = ww.getDisplayString();
587 return autoWidget.getDisplayString();