Ext.namespace('Ext.ux.dd');



Ext.ux.dd.GridDragDropRowOrder = Ext.extend(Ext.util.Observable,

{

    copy: false,



    scrollable: false,



    constructor : function(config)

    {

        if (config)

            Ext.apply(this, config);



        this.addEvents(

        {

            beforerowmove: true,

            afterrowmove: true,

            beforerowcopy: true,

            afterrowcopy: true

        });



       Ext.ux.dd.GridDragDropRowOrder.superclass.constructor.call(this);

    },



    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.getView().mainBody,

        {

            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);

                }



                // 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());

    }

});