5bde239237da5c3e7f958ef487be9e26f42d9c8c
[working/Evergreen.git] / Open-ILS / web / js / dojo / openils / widget / EditPane.js
1 if(!dojo._hasResource['openils.widget.EditPane']) {
2     dojo.provide('openils.widget.EditPane');
3     dojo.require('openils.widget.AutoWidget');
4     dojo.require('openils.widget.AutoFieldWidget');
5     dojo.require('fieldmapper.Fieldmapper');
6     dojo.require('dijit.layout.ContentPane');
7     dojo.require('openils.Util');
8     dojo.require('openils.PermaCrud');
9     dojo.require('dijit.form.Button');
10
11     dojo.declare(
12         'openils.widget.EditPane',
13         [dijit.layout.ContentPane, openils.widget.AutoWidget],
14         {
15             mode : 'update',
16             onPostSubmit : null, // apply callback
17             onCancel : null, // cancel callback
18             hideActionButtons : false,
19             fieldDocs : null,
20             existingTable : null,
21             suppressFields : null,
22             requiredFields : null,
23             paneStackCount : 1, // how many fields to add to each row, for compressing display
24
25             constructor : function(args) {
26                 this.fieldList = [];
27                 for(var k in args)
28                     this[k] = args[k];
29             },
30
31             /**
32              * Builds a basic table of key / value pairs.  Keys are IDL display labels.
33              * Values are dijit's, when values set
34              */
35             startup : function() {
36                 this.inherited(arguments);
37                 this.initAutoEnv();
38                 if(this.readOnly)
39                     this.hideSaveButton = true;
40
41                 // grab any field-level docs
42                 /*
43                 var pcrud = new openils.PermaCrud();
44                 this.fieldDocs = pcrud.search('fdoc', {fm_class:this.fmClass});
45                 */
46
47                 var table = this.existingTable;
48                 if(!table) {
49                     var table = this.table = document.createElement('table');
50                     this.domNode.appendChild(table);
51                 }
52                 var tbody = document.createElement('tbody');
53                 table.appendChild(tbody);
54
55                 this.limitPerms = [];
56                 if(this.fmIDL.permacrud && this.fmIDL.permacrud[this.mode])
57                     this.limitPerms = this.fmIDL.permacrud[this.mode].perms;
58
59                 if(!this.overrideWidgets)
60                     this.overrideWidgets = {};
61
62                 if(!this.overrideWidgetClass)
63                     this.overrideWidgetClass = {};
64
65                 if(!this.overrideWidgetArgs)
66                     this.overrideWidgetArgs = {};
67
68                 var idx = 0;
69                 var currentRow;
70                 for(var f in this.sortedFieldList) {
71                     var field = this.sortedFieldList[f];
72                     if(!field || field.virtual || field.nonIdl) continue;
73
74                     if(this.suppressFields && this.suppressFields.indexOf(field.name) > -1)
75                         continue;
76
77                     if(field.name == this.fmIDL.pkey && this.mode == 'create' && this.fmIDL.pkey_sequence)
78                         continue; /* don't show auto-generated fields on create */
79
80                     if(!this.overrideWidgetArgs[field.name])
81                         this.overrideWidgetArgs[field.name] = {};
82
83                     if(this.overrideWidgetArgs[field.name].hrbefore && this.paneStackCount <= 1) {
84                         var hrTr = document.createElement('tr');
85                         var hrTd = document.createElement('td');
86                         var hr = document.createElement('hr');
87                         hrTd.colSpan = 2;
88                         dojo.addClass(hrTd, 'openils-widget-editpane-hr-cell');
89                         hrTd.appendChild(hr);
90                         hrTr.appendChild(hrTd);
91                         tbody.appendChild(hrTr);
92                     }
93
94                     if((idx++ % this.paneStackCount) == 0 || !currentRow) {
95                         // time to start a new row
96                         currentRow = document.createElement('tr');
97                         tbody.appendChild(currentRow);
98                     }
99
100                     //var docTd = document.createElement('td');
101                     var nameTd = document.createElement('td');
102                     var valTd = document.createElement('td');
103                     var valSpan = document.createElement('span');
104                     valTd.appendChild(valSpan);
105                     dojo.addClass(nameTd, 'openils-widget-editpane-name-cell');
106                     dojo.addClass(valTd, 'openils-widget-editpane-value-cell');
107
108                     /*
109                     if(this.fieldDocs[field]) {
110                         var helpLink = dojo.create('a');
111                         var helpImg = dojo.create('img', {src:'/opac/images/advancedsearch-icon.png'}); // TODO Config
112                         helpLink.appendChild(helpImg);
113                         docTd.appendChild(helpLink);
114                     }
115                     */
116
117                     nameTd.appendChild(document.createTextNode(field.label));
118                     currentRow.setAttribute('fmfield', field.name);
119                     //currentRow.appendChild(docTd);
120                     currentRow.appendChild(nameTd);
121                     currentRow.appendChild(valTd);
122                     //dojo.addClass(docTd, 'oils-fm-edit-pane-help');
123
124                     var args = dojo.mixin(
125                         {   // defaults
126                             idlField : field, 
127                             fmObject : this.fmObject,
128                             fmClass : this.fmClass,
129                             parentNode : valSpan,
130                             orgLimitPerms : this.limitPerms,
131                             readOnly : this.readOnly,
132                             widget : this.overrideWidgets[field.name],
133                             widgetClass : this.overrideWidgetClass[field.name],
134                             disableWidgetTest : this.disableWidgetTest
135                         },
136                         this.overrideWidgetArgs[field.name] // per-field overrides
137                     );
138
139                     if (this.overrideWidgets[field.name]) {
140                         if (this.overrideWidgets[field.name].shove) {
141                             args.shove = dojo.mixin(
142                                 {"mode": this.mode},
143                                 this.overrideWidgets[field.name].shove
144                             );
145                         }
146                     }
147
148                     if(args.readOnly) {
149                         dojo.addClass(nameTd, 'openils-widget-editpane-ro-name-cell');
150                         dojo.addClass(valTd, 'openils-widget-editpane-ro-value-cell');
151                     }
152
153                     if(this.requiredFields && this.requiredFields.indexOf(field.name) >= 0) {
154                         if(!args.dijitArgs) args.dijitArgs = {};
155                         args.dijitArgs.required = true;
156                     }
157
158                     var widget = new openils.widget.AutoFieldWidget(args);
159
160                     widget.build();
161                     this.fieldList.push({name:field.name, widget:widget});
162                 }
163                 if(!this.hideActionButtons)
164                     this.buildActionButtons(tbody);
165
166                 openils.Util.addCSSClass(table, 'oils-fm-edit-pane');
167             },
168
169             applySaveOnEnter : function(widget) {
170                 var self = this;
171                 dojo.connect(this, 'onKeyDown',
172                     function(e) {
173                         if(e.keyCode == dojo.keys.ENTER) 
174                             self.performAutoEditAction();
175                     }
176                 );
177             },
178
179             buildActionButtons : function(tbody) {
180                 var row = document.createElement('tr');
181                 var cancelTd = document.createElement('td');
182                 var applyTd = document.createElement('td');
183                 var cancelSpan = document.createElement('span');
184                 var applySpan = document.createElement('span');
185                 row.appendChild(cancelTd);
186                 row.appendChild(applyTd);
187                 cancelTd.appendChild(cancelSpan);
188                 applyTd.appendChild(applySpan);
189                 tbody.appendChild(row);
190
191                 var self = this;
192                 new dijit.form.Button({
193                     label:'Cancel', // XXX
194                     onClick : this.onCancel
195                 }, cancelSpan);
196
197                 if(this.hideSaveButton) return;
198
199                 new dijit.form.Button({
200                     label:'Save',  // XXX
201                     onClick: function() {self.performAutoEditAction();}
202                 }, applySpan);
203             },
204
205             getFields : function() {
206                 return this.fieldList.map(function(a) { return a.name });
207             },
208
209             // Apply a function for the name and formatted value of each field
210             // in this edit pane.  If any required value is null, then return
211             // an error object.
212             mapValues: function (fn) {
213                 var e = 0, msg = this.fmIDL.label + ' ';
214                 dojo.forEach(this.fieldList, function (f) {
215                     var v, w = f.widget;
216                     if ((v = w.getFormattedValue()) === null && w.isRequired()) { e++; }
217                     fn(f.name, v);
218                 });
219                 if (e > 0) {
220                     msg += 'edit pane has ' + e + ' required field(s) that contain no value(s)';
221                     return new Error(msg);
222                 }
223             },
224
225             getFieldValue : function(field, checkRequired) {
226                 for(var i in this.fieldList) {
227                     if(field == this.fieldList[i].name) {
228                         var val = this.fieldList[i].widget.getFormattedValue();
229                         if (checkRequired &&
230                             val == null && /* XXX stricter check needed? */
231                             this.fieldList[i].widget.isRequired()) {
232                             throw new Error("req");
233                         }
234                         return val;
235
236                     }
237                 }
238             },
239
240             getFieldWidget : function(field) {
241                 for (var i in this.fieldList)
242                     if (field == this.fieldList[i].name)
243                         return this.fieldList[i].widget;
244             },
245
246             setFieldValue : function(field, val) {
247                 for(var i in this.fieldList) {
248                     if(field == this.fieldList[i].name) {
249                         this.fieldList[i].widget.widget.attr('value', val);
250                     }
251                 }
252             },
253
254
255             performAutoEditAction : function() {
256                 var self = this;
257                 self.performEditAction({
258                     oncomplete:function(req, cudResults) {
259                         if(self.onPostSubmit)
260                             self.onPostSubmit(req, cudResults);
261                     }
262                 });
263             },
264
265             performEditAction : function(opts) {
266                 var self = this;
267                 var fields = this.getFields();
268                 if(this.mode == 'create')
269                     this.fmObject = new fieldmapper[this.fmClass]();
270                 try {
271                     for(var idx in fields) {
272                         this.fmObject[fields[idx]](
273                             this.getFieldValue(fields[idx], true)
274                         );
275                     }
276                 } catch (E) {
277                     if (E.message == "req") /* req'd field set to null. bail. */
278                         return;
279                     else /* something else went wrong? */
280                         throw E;
281                 }
282                 if(this.mode == 'create' && this.fmIDL.pkey_sequence)
283                     this.fmObject[this.fmIDL.pkey](null);
284                 if (typeof(this.onSubmit) == "function") {
285                     this.onSubmit(this.fmObject, opts, self);
286                 } else {
287                     (new openils.PermaCrud())[this.mode](this.fmObject, opts);
288                 }
289             }
290         }
291     );
292 }
293