Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / examples / ux / Portal.js
1 /*!
2  * Ext JS Library 3.3.1
3  * Copyright(c) 2006-2010 Sencha Inc.
4  * licensing@sencha.com
5  * http://www.sencha.com/license
6  */
7 Ext.ux.Portal = Ext.extend(Ext.Panel, {
8     layout : 'column',
9     autoScroll : true,
10     cls : 'x-portal',
11     defaultType : 'portalcolumn',
12     
13     initComponent : function(){
14         Ext.ux.Portal.superclass.initComponent.call(this);
15         this.addEvents({
16             validatedrop:true,
17             beforedragover:true,
18             dragover:true,
19             beforedrop:true,
20             drop:true
21         });
22     },
23
24     initEvents : function(){
25         Ext.ux.Portal.superclass.initEvents.call(this);
26         this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig);
27     },
28     
29     beforeDestroy : function() {
30         if(this.dd){
31             this.dd.unreg();
32         }
33         Ext.ux.Portal.superclass.beforeDestroy.call(this);
34     }
35 });
36
37 Ext.reg('portal', Ext.ux.Portal);
38
39 Ext.ux.Portal.DropZone = Ext.extend(Ext.dd.DropTarget, {
40     
41     constructor : function(portal, cfg){
42         this.portal = portal;
43         Ext.dd.ScrollManager.register(portal.body);
44         Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg);
45         portal.body.ddScrollConfig = this.ddScrollConfig;
46     },
47     
48     ddScrollConfig : {
49         vthresh: 50,
50         hthresh: -1,
51         animate: true,
52         increment: 200
53     },
54
55     createEvent : function(dd, e, data, col, c, pos){
56         return {
57             portal: this.portal,
58             panel: data.panel,
59             columnIndex: col,
60             column: c,
61             position: pos,
62             data: data,
63             source: dd,
64             rawEvent: e,
65             status: this.dropAllowed
66         };
67     },
68
69     notifyOver : function(dd, e, data){
70         var xy = e.getXY(), portal = this.portal, px = dd.proxy;
71
72         // case column widths
73         if(!this.grid){
74             this.grid = this.getGrid();
75         }
76
77         // handle case scroll where scrollbars appear during drag
78         var cw = portal.body.dom.clientWidth;
79         if(!this.lastCW){
80             this.lastCW = cw;
81         }else if(this.lastCW != cw){
82             this.lastCW = cw;
83             portal.doLayout();
84             this.grid = this.getGrid();
85         }
86
87         // determine column
88         var col = 0, xs = this.grid.columnX, cmatch = false;
89         for(var len = xs.length; col < len; col++){
90             if(xy[0] < (xs[col].x + xs[col].w)){
91                 cmatch = true;
92                 break;
93             }
94         }
95         // no match, fix last index
96         if(!cmatch){
97             col--;
98         }
99
100         // find insert position
101         var p, match = false, pos = 0,
102             c = portal.items.itemAt(col),
103             items = c.items.items, overSelf = false;
104
105         for(var len = items.length; pos < len; pos++){
106             p = items[pos];
107             var h = p.el.getHeight();
108             if(h === 0){
109                 overSelf = true;
110             }
111             else if((p.el.getY()+(h/2)) > xy[1]){
112                 match = true;
113                 break;
114             }
115         }
116
117         pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0);
118         var overEvent = this.createEvent(dd, e, data, col, c, pos);
119
120         if(portal.fireEvent('validatedrop', overEvent) !== false &&
121            portal.fireEvent('beforedragover', overEvent) !== false){
122
123             // make sure proxy width is fluid
124             px.getProxy().setWidth('auto');
125
126             if(p){
127                 px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
128             }else{
129                 px.moveProxy(c.el.dom, null);
130             }
131
132             this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false};
133             this.scrollPos = portal.body.getScroll();
134
135             portal.fireEvent('dragover', overEvent);
136
137             return overEvent.status;
138         }else{
139             return overEvent.status;
140         }
141
142     },
143
144     notifyOut : function(){
145         delete this.grid;
146     },
147
148     notifyDrop : function(dd, e, data){
149         delete this.grid;
150         if(!this.lastPos){
151             return;
152         }
153         var c = this.lastPos.c, 
154             col = this.lastPos.col, 
155             pos = this.lastPos.p,
156             panel = dd.panel,
157             dropEvent = this.createEvent(dd, e, data, col, c,
158                 pos !== false ? pos : c.items.getCount());
159
160         if(this.portal.fireEvent('validatedrop', dropEvent) !== false &&
161            this.portal.fireEvent('beforedrop', dropEvent) !== false){
162
163             dd.proxy.getProxy().remove();
164             panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
165             
166             if(pos !== false){
167                 c.insert(pos, panel);
168             }else{
169                 c.add(panel);
170             }
171             
172             c.doLayout();
173
174             this.portal.fireEvent('drop', dropEvent);
175
176             // scroll position is lost on drop, fix it
177             var st = this.scrollPos.top;
178             if(st){
179                 var d = this.portal.body.dom;
180                 setTimeout(function(){
181                     d.scrollTop = st;
182                 }, 10);
183             }
184
185         }
186         delete this.lastPos;
187     },
188
189     // internal cache of body and column coords
190     getGrid : function(){
191         var box = this.portal.bwrap.getBox();
192         box.columnX = [];
193         this.portal.items.each(function(c){
194              box.columnX.push({x: c.el.getX(), w: c.el.getWidth()});
195         });
196         return box;
197     },
198
199     // unregister the dropzone from ScrollManager
200     unreg: function() {
201         Ext.dd.ScrollManager.unregister(this.portal.body);
202         Ext.ux.Portal.DropZone.superclass.unreg.call(this);
203     }
204 });