Ext.namespace('Ext.ux.dd'); Ext.ux.dd.GridDragDropRowOrder = Ext.extend(Ext.util.Observable, { copy: false, scrollable: false, constructor : function(config) { Ext.ux.dd.GridDragDropRowOrder.superclass.constructor.call(this); if (config) Ext.apply(this, config); this.addEvents( { beforerowmove: true, afterrowmove: true, beforerowcopy: true, afterrowcopy: true }); }, init : function (grid) { this.grid = grid; grid.enableDragDrop = true; grid.on({ render: { fn: this.onGridRender, scope: this, single: true } }); }, onGridRender : function (grid) { var self = this; this.target = new Ext.dd.DropTarget(grid.getEl(), { ddGroup: grid.ddGroup || 'GridDD', grid: grid, gridDropTarget: this, notifyDrop: function(dd, e, data) { // Remove drag lines. The 'if' condition prevents null error when drop occurs without dragging out of the selection area if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-below'); this.currentRowEl.removeClass('grid-row-insert-above'); } // determine the row var t = Ext.lib.Event.getTarget(e); var rindex = this.grid.getView().findRowIndex(t); if (rindex === false || rindex == data.rowIndex) { return false; } // fire the before move/copy event if (this.gridDropTarget.fireEvent(self.copy ? 'beforerowcopy' : 'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections, 123) === false) { return false; } // update the store var ds = this.grid.getStore(); // Changes for multiselction by Spirit var selections = new Array(); var keys = ds.data.keys; for (var key in keys) { for (var i = 0; i < data.selections.length; i++) { if (keys[key] == data.selections[i].id) { // Exit to prevent drop of selected records on itself. if (rindex == key) { return false; } selections.push(data.selections[i]); } } } // fix rowindex based on before/after move if (rindex > data.rowIndex && this.rowPosition < 0) { rindex--; } if (rindex < data.rowIndex && this.rowPosition > 0) { rindex++; } // fix rowindex for multiselection if (rindex > data.rowIndex && data.selections.length > 1) { rindex = rindex - (data.selections.length - 1); } // we tried to move this node before the next sibling, we stay in place if (rindex == data.rowIndex) { return false; } // fire the before move/copy event /* dupe - does it belong here or above??? if (this.gridDropTarget.fireEvent(self.copy ? 'beforerowcopy' : 'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections, 123) === false) { return false; } */ if (!self.copy) { for (var i = 0; i < data.selections.length; i++) { ds.remove(ds.getById(data.selections[i].id)); } } for (var i = selections.length - 1; i >= 0; i--) { var insertIndex = rindex; ds.insert(insertIndex, selections[i]); } // re-select the row(s) var sm = this.grid.getSelectionModel(); if (sm) { sm.selectRecords(data.selections); } // console.info(this.gridDropTarget); // fire the after move/copy event this.gridDropTarget.fireEvent(self.copy ? 'afterrowcopy' : 'afterrowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections); return true; }, notifyOver: function(dd, e, data) { var t = Ext.lib.Event.getTarget(e); var rindex = this.grid.getView().findRowIndex(t); // Similar to the code in notifyDrop. Filters for selected rows and quits function if any one row matches the current selected row. var ds = this.grid.getStore(); var keys = ds.data.keys; for (var key in keys) { for (var i = 0; i < data.selections.length; i++) { if (keys[key] == data.selections[i].id) { if (rindex == key) { if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-below'); this.currentRowEl.removeClass('grid-row-insert-above'); } return this.dropNotAllowed; } } } } // If on first row, remove upper line. Prevents negative index error as a result of rindex going negative. if (rindex < 0 || rindex === false) { this.currentRowEl.removeClass('grid-row-insert-above'); return this.dropNotAllowed; } try { var currentRow = this.grid.getView().getRow(rindex); // Find position of row relative to page (adjusting for grid's scroll position) var resolvedRow = new Ext.Element(currentRow).getY() - this.grid.getView().scroller.dom.scrollTop; var rowHeight = currentRow.offsetHeight; // Cursor relative to a row. -ve value implies cursor is above the row's middle and +ve value implues cursor is below the row's middle. this.rowPosition = e.getPageY() - resolvedRow - (rowHeight/2); // Clear drag line. if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-below'); this.currentRowEl.removeClass('grid-row-insert-above'); } if (this.rowPosition > 0) { // If the pointer is on the bottom half of the row. this.currentRowEl = new Ext.Element(currentRow); this.currentRowEl.addClass('grid-row-insert-below'); } else { // If the pointer is on the top half of the row. if (rindex - 1 >= 0) { var previousRow = this.grid.getView().getRow(rindex - 1); this.currentRowEl = new Ext.Element(previousRow); this.currentRowEl.addClass('grid-row-insert-below'); } else { // If the pointer is on the top half of the first row. this.currentRowEl.addClass('grid-row-insert-above'); } } } catch (err) { console.warn(err); rindex = false; } return (rindex === false)? this.dropNotAllowed : this.dropAllowed; }, notifyOut: function(dd, e, data) { // Remove drag lines when pointer leaves the gridView. if (this.currentRowEl) { this.currentRowEl.removeClass('grid-row-insert-above'); this.currentRowEl.removeClass('grid-row-insert-below'); } } }); if (this.targetCfg) { Ext.apply(this.target, this.targetCfg); } if (this.scrollable) { Ext.dd.ScrollManager.register(grid.getView().getEditorParent()); grid.on({ beforedestroy: this.onBeforeDestroy, scope: this, single: true }); } }, getTarget: function() { return this.target; }, getGrid: function() { return this.grid; }, getCopy: function() { return this.copy ? true : false; }, setCopy: function(b) { this.copy = b ? true : false; }, onBeforeDestroy : function (grid) { // if we previously registered with the scroll manager, unregister // it (if we don't it will lead to problems in IE) Ext.dd.ScrollManager.unregister(grid.getView().getEditorParent()); } });