3 <title>The source code</title>
4 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
5 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 <body onload="prettyPrint();">
8 <pre class="prettyprint lang-js">Ext.ns('Ext.ux.grid');
10 <div id="cls-Ext.ux.grid.RowEditor"></div>/**
11 * @class Ext.ux.grid.RowEditor
13 * Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid.
14 * A validation mode may be enabled which uses AnchorTips to notify the user of all
15 * validation errors at once.
19 Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, {
23 cls: 'x-small-editor',
24 buttonAlign: 'center',
25 baseCls: 'x-row-editor',
26 elements: 'header,footer,body',
36 commitChangesText: 'You need to commit or cancel your changes',
43 initComponent: function(){
44 Ext.ux.grid.RowEditor.superclass.initComponent.call(this);
46 <div id="event-Ext.ux.grid.RowEditor-beforeedit"></div>/**
48 * Fired before the row editor is activated.
49 * If the listener returns <tt>false</tt> the editor will not be activated.
50 * @param {Ext.ux.grid.RowEditor} roweditor This object
51 * @param {Number} rowIndex The rowIndex of the row just edited
54 <div id="event-Ext.ux.grid.RowEditor-canceledit"></div>/**
56 * Fired when the editor is cancelled.
57 * @param {Ext.ux.grid.RowEditor} roweditor This object
58 * @param {Boolean} forced True if the cancel button is pressed, false is the editor was invalid.
61 <div id="event-Ext.ux.grid.RowEditor-validateedit"></div>/**
63 * Fired after a row is edited and passes validation.
64 * If the listener returns <tt>false</tt> changes to the record will not be set.
65 * @param {Ext.ux.grid.RowEditor} roweditor This object
66 * @param {Object} changes Object with changes made to the record.
67 * @param {Ext.data.Record} r The Record that was edited.
68 * @param {Number} rowIndex The rowIndex of the row just edited
71 <div id="event-Ext.ux.grid.RowEditor-afteredit"></div>/**
73 * Fired after a row is edited and passes validation. This event is fired
74 * after the store's update event is fired with this edit.
75 * @param {Ext.ux.grid.RowEditor} roweditor This object
76 * @param {Object} changes Object with changes made to the record.
77 * @param {Ext.data.Record} r The Record that was edited.
78 * @param {Number} rowIndex The rowIndex of the row just edited
87 if(this.clicksToEdit === 2){
88 grid.on('rowdblclick', this.onRowDblClick, this);
90 grid.on('rowclick', this.onRowClick, this);
92 grid.on('rowdblclick', this.onRowDblClick, this);
96 // stopEditing without saving when a record is removed from Store.
97 grid.getStore().on('remove', function() {
98 this.stopEditing(false);
103 keydown: this.onGridKey,
104 columnresize: this.verifyLayout,
105 columnmove: this.refreshFields,
106 reconfigure: this.refreshFields,
107 beforedestroy : this.beforedestroy,
108 destroy : this.destroy,
111 fn: this.positionButtons
114 grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, {delay:1});
115 grid.getView().on('refresh', this.stopEditing.createDelegate(this, []));
118 beforedestroy: function() {
119 this.grid.getStore().un('remove', this.onStoreRemove, this);
120 this.stopEditing(false);
121 Ext.destroy(this.btns);
124 refreshFields: function(){
131 this.items.each(function(f){
132 if(String(this.values[f.id]) !== String(f.getValue())){
140 startEditing: function(rowIndex, doFocus){
141 if(this.editing && this.isDirty()){
142 this.showTooltip(this.commitChangesText);
145 if(Ext.isObject(rowIndex)){
146 rowIndex = this.grid.getStore().indexOf(rowIndex);
148 if(this.fireEvent('beforeedit', this, rowIndex) !== false){
150 var g = this.grid, view = g.getView(),
151 row = view.getRow(rowIndex),
152 record = g.store.getAt(rowIndex);
154 this.record = record;
155 this.rowIndex = rowIndex;
158 this.render(view.getEditorParent());
160 var w = Ext.fly(row).getWidth();
162 if(!this.initialized){
165 var cm = g.getColumnModel(), fields = this.items.items, f, val;
166 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
167 val = this.preEditValue(record, cm.getDataIndex(i));
170 this.values[f.id] = Ext.isEmpty(val) ? '' : val;
172 this.verifyLayout(true);
173 if(!this.isVisible()){
174 this.setPagePosition(Ext.fly(row).getXY());
176 this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
178 if(!this.isVisible()){
179 this.show().doLayout();
181 if(doFocus !== false){
182 this.doFocus.defer(this.focusDelay, this);
187 stopEditing : function(saveChanges){
188 this.editing = false;
189 if(!this.isVisible()){
192 if(saveChanges === false || !this.isValid()){
194 this.fireEvent('canceledit', this, saveChanges === false);
200 cm = this.grid.colModel,
201 fields = this.items.items;
202 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
204 var dindex = cm.getDataIndex(i);
205 if(!Ext.isEmpty(dindex)){
206 var oldValue = r.data[dindex],
207 value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
208 if(String(oldValue) !== String(value)){
209 changes[dindex] = value;
215 if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){
217 Ext.iterate(changes, function(name, value){
221 this.fireEvent('afteredit', this, changes, r, this.rowIndex);
226 verifyLayout: function(force){
227 if(this.el && (this.isVisible() || force === true)){
228 var row = this.grid.getView().getRow(this.rowIndex);
229 this.setSize(Ext.fly(row).getWidth(), Ext.fly(row).getHeight() + 9);
230 var cm = this.grid.colModel, fields = this.items.items;
231 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
235 adjust += 3; // outer padding
240 fields[i].setWidth(cm.getColumnWidth(i) - adjust);
246 this.positionButtons();
250 slideHide : function(){
254 initFields: function(){
255 var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins;
256 this.removeAll(false);
257 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
258 var c = cm.getColumnAt(i),
261 ed = c.displayEditor || new Ext.form.DisplayField();
264 ed.margins = pm('0 1 2 1');
265 } else if(i == len - 1){
266 ed.margins = pm('0 0 2 1');
268 ed.margins = pm('0 1 2');
270 ed.setWidth(cm.getColumnWidth(i));
272 if(ed.ownerCt !== this){
273 ed.on('focus', this.ensureVisible, this);
274 ed.on('specialkey', this.onKey, this);
278 this.initialized = true;
281 onKey: function(f, e){
282 if(e.getKey() === e.ENTER){
283 this.stopEditing(true);
288 onGridKey: function(e){
289 if(e.getKey() === e.ENTER && !this.isVisible()){
290 var r = this.grid.getSelectionModel().getSelected();
292 var index = this.grid.store.indexOf(r);
293 this.startEditing(index);
299 ensureVisible: function(editor){
300 if(this.isVisible()){
301 this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);
305 onRowClick: function(g, rowIndex, e){
306 if(this.clicksToEdit == 'auto'){
307 var li = this.lastClickIndex;
308 this.lastClickIndex = rowIndex;
309 if(li != rowIndex && !this.isVisible()){
313 this.startEditing(rowIndex, false);
314 this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
317 onRowDblClick: function(g, rowIndex, e){
318 this.startEditing(rowIndex, false);
319 this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
322 onRender: function(){
323 Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments);
324 this.el.swallowEvent(['keydown', 'keyup', 'keypress']);
325 this.btns = new Ext.Panel({
330 width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE
336 width: this.minButtonWidth,
337 handler: this.stopEditing.createDelegate(this, [true])
340 text: this.cancelText,
341 width: this.minButtonWidth,
342 handler: this.stopEditing.createDelegate(this, [false])
345 this.btns.render(this.bwrap);
348 afterRender: function(){
349 Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments);
350 this.positionButtons();
351 if(this.monitorValid){
352 this.startMonitoring();
357 if(this.monitorValid){
358 this.startMonitoring();
360 Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments);
364 Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments);
365 this.stopMonitoring();
366 this.grid.getView().focusRow(this.rowIndex);
369 positionButtons: function(){
372 h = this.el.dom.clientHeight,
374 scroll = view.scroller.dom.scrollLeft,
375 bw = this.btns.getWidth(),
376 width = Math.min(g.getWidth(), g.getColumnModel().getTotalWidth());
378 this.btns.el.shift({left: (width/2)-(bw/2)+scroll, top: h - 2, stopFx: true, duration:0.2});
383 preEditValue : function(r, field){
384 var value = r.data[field];
385 return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value;
389 postEditValue : function(value, originalValue, r, field){
390 return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
393 doFocus: function(pt){
394 if(this.isVisible()){
396 cm = this.grid.getColumnModel(),
399 index = this.getTargetColumnIndex(pt);
401 for(var i = index||0, len = cm.getColumnCount(); i < len; i++){
402 c = cm.getColumnAt(i);
403 if(!c.hidden && c.getEditor()){
404 c.getEditor().focus();
411 getTargetColumnIndex: function(pt){
412 var grid = this.grid,
415 cms = grid.colModel.config,
418 for(var len = cms.length, c; c = cms[i]; i++){
420 if(Ext.fly(v.getHeaderCell(i)).getRegion().right >= x){
429 startMonitoring : function(){
430 if(!this.bound && this.monitorValid){
433 run : this.bindHandler,
434 interval : this.monitorPoll || 200,
440 stopMonitoring : function(){
449 this.items.each(function(f){
450 if(!f.isValid(true)){
459 bindHandler : function(){
461 return false; // stops binding
463 var valid = this.isValid();
464 if(!valid && this.errorSummary){
465 this.showTooltip(this.getErrorText().join(''));
467 this.btns.saveBtn.setDisabled(!valid);
468 this.fireEvent('validation', this, valid);
471 showTooltip: function(msg){
472 var t = this.tooltip;
474 t = this.tooltip = new Ext.ToolTip({
478 title: this.errorText,
481 anchorToTarget: true,
485 var v = this.grid.getView(),
486 top = parseInt(this.el.dom.style.top, 10),
487 scroll = v.scroller.dom.scrollTop,
488 h = this.el.getHeight();
490 if(top + h >= scroll){
491 t.initTarget(this.items.last().getEl());
499 }else if(t.rendered){
504 getErrorText: function(){
506 this.items.each(function(f){
507 if(!f.isValid(true)){
508 data.push('<li>', f.activeError, '</li>');
515 Ext.preg('roweditor', Ext.ux.grid.RowEditor);
517 Ext.override(Ext.form.Field, {
518 markInvalid : function(msg){
519 if(!this.rendered || this.preventMark){ // not rendered
522 msg = msg || this.invalidText;
524 var mt = this.getMessageHandler();
527 }else if(this.msgTarget){
528 this.el.addClass(this.invalidClass);
529 var t = Ext.getDom(this.msgTarget);
532 t.style.display = this.msgDisplay;
535 this.activeError = msg;
536 this.fireEvent('invalid', this, msg);
540 Ext.override(Ext.ToolTip, {
541 doAutoWidth : function(){
542 var bw = this.body.getTextWidth();
544 bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
546 bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + 20;
547 this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
549 // IE7 repaint bug on initial show
550 if(Ext.isIE7 && !this.repainted){
552 this.repainted = true;