    // A DropZone which cooperates with DragZones whose dragData contains
    // a "field" property representing a form Field. Fields may be dropped onto
    // grid data cells containing a matching data type.
    Ext.define('Ext.ux.dd.CellFieldDropZone', {
        extend: 'Ext.dd.DropZone',

        constructor: function(cfg){
            cfg = cfg || {};
            if (cfg.onCellDrop) {
                this.onCellDrop = cfg.onCellDrop;
            }
            if (cfg.ddGroup) {
                this.ddGroup = cfg.ddGroup;
            }
        },

    //  Call the DropZone constructor using the View's scrolling element
    //  only after the grid has been rendered.
        init: function(grid) {
            var me = this;

            if (grid.rendered) {
                me.grid = grid;
                grid.getView().on({
                    render: function(v) {
                        me.view = v;
                        Ext.ux.dd.CellFieldDropZone.superclass.constructor.call(me, me.view.el);
                    },
                    single: true
                });
            } else {
                grid.on('render', me.init, me, {single: true});
            }
        },

    //  Scroll the main configured Element when we drag close to the edge
        containerScroll: true,

        getTargetFromEvent: function(e) {
            var me = this,
                v = me.view;

    //      Ascertain whether the mousemove is within a grid cell
            var cell = e.getTarget(v.getCellSelector());
            if (cell) {

    //          We *are* within a grid cell, so ask the View exactly which one,
    //          Extract data from the Model to create a target object for
    //          processing in subsequent onNodeXXXX methods. Note that the target does
    //          not have to be a DOM element. It can be whatever the noNodeXXX methods are
    //          programmed to expect.
                var row = v.findItemByChild(cell),
                    columnIndex = cell.cellIndex;

                if (row && Ext.isDefined(columnIndex)) {
                    return {
                        node: cell,
                        record: v.getRecord(row),
                        fieldName: me.grid.columns[columnIndex].dataIndex
                    };
                }
            }
        },

    //  On Node enter, see if it is valid for us to drop the field on that type of column.
        onNodeEnter: function(target, dd, e, dragData) {
            delete this.dropOK;
            if (!target) {
                return;
            }

    //      Check that a field is being dragged.
            var f = dragData.field;
            if (!f) {
                return;
            }

    //      Check whether the data type of the column being dropped on accepts the
    //      dragged field type. If so, set dropOK flag, and highlight the target node.
            var type = target.record.fields.get(target.fieldName).type,
                types = Ext.data.Types;
            switch(type){
                case types.FLOAT:
                case types.INT:
                    if (!f.isXType('numberfield')) {
                        return;
                    }
                    break;
                case types.DATE:
                    if (!f.isXType('datefield')) {
                        return;
                    }
                    break;
                case types.BOOL:
                    if (!f.isXType('checkbox')) {
                        return;
                    }
            }
            this.dropOK = true;
            Ext.fly(target.node).addCls('x-drop-target-active');
        },

    //  Return the class name to add to the drag proxy. This provides a visual indication
    //  of drop allowed or not allowed.
        onNodeOver: function(target, dd, e, dragData) {
            return this.dropOK ? this.dropAllowed : this.dropNotAllowed;
        },

    //  highlight the target node.
        onNodeOut: function(target, dd, e, dragData) {
            Ext.fly(target.node).removeCls('x-drop-target-active');
        },

    //  Process the drop event if we have previously ascertained that a drop is OK.
        onNodeDrop: function(target, dd, e, dragData) {
            if (this.dropOK) {
                var value = dragData.field.getValue();
                target.record.set(target.fieldName, value);
                this.onCellDrop(target.fieldName, value);
                return true;
            }
        },

        onCellDrop: Ext.emptyFn
    });