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 */
18 editPaneOnSubmit : null,
19 createPaneOnSubmit : null,
21 defaultCellWidth : null,
24 suppressFields : null,
25 suppressEditFields : null,
26 suppressFilterFields : null,
28 hideLineNumber : false,
29 selectorWidth : '1.5',
30 lineNumberWidth : '1.5',
31 showColumnPicker : false,
32 columnPickerPrefix : null,
35 requiredFields : null,
36 hidePaginator : false,
37 showLoadFilter : false,
38 suppressLinkedFields : null, // list of fields whose linked display data should not be fetched from the server
40 /* by default, don't show auto-generated (sequence) fields */
41 showSequenceFields : false,
43 // style the cells in the line number column
44 onStyleRow : function(row) {
45 if (!this.hideLineNumber) {
46 var cellIdx = this.hideSelector ? 0 : 1;
47 dojo.addClass(this.views.views[0].getCellNode(row.index, cellIdx), 'autoGridLineNumber');
51 startup : function() {
52 this.selectionMode = 'single';
53 this.sequence = openils.widget.AutoGrid.sequence++;
54 openils.widget.AutoGrid.gridCache[this.sequence] = this;
55 this.inherited(arguments);
57 this.attr('structure', this._compileStructure());
58 this.setStore(this.buildAutoStore());
59 this.cachedQueryOpts = {};
60 this._showing_create_pane = false;
62 if(this.showColumnPicker) {
63 if(!this.columnPickerPrefix) {
64 console.error("No columnPickerPrefix defined");
66 var picker = new openils.widget.GridColumnPicker(
67 openils.User.authtoken, this.columnPickerPrefix, this);
68 if(openils.User.authtoken) {
71 openils.Util.addOnLoad(function() { picker.load() });
76 this.overrideEditWidgets = {};
77 this.overrideEditWidgetClass = {};
78 this.overrideWidgetArgs = {};
81 this._applyEditOnEnter();
82 else if(this.singleEditStyle)
83 this._applySingleEditStyle();
85 if(!this.hideSelector) {
86 dojo.connect(this, 'onHeaderCellClick',
89 this.toggleSelectAll();
94 if(!this.hidePaginator) {
96 this.paginator = new dijit.layout.ContentPane();
99 var back = dojo.create('a', {
100 innerHTML : 'Back', // TODO i18n
101 style : 'padding-right:6px;',
102 href : 'javascript:void(0);',
103 onclick : function() {
104 self.cachedQueryOpts.offset = self.displayOffset -= self.displayLimit;
105 if(self.displayOffset < 0)
106 self.cachedQueryOpts.offset = self.displayOffset = 0;
111 var forw = dojo.create('a', {
112 innerHTML : 'Next', // TODO i18n
113 style : 'padding-right:6px;',
114 href : 'javascript:void(0);',
115 onclick : function() {
116 self.cachedQueryOpts.offset = self.displayOffset += self.displayLimit;
121 dojo.place(this.paginator.domNode, this.domNode, 'before');
122 dojo.place(back, this.paginator.domNode);
123 dojo.place(forw, this.paginator.domNode);
125 if(this.showLoadFilter) {
126 dojo.require('openils.widget.PCrudFilterDialog');
129 innerHTML : 'Filter', // TODO i18n
130 style : 'padding-right:6px;',
131 href : 'javascript:void(0);',
132 onclick : function() {
133 if (!self.filterDialog) {
134 self.filterDialog = new openils.widget.PCrudFilterDialog({fmClass:self.fmClass, suppressFilterFields:self.suppressFilterFields})
135 self.filterDialog.onApply = function(filter) {
137 self.loadAll(self.cachedQueryOpts, filter);
139 self.filterDialog.startup();
141 self.filterDialog.show();
144 this.paginator.domNode
149 this.loadProgressIndicator = dojo.create('img', {
150 src:'/opac/images/progressbar_green.gif', // TODO configured path
151 style:'height:16px;width:16px;'
153 dojo.place(this.loadProgressIndicator, this.paginator.domNode);
157 hideLoadProgressIndicator : function() {
158 dojo.style(this.loadProgressIndicator, 'visibility', 'hidden');
161 showLoadProgressIndicator : function() {
162 dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
165 /* Don't allow sorting on the selector column */
166 canSort : function(rowIdx) {
167 if(rowIdx == 1 && !this.hideSelector)
169 if(this.hideSelector && rowIdx == 1 && !this.hideLineNumber)
171 if(!this.hideSelector && rowIdx == 2 && !this.hideLineNumber)
176 _compileStructure : function() {
177 var existing = (this.structure && this.structure[0].cells[0]) ?
178 this.structure[0].cells[0] : [];
182 function pushEntry(entry) {
183 if(self.suppressFields) {
184 if(dojo.indexOf(self.suppressFields, entry.field) != -1)
188 entry.get = openils.widget.AutoGrid.defaultGetter
189 if(!entry.width && self.defaultCellWidth)
190 entry.width = self.defaultCellWidth;
194 if(!this.hideSelector) {
195 // insert the selector column
198 formatter : function(rowIdx) { return self._formatRowSelectInput(rowIdx); },
199 get : function(rowIdx, item) { if(item) return rowIdx; },
200 width : this.selectorWidth,
206 if(!this.hideLineNumber) {
207 // insert the line number column
210 get : function(rowIdx, item) { if(item) return 1 + rowIdx; },
211 width : this.lineNumberWidth,
213 nonSelectable : false
217 if(!this.fieldOrder) {
218 /* no order defined, start with any explicit grid fields */
219 for(var e in existing) {
220 var entry = existing[e];
221 var field = this.fmIDL.fields.filter(
222 function(i){return (i.name == entry.field)})[0];
223 if(field) entry.name = entry.name || field.label;
228 for(var f in this.sortedFieldList) {
229 var field = this.sortedFieldList[f];
230 if(!field || field.virtual) continue;
232 // field was already added above
233 if(fields.filter(function(i){return (i.field == field.name)})[0])
236 var entry = existing.filter(function(i){return (i.field == field.name)})[0];
238 entry.name = entry.name || field.label;
240 // unless specifically requested, hide sequence fields
241 if(!this.showSequenceFields && field.name == this.fmIDL.pkey && this.fmIDL.pkey_sequence)
244 entry = {field:field.name, name:field.label};
249 if(this.fieldOrder) {
250 /* append any explicit non-IDL grid fields to the end */
251 for(var e in existing) {
252 var entry = existing[e];
253 var field = fields.filter(
254 function(i){return (i.field == entry.field)})[0];
255 if(field) continue; // don't duplicate
260 return [{cells: [fields]}];
263 toggleSelectAll : function() {
264 var selected = this.getSelectedRows();
265 for(var i = 0; i < this.rowCount; i++) {
273 getSelectedRows : function() {
276 dojo.query('[name=autogrid.selector]', this.domNode),
279 rows.push(input.getAttribute('row'));
285 getFirstSelectedRow : function() {
286 return this.getSelectedRows()[0];
289 getSelectedItems : function() {
292 dojo.forEach(this.getSelectedRows(), function(idx) { items.push(self.getItem(idx)); });
296 selectRow : function(rowIdx) {
297 var inputs = dojo.query('[name=autogrid.selector]', this.domNode);
298 for(var i = 0; i < inputs.length; i++) {
299 if(inputs[i].getAttribute('row') == rowIdx) {
300 if(!inputs[i].disabled)
301 inputs[i].checked = true;
307 deSelectRow : function(rowIdx) {
308 var inputs = dojo.query('[name=autogrid.selector]', this.domNode);
309 for(var i = 0; i < inputs.length; i++) {
310 if(inputs[i].getAttribute('row') == rowIdx) {
311 inputs[i].checked = false;
318 * @return {Array} List of every fieldmapper object in the data store
320 getAllObjects : function() {
324 onComplete : function(list) {
327 objs.push(new fieldmapper[self.fmClass]().fromStoreItem(item));
336 * Deletes the underlying object for all selected rows
338 deleteSelected : function() {
339 var items = this.getSelectedItems();
340 var total = items.length;
342 dojo.require('openils.PermaCrud');
345 var fmObject = new fieldmapper[self.fmClass]().fromStoreItem(item);
346 new openils.PermaCrud()['eliminate'](fmObject, {oncomplete : function(r) { self.store.deleteItem(item) }});
353 (dump ? dump : console.log)(E);
357 _formatRowSelectInput : function(rowIdx) {
358 if(rowIdx === null || rowIdx === undefined) return '';
359 var s = "<input type='checkbox' name='autogrid.selector' row='" + rowIdx + "'";
360 if(this.disableSelectorForRow && this.disableSelectorForRow(rowIdx))
361 s += " disabled='disabled'";
365 _applySingleEditStyle : function() {
366 this.onMouseOverRow = function(e) {};
367 this.onMouseOutRow = function(e) {};
368 this.onCellFocus = function(cell, rowIndex) {
369 this.selection.deselectAll();
370 this.selection.select(this.focus.rowIndex);
374 /* capture keydown and launch edit dialog on enter */
375 _applyEditOnEnter : function() {
376 this._applySingleEditStyle();
378 dojo.connect(this, 'onRowDblClick',
380 if(this.editStyle == 'pane')
381 this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
383 this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
387 dojo.connect(this, 'onKeyDown',
389 if(e.keyCode == dojo.keys.ENTER) {
390 this.selection.deselectAll();
391 this.selection.select(this.focus.rowIndex);
392 if(this.editStyle == 'pane')
393 this._drawEditPane(this.selection.getFirstSelected(), this.focus.rowIndex);
395 this._drawEditDialog(this.selection.getFirstSelected(), this.focus.rowIndex);
401 _makeEditPane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
403 var fmObject = new fieldmapper[this.fmClass]().fromStoreItem(storeItem);
404 var idents = grid.store.getIdentityAttributes();
407 var pane = new openils.widget.EditPane({
409 hideSaveButton : this.editReadOnly,
410 readOnly : this.editReadOnly,
411 overrideWidgets : this.overrideEditWidgets,
412 overrideWidgetClass : this.overrideEditWidgetClass,
413 overrideWidgetArgs : this.overrideWidgetArgs,
414 disableWidgetTest : this.disableWidgetTest,
415 requiredFields : this.requiredFields,
416 suppressFields : this.suppressEditFields,
417 onPostSubmit : function() {
418 for(var i in fmObject._fields) {
419 var field = fmObject._fields[i];
420 if(idents.filter(function(j){return (j == field)})[0])
421 continue; // don't try to edit an identifier field
422 grid.store.setValue(storeItem, field, fmObject[field]());
424 if(self.onPostUpdate)
425 self.onPostUpdate(storeItem, rowIndex);
429 grid.views.views[0].getCellNode(rowIndex, 0).focus();
436 onCancel : function() {
437 setTimeout(function(){
438 grid.views.views[0].getCellNode(rowIndex, 0).focus();},200);
439 if(onCancel) onCancel();
443 if (typeof this.editPaneOnSubmit == "function")
444 pane.onSubmit = this.editPaneOnSubmit;
445 pane.fieldOrder = this.fieldOrder;
446 pane.mode = 'update';
450 _makeCreatePane : function(onPostSubmit, onCancel) {
452 var pane = new openils.widget.EditPane({
453 fmClass : this.fmClass,
454 overrideWidgets : this.overrideEditWidgets,
455 overrideWidgetClass : this.overrideEditWidgetClass,
456 overrideWidgetArgs : this.overrideWidgetArgs,
457 disableWidgetTest : this.disableWidgetTest,
458 requiredFields : this.requiredFields,
459 suppressFields : this.suppressEditFields,
460 onPostSubmit : function(req, cudResults) {
461 var fmObject = cudResults[0];
462 if(grid.onPostCreate)
463 grid.onPostCreate(fmObject);
465 grid.store.newItem(fmObject.toStoreItem());
466 setTimeout(function(){
468 grid.selection.select(grid.rowCount-1);
469 grid.views.views[0].getCellNode(grid.rowCount-1, 1).focus();
473 onPostSubmit(fmObject);
475 onCancel : function() {
476 if(onCancel) onCancel();
479 if (typeof this.createPaneOnSubmit == "function")
480 pane.onSubmit = this.createPaneOnSubmit;
481 pane.fieldOrder = this.fieldOrder;
482 pane.mode = 'create';
487 * Creates an EditPane with a copy of the data from the provided store
488 * item for cloning said item
489 * @param {Object} storeItem Dojo data item
490 * @param {Number} rowIndex The Grid row index of the item to be cloned
491 * @param {Function} onPostSubmit Optional callback for post-submit behavior
492 * @param {Function} onCancel Optional callback for clone cancelation
493 * @return {Object} The clone EditPane
495 _makeClonePane : function(storeItem, rowIndex, onPostSubmit, onCancel) {
496 var clonePane = this._makeCreatePane(onPostSubmit, onCancel);
497 var origPane = this._makeEditPane(storeItem, rowIndex);
500 dojo.forEach(origPane.fieldList,
502 if(field.widget.widget.attr('disabled')) return;
503 var w = clonePane.fieldList.filter(
504 function(i) { return (i.name == field.name) })[0];
505 w.widget.baseWidgetValue(field.widget.widget.attr('value')); // sync widgets
506 w.widget.onload = function(){w.widget.baseWidgetValue(field.widget.widget.attr('value'))}; // async widgets
514 _drawEditDialog : function(storeItem, rowIndex) {
516 var done = function() { self.hideDialog(); };
517 var pane = this._makeEditPane(storeItem, rowIndex, done, done);
518 this.editDialog = new openils.widget.EditDialog({editPane:pane});
519 this.editDialog.startup();
520 this.editDialog.show();
524 * Generates an EditDialog for object creation and displays it to the user
526 showCreateDialog : function() {
528 var done = function() { self.hideDialog(); };
529 var pane = this._makeCreatePane(done, done);
530 this.editDialog = new openils.widget.EditDialog({editPane:pane});
531 this.editDialog.startup();
532 this.editDialog.show();
535 _drawEditPane : function(storeItem, rowIndex) {
537 var done = function() { self.hidePane(); };
538 dojo.style(this.domNode, 'display', 'none');
539 this.editPane = this._makeEditPane(storeItem, rowIndex, done, done);
540 this.editPane.startup();
541 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
542 if(this.onEditPane) this.onEditPane(this.editPane);
545 showClonePane : function(onPostSubmit) {
547 var done = function() { self.hidePane(); };
550 var row = this.getFirstSelectedRow();
553 var postSubmit = (onPostSubmit) ?
554 function(result) { onPostSubmit(self.getItem(row), result); self.hidePane(); } :
557 dojo.style(this.domNode, 'display', 'none');
558 this.editPane = this._makeClonePane(this.getItem(row), row, postSubmit, done);
559 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
560 if(this.onEditPane) this.onEditPane(this.editPane);
563 showCreatePane : function() {
564 if (this._showing_create_pane)
566 this._showing_create_pane = true;
569 var done = function() {
570 self._showing_create_pane = false;
573 dojo.style(this.domNode, 'display', 'none');
574 this.editPane = this._makeCreatePane(done, done);
575 this.editPane.startup();
576 this.domNode.parentNode.insertBefore(this.editPane.domNode, this.domNode);
577 if(this.onEditPane) this.onEditPane(this.editPane);
580 hideDialog : function() {
581 this.editDialog.hide();
582 this.editDialog.destroy();
583 delete this.editDialog;
587 hidePane : function() {
588 this.domNode.parentNode.removeChild(this.editPane.domNode);
589 this.editPane.destroy();
590 delete this.editPane;
591 dojo.style(this.domNode, 'display', 'block');
595 resetStore : function() {
596 this.setStore(this.buildAutoStore());
599 refresh : function() {
604 this.loadAll(this.cachedQueryOpts, this.cachedQuerySearch);
607 loadAll : function(opts, search) {
608 dojo.require('openils.PermaCrud');
609 if(this.loadProgressIndicator)
610 dojo.style(this.loadProgressIndicator, 'visibility', 'visible');
613 {limit : this.displayLimit, offset : this.displayOffset},
616 opts = dojo.mixin(opts, {
619 onresponse : function(r) {
620 var item = openils.Util.readResponse(r);
621 self.store.newItem(item.toStoreItem());
623 oncomplete : function() {
624 if(self.loadProgressIndicator)
625 dojo.style(self.loadProgressIndicator, 'visibility', 'hidden');
629 this.cachedQuerySearch = search;
630 this.cachedQueryOpts = opts;
632 new openils.PermaCrud().search(this.fmClass, search, opts);
634 new openils.PermaCrud().retrieveAll(this.fmClass, opts);
639 // static ID generater seed
640 openils.widget.AutoGrid.sequence = 0;
641 openils.widget.AutoGrid.gridCache = {};
643 openils.widget.AutoGrid.markupFactory = dojox.grid.DataGrid.markupFactory;
645 openils.widget.AutoGrid.defaultGetter = function(rowIndex, item) {
647 if(!this.grid.overrideWidgetArgs[this.field])
648 this.grid.overrideWidgetArgs[this.field] = {};
649 var val = this.grid.store.getValue(item, this.field);
650 var autoWidget = new openils.widget.AutoFieldWidget(dojo.mixin({
651 fmClass: this.grid.fmClass,
655 forceSync : true, // prevents many simultaneous requests for the same data
656 suppressLinkedFields : this.grid.suppressLinkedFields
657 },this.grid.overrideWidgetArgs[this.field]));
662 // With proper caching, this should not be necessary to prevent grid render flickering
667 var node = _this.grid.getCell(_this.index).view.getCellNode(rowIndex, _this.index);
669 node.innerHTML = ww.getDisplayString();
675 return autoWidget.getDisplayString();
678 openils.widget.AutoGrid.orgUnitGetter = function(rowIndex, item) {
679 if (!item) return "";
681 var aou_id = this.grid.store.getValue(item, this.field);
683 return fieldmapper.aou.findOrgUnit(aou_id).shortname();