text: Ext.define('ExtModules.Layouts.Portal.PortalDropZone', {
      extend: 'Ext.dd.DropTarget',
  
      constructor: function(portal, cfg) {
          this.portal = portal;
          Ext.dd.ScrollManager.register(portal.body);
          ExtModules.Layouts.Portal.PortalDropZone.superclass.constructor.call(this, portal.body, cfg);
          portal.body.ddScrollConfig = this.ddScrollConfig;
      },
  
      ddScrollConfig: {
          vthresh: 50,
          hthresh: -1,
          animate: true,
          increment: 200
      },
  
      createEvent: function(dd, e, data, col, c, pos) {
          return {
              portal: this.portal,
              panel: data.panel,
              columnIndex: col,
              column: c,
              position: pos,
              data: data,
              source: dd,
              rawEvent: e,
              status: this.dropAllowed
          };
      },
  
      notifyOver: function(dd, e, data) {
          var xy = e.getXY(),
              portal = this.portal,
              proxy = dd.proxy;
  
          // case column widths
          if (!this.grid) {
              this.grid = this.getGrid();
          }
  
          // handle case scroll where scrollbars appear during drag
          var cw = portal.body.dom.clientWidth;
          if (!this.lastCW) {
              // set initial client width
              this.lastCW = cw;
          } else if (this.lastCW != cw) {
              // client width has changed, so refresh layout & grid calcs
              this.lastCW = cw;
              //portal.doLayout();
              this.grid = this.getGrid();
          }
  
          // determine column
          var colIndex = 0,
              colRight = 0,
              cols = this.grid.columnX,
              len = cols.length,
              cmatch = false;
  
          for (len; colIndex < len; colIndex++) {
              colRight = cols[colIndex].x + cols[colIndex].w;
              if (xy[0] < colRight) {
                  cmatch = true;
                  break;
              }
          }
          // no match, fix last index
          if (!cmatch) {
              colIndex--;
          }
  
          // find insert position
          var overPortlet, pos = 0;
          var h = 0;
          var match = false;
          var overColumn = portal.items.getAt(colIndex);
          if (overColumn.xtype != 'splitter') {
  			var portlets = overColumn.items.items;
  			var overSelf = false;
  
  			len = portlets.length;
          	for (len; pos < len; pos++) {
          		overPortlet = portlets[pos];
          		h = overPortlet.el.getHeight();
          		if (h === 0) {
          			overSelf = true;
          		} else if ((overPortlet.el.getY() + (h / 2)) > xy[1]) {
          			match = true;
          			break;
          		}
          	}
  
          	pos = (match && overPortlet ? pos : overColumn.items.getCount()) + (overSelf ? -1 : 0);
          	var overEvent = this.createEvent(dd, e, data, colIndex, overColumn, pos);
  
          	if (portal.fireEvent('validatedrop', overEvent) !== false && portal.fireEvent('beforedragover', overEvent) !== false) {
  
          		// make sure proxy width is fluid in different width columns
          		dd.panelProxy.getProxy().setWidth(overColumn.width);
          		if (overPortlet) {
          			dd.panelProxy.moveProxy(overPortlet.el.dom.parentNode, match ? overPortlet.el.dom : null);
          		} else {
          			dd.panelProxy.moveProxy(overColumn.el.dom, null);
          		}
  
          		this.lastPos = {
          			c: overColumn,
          			col: colIndex,
          			p: overSelf || (match && overPortlet) ? pos : false
          		};
          		this.scrollPos = portal.body.getScroll();
  
          		portal.fireEvent('dragover', overEvent);
          		return overEvent.status;
          	} else {
          		return overEvent.status;
          	}
          }
      },
  
      notifyOut: function() {
          delete this.grid;
      },
  
      notifyDrop: function(dd, e, data) {
          delete this.grid;
          if (!this.lastPos) {
              return;
          }
          var c = this.lastPos.c,
              col = this.lastPos.col,
              pos = this.lastPos.p,
              panel = dd.panel,
              dropEvent = this.createEvent(dd, e, data, col, c, pos !== false ? pos : c.items.getCount());
  
          if (this.portal.fireEvent('validatedrop', dropEvent) !== false && 
              this.portal.fireEvent('beforedrop', dropEvent) !== false) {
  
              Ext.suspendLayouts();
              
              // make sure panel is visible prior to inserting so that the layout doesn't ignore it
              panel.el.dom.style.display = '';
              dd.panelProxy.hide();
              dd.proxy.hide();
  
              if (pos !== false) {
                  c.insert(pos, panel);
              } else {
                  c.add(panel);
              }
  
              Ext.resumeLayouts(true);
  
              this.portal.fireEvent('drop', dropEvent);
  
              // scroll position is lost on drop, fix it
              var st = this.scrollPos.top;
              if (st) {
                  var d = this.portal.body.dom;
                  setTimeout(function() {
                      d.scrollTop = st;
                  },
                  10);
              }
          }
          
          delete this.lastPos;
          return true;
      },
  
      // internal cache of body and column coords
      getGrid: function() {
          var box = this.portal.body.getBox();
          box.columnX = [];
          this.portal.items.each(function(c) {
              box.columnX.push({
                  x: c.el.getX(),
                  w: c.el.getWidth()
              });
          });
          return box;
      },
  
      // unregister the dropzone from ScrollManager
      unreg: function() {
          Ext.dd.ScrollManager.unregister(this.portal.body);
          ExtModules.Layouts.Portal.PortalDropZone.superclass.unreg.call(this);
      }
  });
  