netzke-basepack 0.5.12 → 0.5.13
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +13 -0
- data/Rakefile +2 -2
- data/TODO.rdoc +4 -0
- data/lib/app/models/netzke_persistent_array_auto_model.rb +1 -1
- data/lib/netzke/active_record/attributes.rb +1 -1
- data/lib/netzke/active_record/combobox_options.rb +3 -33
- data/lib/netzke/data_accessor.rb +46 -26
- data/lib/netzke/form_panel.rb +11 -5
- data/lib/netzke/form_panel/form_panel_api.rb +5 -1
- data/lib/netzke/form_panel/form_panel_fields.rb +58 -56
- data/lib/netzke/grid_panel.rb +34 -38
- data/lib/netzke/grid_panel/grid_panel_api.rb +11 -2
- data/lib/netzke/grid_panel/grid_panel_columns.rb +79 -65
- data/lib/netzke/grid_panel/grid_panel_js.rb +2 -736
- data/lib/netzke/grid_panel/javascripts/advanced_search.js +65 -0
- data/lib/netzke/grid_panel/javascripts/edit_in_form.js +47 -0
- data/lib/netzke/grid_panel/javascripts/grid_panel_pre.js +562 -0
- data/lib/netzke/json_array_editor.rb +7 -1
- data/lib/netzke/tab_panel.rb +1 -0
- data/stylesheets/basepack.css +1 -1
- metadata +10 -7
@@ -0,0 +1,65 @@
|
|
1
|
+
Ext.override(Netzke.pre.GridPanel, {
|
2
|
+
onSearch: function(){
|
3
|
+
delete this.searchWindow;
|
4
|
+
this.searchWindow = new Ext.Window({
|
5
|
+
title:'Advanced search',
|
6
|
+
layout:'fit',
|
7
|
+
modal: true,
|
8
|
+
width: 400,
|
9
|
+
height: Ext.lib.Dom.getViewHeight() *0.9,
|
10
|
+
closeAction:'close',
|
11
|
+
buttons:[{
|
12
|
+
text: 'OK',
|
13
|
+
handler: function(){
|
14
|
+
this.ownerCt.ownerCt.closePositively();
|
15
|
+
}
|
16
|
+
},{
|
17
|
+
text:'Cancel',
|
18
|
+
handler:function(){
|
19
|
+
this.ownerCt.ownerCt.closeNegatively();
|
20
|
+
}
|
21
|
+
}],
|
22
|
+
closePositively : function(){
|
23
|
+
this.conditions = this.getWidget().getForm().getValues();
|
24
|
+
this.closeRes = 'OK';
|
25
|
+
this.close();
|
26
|
+
},
|
27
|
+
closeNegatively: function(){
|
28
|
+
this.closeRes = 'cancel';
|
29
|
+
this.close();
|
30
|
+
}
|
31
|
+
});
|
32
|
+
|
33
|
+
this.searchWindow.on('close', function(){
|
34
|
+
if (this.searchWindow.closeRes == 'OK'){
|
35
|
+
var searchConditions = this.searchWindow.conditions;
|
36
|
+
var filtered = false;
|
37
|
+
// check if there's any search condition set
|
38
|
+
for (var k in searchConditions) {
|
39
|
+
if (searchConditions[k].length > 0) {
|
40
|
+
filtered = true;
|
41
|
+
break;
|
42
|
+
}
|
43
|
+
}
|
44
|
+
this.actions.search.setText(filtered ? "Search *" : "Search");
|
45
|
+
this.getStore().baseParams = {extra_conditions: Ext.encode(this.searchWindow.conditions)};
|
46
|
+
this.getStore().load();
|
47
|
+
}
|
48
|
+
}, this);
|
49
|
+
|
50
|
+
this.searchWindow.on('add', function(container, searchPanel){
|
51
|
+
searchPanel.on('apply', function(widget){
|
52
|
+
this.searchWindow.closePositively();
|
53
|
+
return false; // stop the event
|
54
|
+
}, this);
|
55
|
+
}, this);
|
56
|
+
|
57
|
+
this.searchWindow.show(null, function(){
|
58
|
+
this.searchWindow.closeRes = 'cancel';
|
59
|
+
if (!this.searchWindow.getWidget()){
|
60
|
+
this.loadAggregatee({id:"searchPanel", container:this.searchWindow.id});
|
61
|
+
}
|
62
|
+
}, this);
|
63
|
+
|
64
|
+
}
|
65
|
+
});
|
@@ -0,0 +1,47 @@
|
|
1
|
+
Ext.override(Netzke.pre.GridPanel, {
|
2
|
+
onEditInForm: function(){
|
3
|
+
var selModel = this.getSelectionModel();
|
4
|
+
if (selModel.getCount() > 1) {
|
5
|
+
var recordId = selModel.getSelected().id;
|
6
|
+
this.loadAggregatee({id: "multiEditForm",
|
7
|
+
params: {record_id: recordId},
|
8
|
+
callback: function(w){
|
9
|
+
var form = w.items.first();
|
10
|
+
form.on('apply', function(){
|
11
|
+
var ids = [];
|
12
|
+
selModel.each(function(r){
|
13
|
+
ids.push(r.id);
|
14
|
+
});
|
15
|
+
form.baseParams = {ids: Ext.encode(ids)}
|
16
|
+
}, this);
|
17
|
+
|
18
|
+
w.on('close', function(){
|
19
|
+
if (w.closeRes === "ok") {
|
20
|
+
this.store.reload();
|
21
|
+
}
|
22
|
+
}, this);
|
23
|
+
}, scope: this});
|
24
|
+
} else {
|
25
|
+
var recordId = selModel.getSelected().id;
|
26
|
+
this.loadAggregatee({id: "editForm",
|
27
|
+
params: {record_id: recordId},
|
28
|
+
callback: function(form){
|
29
|
+
form.on('close', function(){
|
30
|
+
if (form.closeRes === "ok") {
|
31
|
+
this.store.reload();
|
32
|
+
}
|
33
|
+
}, this);
|
34
|
+
}, scope: this});
|
35
|
+
}
|
36
|
+
},
|
37
|
+
|
38
|
+
onAddInForm: function(){
|
39
|
+
this.loadAggregatee({id: "addForm", callback: function(form){
|
40
|
+
form.on('close', function(){
|
41
|
+
if (form.closeRes === "ok") {
|
42
|
+
this.store.reload();
|
43
|
+
}
|
44
|
+
}, this);
|
45
|
+
}, scope: this});
|
46
|
+
}
|
47
|
+
});
|
@@ -0,0 +1,562 @@
|
|
1
|
+
/*
|
2
|
+
Static part of GridPanel's JavaScript class.
|
3
|
+
*/
|
4
|
+
Netzke.pre.GridPanel = Ext.extend(Ext.grid.EditorGridPanel, {
|
5
|
+
trackMouseOver: true,
|
6
|
+
loadMask: true,
|
7
|
+
autoScroll: true,
|
8
|
+
|
9
|
+
initComponent: function(){
|
10
|
+
if (!this.clmns) {this.feedback('No columns defined for grid '+this.id);}
|
11
|
+
|
12
|
+
/* Process columns - all in sake of creating the column model */
|
13
|
+
// Normalize columns passed in the config
|
14
|
+
var normClmns = [];
|
15
|
+
Ext.each(this.clmns, function(c){
|
16
|
+
// normalize columns
|
17
|
+
if (typeof c == 'string') {
|
18
|
+
normClmns.push({name:c});
|
19
|
+
} else {
|
20
|
+
normClmns.push(c);
|
21
|
+
}
|
22
|
+
});
|
23
|
+
|
24
|
+
delete this.clmns; // we don't need them anymore
|
25
|
+
|
26
|
+
var cmConfig = []; // column model config - we'll use it later to create the ColumnModel
|
27
|
+
this.plugins = []; // checkbox colums is a special case, being a plugin
|
28
|
+
|
29
|
+
var filters = [];
|
30
|
+
|
31
|
+
// Run through columns and set up different configuration for each
|
32
|
+
Ext.each(normClmns, function(c){
|
33
|
+
// We will not use meta columns as actual columns (not even hidden) - only to create the records
|
34
|
+
if (c.meta) return;
|
35
|
+
|
36
|
+
// Apply default column config
|
37
|
+
Ext.applyIf(c, this.defaultColumnConfig);
|
38
|
+
|
39
|
+
// setting dataIndex separately
|
40
|
+
c.dataIndex = c.name;
|
41
|
+
|
42
|
+
// Automatically calculated default values
|
43
|
+
if (!c.header) {c.header = c.label || c.name.humanize()}
|
44
|
+
|
45
|
+
// normalize editor
|
46
|
+
if (c.editor) {
|
47
|
+
c.editor = Netzke.isObject(c.editor) ? c.editor : {xtype:c.editor};
|
48
|
+
} else {
|
49
|
+
c.editor = {xtype: this.attrTypeEditorMap[c.attrType] || 'textfield'}
|
50
|
+
}
|
51
|
+
|
52
|
+
// if comboboxOptions are provided, we render a combobox instead of textfield
|
53
|
+
if (c.comboboxOptions && c.editor.xtype === "textfield") {
|
54
|
+
c.editor = {xtype: "combobox", options: c.comboboxOptions.split('\\n')}
|
55
|
+
}
|
56
|
+
|
57
|
+
// collect filters
|
58
|
+
if (c.filterable){
|
59
|
+
filters.push({type:this.filterTypeForAttrType(c.attrType), dataIndex:c.name});
|
60
|
+
}
|
61
|
+
|
62
|
+
if (c.editor && c.editor.xtype == 'checkbox') {
|
63
|
+
// Special case of checkbox column
|
64
|
+
var plugin = new Ext.ux.grid.CheckColumn(c);
|
65
|
+
this.plugins.push(plugin);
|
66
|
+
cmConfig.push(plugin);
|
67
|
+
} else {
|
68
|
+
// a "normal" column, not a plugin
|
69
|
+
if (!c.readOnly && !this.prohibitUpdate) {
|
70
|
+
// c.editor contains complete config of the editor
|
71
|
+
var xtype = c.editor.xtype;
|
72
|
+
c.editor = Ext.ComponentMgr.create(Ext.apply({
|
73
|
+
parentId: this.id,
|
74
|
+
name: c.name,
|
75
|
+
selectOnFocus:true
|
76
|
+
}, c.editor));
|
77
|
+
} else {
|
78
|
+
c.editor = null;
|
79
|
+
}
|
80
|
+
|
81
|
+
// Normalize the renderer
|
82
|
+
this.normalizeRenderer(c);
|
83
|
+
|
84
|
+
// set the renderer
|
85
|
+
// if (c.renderer && !Ext.isArray(c.renderer) && c.renderer.match(/^\\s*function\\s*\\(/)) {
|
86
|
+
// // if the renderer is an inline function - eval it (double escaping because we are inside of the Ruby string here...)
|
87
|
+
// eval("c.renderer = " + c.renderer + ";");
|
88
|
+
// } else if (Ext.isFunction(this[c.renderer])) {
|
89
|
+
// // whether the renderer is defined in this.scope
|
90
|
+
// c.renderer = this[c.renderer].createDelegate(this);
|
91
|
+
// } else {
|
92
|
+
// // othrewise it's a string representing the name of the renderer or a json-encoded array,
|
93
|
+
// // where the first parameter is the renderer's name, and the rest - parameters that should be
|
94
|
+
// // passed to the renderer at the moment of calling
|
95
|
+
// var renderer = Ext.netzke.normalizedRenderer(c.renderer);
|
96
|
+
// if (renderer != null) {
|
97
|
+
// c.renderer = renderer
|
98
|
+
// };
|
99
|
+
// }
|
100
|
+
//
|
101
|
+
// add to the list
|
102
|
+
cmConfig.push(c);
|
103
|
+
}
|
104
|
+
|
105
|
+
}, this);
|
106
|
+
|
107
|
+
// Finally, create the ColumnModel based on processed columns
|
108
|
+
this.cm = new Ext.grid.ColumnModel(cmConfig);
|
109
|
+
|
110
|
+
// Hidden change event
|
111
|
+
if (this.persistentConfig) {this.cm.on('hiddenchange', this.onColumnHiddenChange, this);}
|
112
|
+
|
113
|
+
/* ... and done with columns */
|
114
|
+
|
115
|
+
// Filters
|
116
|
+
if (this.enableColumnFilters) {
|
117
|
+
this.plugins.push(new Ext.ux.grid.GridFilters({filters:filters}));
|
118
|
+
}
|
119
|
+
|
120
|
+
// Create Ext.data.Record constructor specific for our particular column configuration
|
121
|
+
this.recordConfig = [];
|
122
|
+
Ext.each(normClmns, function(column){this.recordConfig.push({name:column.name, defaultValue:column.defaultValue});}, this);
|
123
|
+
this.Row = Ext.data.Record.create(this.recordConfig);
|
124
|
+
|
125
|
+
// Drag'n'Drop
|
126
|
+
if (this.enableRowsReordering){
|
127
|
+
this.ddPlugin = new Ext.ux.dd.GridDragDropRowOrder({
|
128
|
+
scrollable: true // enable scrolling support (default is false)
|
129
|
+
});
|
130
|
+
this.plugins.push(this.ddPlugin);
|
131
|
+
}
|
132
|
+
|
133
|
+
// Explicitely create the connection to get grid's data,
|
134
|
+
// because we don't want the app-wide Ext.Ajax to be used,
|
135
|
+
// as we are going to subscribe to its events
|
136
|
+
var connection = new Ext.data.Connection({
|
137
|
+
url: this.buildApiUrl("get_data"),
|
138
|
+
extraParams: {
|
139
|
+
authenticity_token : Netzke.authenticityToken
|
140
|
+
},
|
141
|
+
|
142
|
+
// inform Ext.Ajax about our events
|
143
|
+
listeners: {
|
144
|
+
beforerequest: function(){
|
145
|
+
Ext.Ajax.fireEvent('beforerequest', arguments);
|
146
|
+
},
|
147
|
+
requestexception: function(){
|
148
|
+
Ext.Ajax.fireEvent('requestexception', arguments);
|
149
|
+
},
|
150
|
+
requestcomplete: function(){
|
151
|
+
Ext.Ajax.fireEvent('requestcomplete', arguments);
|
152
|
+
}
|
153
|
+
}
|
154
|
+
});
|
155
|
+
|
156
|
+
// besides getting data into the store, we may also get commands to execute
|
157
|
+
connection.on('requestcomplete', function(conn, r){
|
158
|
+
var response = Ext.decode(r.responseText);
|
159
|
+
|
160
|
+
// delete data-related properties
|
161
|
+
Ext.each(['data', 'total', 'success'], function(property){delete response[property];});
|
162
|
+
this.bulkExecute(response);
|
163
|
+
}, this);
|
164
|
+
|
165
|
+
// HttpProxy that uses our custom connection
|
166
|
+
var httpProxy = new Ext.data.HttpProxy(connection);
|
167
|
+
|
168
|
+
// Data store
|
169
|
+
this.store = new Ext.data.Store({
|
170
|
+
proxy: this.proxy = httpProxy,
|
171
|
+
reader: new Ext.data.ArrayReader({root: "data", totalProperty: "total", successProperty: "success", id:0}, this.Row),
|
172
|
+
remoteSort: true,
|
173
|
+
listeners:{'loadexception':{
|
174
|
+
fn:this.loadExceptionHandler,
|
175
|
+
scope:this
|
176
|
+
}}
|
177
|
+
});
|
178
|
+
|
179
|
+
// Normalize bottom bar
|
180
|
+
this.bbar = (this.enablePagination) ? new Ext.PagingToolbar({
|
181
|
+
pageSize : this.rowsPerPage,
|
182
|
+
items : this.bbar ? ["-"].concat(this.bbar) : [],
|
183
|
+
store : this.store,
|
184
|
+
emptyMsg: 'Empty',
|
185
|
+
displayInfo: true
|
186
|
+
}) : this.bbar;
|
187
|
+
|
188
|
+
// Selection model
|
189
|
+
this.sm = new Ext.grid.RowSelectionModel();
|
190
|
+
|
191
|
+
// Now let Ext.grid.EditorGridPanel do the rest
|
192
|
+
// Original initComponent
|
193
|
+
Netzke.pre.GridPanel.superclass.initComponent.call(this);
|
194
|
+
|
195
|
+
// Inform the server part about column operations
|
196
|
+
if (this.persistentConfig) {
|
197
|
+
this.on('columnresize', this.onColumnResize, this);
|
198
|
+
this.on('columnmove', this.onColumnMove, this);
|
199
|
+
}
|
200
|
+
|
201
|
+
// Context menu
|
202
|
+
if (this.enableContextMenu) {
|
203
|
+
this.on('rowcontextmenu', this.onRowContextMenu, this);
|
204
|
+
}
|
205
|
+
|
206
|
+
// Load data AFTER the toolbar is bound to the store, which will provide for correct page number
|
207
|
+
if (this.loadInlineData) {
|
208
|
+
this.getStore().loadData(this.inlineData);
|
209
|
+
|
210
|
+
// If rows per page specified, fake store.lastOptions as if the data was loaded
|
211
|
+
// by PagingToolbar (for correct functionning of refresh tool and extended search)
|
212
|
+
if (this.rowsPerPage) {
|
213
|
+
this.getStore().lastOptions = {params:{limit:this.rowsPerPage, start:0}}; // this is how PagingToolbar does it...
|
214
|
+
}
|
215
|
+
|
216
|
+
// inlineData may also contain commands (TODO: make it DRY)
|
217
|
+
// delete data-related properties
|
218
|
+
Ext.each(['data', 'total', 'success'], function(property){delete this.inlineData[property];}, this);
|
219
|
+
this.bulkExecute(this.inlineData);
|
220
|
+
}
|
221
|
+
|
222
|
+
// Process selectionchange event
|
223
|
+
this.getSelectionModel().on('selectionchange', function(selModel){
|
224
|
+
// enable/disable actions
|
225
|
+
this.actions.del.setDisabled(!selModel.hasSelection() || this.prohibitDelete);
|
226
|
+
this.actions.edit.setDisabled(selModel.getCount() != 1 || this.prohibitUpdate);
|
227
|
+
}, this);
|
228
|
+
|
229
|
+
// Drag n Drop event
|
230
|
+
if (this.enableRowsReordering){
|
231
|
+
this.ddPlugin.on('afterrowmove', this.onAfterRowMove, this);
|
232
|
+
}
|
233
|
+
|
234
|
+
// GridView
|
235
|
+
this.getView().getRowClass = this.defaultGetRowClass;
|
236
|
+
},
|
237
|
+
|
238
|
+
filterTypeForAttrType: function(attrType){
|
239
|
+
var map = {
|
240
|
+
integer :'Numeric',
|
241
|
+
decimal :'Numeric',
|
242
|
+
datetime:'Date',
|
243
|
+
date :'Date',
|
244
|
+
string :'String'
|
245
|
+
};
|
246
|
+
map['boolean'] = "Boolean"; // "boolean" is a JS reserved word
|
247
|
+
return map[attrType] || 'String';
|
248
|
+
},
|
249
|
+
|
250
|
+
attrTypeEditorMap: {
|
251
|
+
integer : "numberfield",
|
252
|
+
"boolean": "checkbox",
|
253
|
+
decimal : "numberfield",
|
254
|
+
datetime : "xdatetime",
|
255
|
+
date : "datefield",
|
256
|
+
string : "textfield"
|
257
|
+
},
|
258
|
+
|
259
|
+
onAdd: function(){
|
260
|
+
var r = new this.Row();
|
261
|
+
r.isNew = true; // to distinguish new records
|
262
|
+
// r.set('id', r.id); // otherwise later r.get('id') returns empty string
|
263
|
+
this.stopEditing();
|
264
|
+
this.getStore().add(r);
|
265
|
+
|
266
|
+
// Set default values
|
267
|
+
this.getStore().fields.each(function(field){
|
268
|
+
r.set(field.name, field.defaultValue);
|
269
|
+
});
|
270
|
+
|
271
|
+
this.tryStartEditing(this.store.indexOf(r));
|
272
|
+
},
|
273
|
+
|
274
|
+
onDel: function() {
|
275
|
+
Ext.Msg.confirm('Confirm', 'Are you sure?', function(btn){
|
276
|
+
if (btn == 'yes') {
|
277
|
+
var records = [];
|
278
|
+
this.getSelectionModel().each(function(r){
|
279
|
+
if (r.isNew) {
|
280
|
+
// this record is not know to server - simply remove from store
|
281
|
+
this.store.remove(r);
|
282
|
+
} else {
|
283
|
+
records.push(r.id);
|
284
|
+
}
|
285
|
+
}, this);
|
286
|
+
|
287
|
+
if (records.length > 0){
|
288
|
+
// call API
|
289
|
+
this.deleteData({records: Ext.encode(records)});
|
290
|
+
}
|
291
|
+
}
|
292
|
+
}, this);
|
293
|
+
},
|
294
|
+
|
295
|
+
onApply: function(){
|
296
|
+
var newRecords = [];
|
297
|
+
var updatedRecords = [];
|
298
|
+
Ext.each(this.store.getModifiedRecords(),
|
299
|
+
function(r) {
|
300
|
+
if (r.isNew) {
|
301
|
+
newRecords.push(Ext.apply(r.getChanges(), {id:r.id}));
|
302
|
+
} else {
|
303
|
+
updatedRecords.push(Ext.apply(r.getChanges(), {id:r.id}));
|
304
|
+
}
|
305
|
+
},
|
306
|
+
this);
|
307
|
+
|
308
|
+
if (newRecords.length > 0 || updatedRecords.length > 0) {
|
309
|
+
var params = {};
|
310
|
+
|
311
|
+
if (newRecords.length > 0) {
|
312
|
+
params.created_records = Ext.encode(newRecords);
|
313
|
+
}
|
314
|
+
|
315
|
+
if (updatedRecords.length > 0) {
|
316
|
+
params.updated_records = Ext.encode(updatedRecords);
|
317
|
+
}
|
318
|
+
|
319
|
+
if (this.store.baseParams !== {}) {
|
320
|
+
params.base_params = Ext.encode(this.store.baseParams);
|
321
|
+
}
|
322
|
+
|
323
|
+
this.postData(params);
|
324
|
+
}
|
325
|
+
|
326
|
+
},
|
327
|
+
|
328
|
+
// Handlers for tools
|
329
|
+
//
|
330
|
+
|
331
|
+
onRefresh: function() {
|
332
|
+
if (this.fireEvent('refresh', this) !== false) {
|
333
|
+
this.store.reload();
|
334
|
+
}
|
335
|
+
},
|
336
|
+
|
337
|
+
// Event handlers
|
338
|
+
//
|
339
|
+
|
340
|
+
onColumnResize: function(index, size){
|
341
|
+
this.resizeColumn({
|
342
|
+
index:index,
|
343
|
+
size:size
|
344
|
+
});
|
345
|
+
},
|
346
|
+
|
347
|
+
onColumnHiddenChange: function(cm, index, hidden){
|
348
|
+
this.hideColumn({
|
349
|
+
index:index,
|
350
|
+
hidden:hidden
|
351
|
+
});
|
352
|
+
},
|
353
|
+
|
354
|
+
onColumnMove: function(oldIndex, newIndex){
|
355
|
+
this.moveColumn({
|
356
|
+
old_index:oldIndex,
|
357
|
+
new_index:newIndex
|
358
|
+
});
|
359
|
+
|
360
|
+
var newRecordConfig = [];
|
361
|
+
Ext.each(this.getColumnModel().config, function(c){newRecordConfig.push({name: c.name})});
|
362
|
+
delete this.Row; // old record constructor
|
363
|
+
this.Row = Ext.data.Record.create(newRecordConfig);
|
364
|
+
this.getStore().reader.recordType = this.Row;
|
365
|
+
},
|
366
|
+
|
367
|
+
onRowContextMenu: function(grid, rowIndex, e){
|
368
|
+
e.stopEvent();
|
369
|
+
var coords = e.getXY();
|
370
|
+
|
371
|
+
if (!grid.getSelectionModel().isSelected(rowIndex)) {
|
372
|
+
grid.getSelectionModel().selectRow(rowIndex);
|
373
|
+
}
|
374
|
+
|
375
|
+
var menu = new Ext.menu.Menu({
|
376
|
+
items: this.contextMenu
|
377
|
+
});
|
378
|
+
|
379
|
+
menu.showAt(coords);
|
380
|
+
},
|
381
|
+
|
382
|
+
onAfterRowMove: function(dt, oldIndex, newIndex, records){
|
383
|
+
var ids = [];
|
384
|
+
// collect records ids
|
385
|
+
Ext.each(records, function(r){ids.push(r.id)});
|
386
|
+
// call GridPanel's API
|
387
|
+
this.moveRows({ids:Ext.encode(ids), new_index: newIndex});
|
388
|
+
},
|
389
|
+
|
390
|
+
// Other methods
|
391
|
+
//
|
392
|
+
|
393
|
+
loadExceptionHandler: function(proxy, options, response, error){
|
394
|
+
if (response.status == 200 && (responseObject = Ext.decode(response.responseText)) && responseObject.flash){
|
395
|
+
this.feedback(responseObject.flash);
|
396
|
+
} else {
|
397
|
+
if (error){
|
398
|
+
this.feedback(error.message);
|
399
|
+
} else {
|
400
|
+
this.feedback(response.statusText);
|
401
|
+
}
|
402
|
+
}
|
403
|
+
},
|
404
|
+
|
405
|
+
update: function(){
|
406
|
+
this.store.reload();
|
407
|
+
},
|
408
|
+
|
409
|
+
loadStoreData: function(data){
|
410
|
+
this.store.loadData(data);
|
411
|
+
Ext.each(['data', 'total', 'success'], function(property){delete data[property];}, this);
|
412
|
+
this.bulkExecute(data);
|
413
|
+
},
|
414
|
+
|
415
|
+
// try editing the first editable (i.e. not hidden, not read-only) sell
|
416
|
+
tryStartEditing: function(row){
|
417
|
+
var editableIndex = 0;
|
418
|
+
Ext.each(this.getColumnModel().config, function(c){
|
419
|
+
// skip columns that cannot be edited
|
420
|
+
if (!(c.hidden == true || c.editable == false || !c.editor || c.attrType == 'boolean')) {
|
421
|
+
return false;
|
422
|
+
}
|
423
|
+
editableIndex++;
|
424
|
+
});
|
425
|
+
|
426
|
+
if (editableIndex < this.getColumnModel().config.length) {this.startEditing(row, editableIndex);}
|
427
|
+
},
|
428
|
+
|
429
|
+
// Called by the server side to update newly created records
|
430
|
+
updateNewRecords: function(records){
|
431
|
+
this.updateRecords(records);
|
432
|
+
},
|
433
|
+
|
434
|
+
// Called by the server side to update modified records
|
435
|
+
updateModRecords: function(records){
|
436
|
+
this.updateRecords(records, true);
|
437
|
+
},
|
438
|
+
|
439
|
+
// Updates modified or newly created records, by record ID
|
440
|
+
// Example of the records argument (updated columns):
|
441
|
+
// {1098 => [1, 'value1', 'value2'], 1099 => [2, 'value1', 'value2']}
|
442
|
+
// Example of the records argument (new columns, id autogenerated by Ext):
|
443
|
+
// {"ext-record-200" => [1, 'value1', 'value2']}
|
444
|
+
updateRecords: function(records, mod){
|
445
|
+
if (!mod) {mod = false;}
|
446
|
+
var modRecordsInGrid = [].concat(this.store.getModifiedRecords()); // there must be a better way to clone an array...
|
447
|
+
|
448
|
+
// replace arrays of data in the args object with Ext.data.Record objects
|
449
|
+
for (var k in records){
|
450
|
+
records[k] = this.store.reader.readRecords([records[k]]).records[0];
|
451
|
+
}
|
452
|
+
|
453
|
+
// for each new record write the data returned by the server, and commit the record
|
454
|
+
Ext.each(modRecordsInGrid, function(recordInGrid){
|
455
|
+
if (mod ^ recordInGrid.isNew) {
|
456
|
+
// if record is new, we access its id by "id", otherwise, the id is in the primary key column
|
457
|
+
var recordId = recordInGrid.id;
|
458
|
+
// new data that the server sent us to update this record (identified by the id)
|
459
|
+
var newData = records[recordId];
|
460
|
+
|
461
|
+
if (newData){
|
462
|
+
for (var k in newData.data){
|
463
|
+
recordInGrid.set(k, newData.get(k));
|
464
|
+
}
|
465
|
+
|
466
|
+
recordInGrid.isNew = false;
|
467
|
+
recordInGrid.commit();
|
468
|
+
}
|
469
|
+
|
470
|
+
}
|
471
|
+
}, this);
|
472
|
+
|
473
|
+
// clear the selections
|
474
|
+
this.getSelectionModel().clearSelections();
|
475
|
+
|
476
|
+
// check if there are still records with errors
|
477
|
+
var modRecords = this.store.getModifiedRecords();
|
478
|
+
if (modRecords.length == 0) {
|
479
|
+
// if all records are accepted, reload the grid (so that eventual order/filtering is correct)
|
480
|
+
this.store.reload();
|
481
|
+
|
482
|
+
// ... and set default getRowClass function
|
483
|
+
this.getView().getRowClass = this.defaultGetRowClass;
|
484
|
+
} else {
|
485
|
+
this.getView().getRowClass = function(r){
|
486
|
+
return r.dirty ? "grid-dirty-record" : ""
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
this.getView().refresh();
|
491
|
+
this.getSelectionModel().fireEvent('selectionchange', this.getSelectionModel());
|
492
|
+
},
|
493
|
+
|
494
|
+
defaultGetRowClass: function(r){
|
495
|
+
return r.isNew ? "grid-dirty-record" : ""
|
496
|
+
},
|
497
|
+
|
498
|
+
selectFirstRow: function(){
|
499
|
+
this.getSelectionModel().suspendEvents();
|
500
|
+
this.getSelectionModel().selectRow(0);
|
501
|
+
this.getSelectionModel().resumeEvents();
|
502
|
+
},
|
503
|
+
|
504
|
+
// Normalizes the renderer for a column.
|
505
|
+
// Renderer may be:
|
506
|
+
// 1) a string that contains the name of the function to be used as renderer.
|
507
|
+
// 2) an array, where the first element is the function name, and the rest - the arguments
|
508
|
+
// that will be passed to that function along with the value to be rendered.
|
509
|
+
// The function is searched in the following objects: 1) Ext.util.Format, 2) this.
|
510
|
+
// If not found, it is simply evaluated. Handy, when as renderer we receive an inline JS function,
|
511
|
+
// or reference to a function in some other scope.
|
512
|
+
// So, these will work:
|
513
|
+
// * "uppercase"
|
514
|
+
// * ["ellipsis", 10]
|
515
|
+
// * ["substr", 3, 5]
|
516
|
+
// * "myRenderer" (if this.myRenderer is a function)
|
517
|
+
// * ["Some.scope.Format.customRenderer", 10, 20, 30] (if Some.scope.Format.customRenderer is a function)
|
518
|
+
// * "function(v){ return 'Value: ' + v; }"
|
519
|
+
normalizeRenderer: function(c) {
|
520
|
+
if (!c.renderer) return;
|
521
|
+
|
522
|
+
var name, args = [];
|
523
|
+
|
524
|
+
if ('string' === typeof c.renderer) {
|
525
|
+
name = c.renderer;
|
526
|
+
} else {
|
527
|
+
name = c.renderer[0];
|
528
|
+
args = c.renderer.slice(1);
|
529
|
+
}
|
530
|
+
|
531
|
+
// First check whether Ext.util.Format has it
|
532
|
+
if (Ext.isFunction(Ext.util.Format[name])) {
|
533
|
+
c.renderer = Ext.util.Format[name].createDelegate(this, args, 1);
|
534
|
+
} else if (Ext.isFunction(this[name])) {
|
535
|
+
// ... then if our own class has it
|
536
|
+
c.renderer = this[name].createDelegate(this, args, 1);
|
537
|
+
} else {
|
538
|
+
// ... and, as last resort, evaluate it (allows passing inline javascript function as renderer)
|
539
|
+
eval("c.renderer = " + c.renderer + ";");
|
540
|
+
}
|
541
|
+
},
|
542
|
+
|
543
|
+
|
544
|
+
onEdit: function(){
|
545
|
+
var row = this.getSelectionModel().getSelected();
|
546
|
+
if (row){
|
547
|
+
this.tryStartEditing(this.store.indexOf(row));
|
548
|
+
}
|
549
|
+
}
|
550
|
+
|
551
|
+
// :reorder_columns => <<-END_OF_JAVASCRIPT.l,
|
552
|
+
// function(columns){
|
553
|
+
// columnsInNewShipment = [];
|
554
|
+
// Ext.each(columns, function(c){
|
555
|
+
// columnsInNewShipment.push({name:c});
|
556
|
+
// });
|
557
|
+
// newRecordType = Ext.data.Record.create(columnsInNewShipment);
|
558
|
+
// this.store.reader.recordType = newRecordType; // yes, recordType is a protected property, but that's the only way we can do it, and it seems to work for now
|
559
|
+
// }
|
560
|
+
// END_OF_JAVASCRIPT
|
561
|
+
|
562
|
+
});
|