Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / grid / Scroller.js
1 /**
2  * @class Ext.grid.Scroller
3  * @extends Ext.Component
4  *
5  * Docked in an Ext.grid.Panel, controls virtualized scrolling and synchronization
6  * across different sections.
7  *
8  * @private
9  */
10 Ext.define('Ext.grid.Scroller', {
11     extend: 'Ext.Component',
12     alias: 'widget.gridscroller',
13     weight: 110,
14     cls: Ext.baseCSSPrefix + 'scroller',
15     focusable: false,
16     
17     renderTpl: ['<div class="' + Ext.baseCSSPrefix + 'stretcher"></div>'],
18     
19     initComponent: function() {
20         var me       = this,
21             dock     = me.dock,
22             cls      = Ext.baseCSSPrefix + 'scroller-vertical',
23             sizeProp = 'width',
24             // Subtracting 2px would give us a perfect fit of the scroller
25             // however, some browsers wont allow us to scroll content thats not
26             // visible, therefore we use 1px.
27             // Note: This 1px offset matches code in Ext.grid.ColumnLayout when
28             // reserving room for the scrollbar
29             scrollbarWidth = Ext.getScrollBarWidth() + (Ext.isIE ? 1 : -1);
30
31         me.offsets = {bottom: 0};
32
33         if (dock === 'top' || dock === 'bottom') {
34             cls = Ext.baseCSSPrefix + 'scroller-horizontal';
35             sizeProp = 'height';
36         }
37         me[sizeProp] = scrollbarWidth;
38         
39         me.cls += (' ' + cls);
40         
41         Ext.applyIf(me.renderSelectors, {
42             stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher'
43         });
44         me.callParent();
45     },
46     
47     
48     afterRender: function() {
49         var me = this;
50         me.callParent();
51         me.ownerCt.on('afterlayout', me.onOwnerAfterLayout, me);
52         me.mon(me.el, 'scroll', me.onElScroll, me);
53         Ext.cache[me.el.id].skipGarbageCollection = true;
54     },
55     
56     getSizeCalculation: function() {
57         var owner  = this.getPanel(),
58             dock   = this.dock,
59             elDom  = this.el.dom,
60             width  = 1,
61             height = 1,
62             view, tbl;
63             
64         if (dock === 'top' || dock === 'bottom') {
65             // TODO: Must gravitate to a single region..
66             // Horizontal scrolling only scrolls virtualized region
67             var items  = owner.query('tableview'),
68                 center = items[1] || items[0];
69             
70             if (!center) {
71                 return false;
72             }
73             // center is not guaranteed to have content, such as when there
74             // are zero rows in the grid/tree. We read the width from the
75             // headerCt instead.
76             width = center.headerCt.getFullWidth();
77             
78             if (Ext.isIEQuirks) {
79                 width--;
80             }
81             // Account for the 1px removed in Scroller.
82             width--;
83         } else {            
84             view = owner.down('tableview:not([lockableInjected])');
85             if (!view) {
86                 return false;
87             }
88             tbl = view.el;
89             if (!tbl) {
90                 return false;
91             }
92             
93             // needs to also account for header and scroller (if still in picture)
94             // should calculate from headerCt.
95             height = tbl.dom.scrollHeight;
96         }
97         if (isNaN(width)) {
98             width = 1;
99         }
100         if (isNaN(height)) {
101             height = 1;
102         }
103         return {
104             width: width,
105             height: height
106         };
107     },
108     
109     invalidate: function(firstPass) {
110         if (!this.stretchEl || !this.ownerCt) {
111             return;
112         }
113         var size  = this.getSizeCalculation(),
114             elDom = this.el.dom;
115         if (size) {
116             this.stretchEl.setSize(size);
117         
118             // BrowserBug: IE7
119             // This makes the scroller enabled, when initially rendering.
120             elDom.scrollTop = elDom.scrollTop;
121         }
122     },
123
124     onOwnerAfterLayout: function(owner, layout) {
125         this.invalidate();
126     },
127
128     /**
129      * Sets the scrollTop and constrains the value between 0 and max.
130      * @param {Number} scrollTop
131      * @return {Number} The resulting scrollTop value after being constrained
132      */
133     setScrollTop: function(scrollTop) {
134         if (this.el) {
135             var elDom = this.el.dom;
136             return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight);
137         }
138     },
139
140     /**
141      * Sets the scrollLeft and constrains the value between 0 and max.
142      * @param {Number} scrollLeft
143      * @return {Number} The resulting scrollLeft value after being constrained
144      */
145     setScrollLeft: function(scrollLeft) {
146         if (this.el) {
147             var elDom = this.el.dom;
148             return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth);
149         }
150     },
151
152     /**
153      * Scroll by deltaY
154      * @param {Number} delta
155      * @return {Number} The resulting scrollTop value
156      */
157     scrollByDeltaY: function(delta) {
158         if (this.el) {
159             var elDom = this.el.dom;
160             return this.setScrollTop(elDom.scrollTop + delta);
161         }
162     },
163
164     /**
165      * Scroll by deltaX
166      * @param {Number} delta
167      * @return {Number} The resulting scrollLeft value
168      */
169     scrollByDeltaX: function(delta) {
170         if (this.el) {
171             var elDom = this.el.dom;
172             return this.setScrollLeft(elDom.scrollLeft + delta);
173         }
174     },
175     
176     
177     /**
178      * Scroll to the top.
179      */
180     scrollToTop : function(){
181         this.setScrollTop(0);
182     },
183     
184     // synchronize the scroller with the bound gridviews
185     onElScroll: function(event, target) {
186         this.fireEvent('bodyscroll', event, target);
187     },
188
189     getPanel: function() {
190         var me = this;
191         if (!me.panel) {
192             me.panel = this.up('[scrollerOwner]');
193         }
194         return me.panel;
195     }
196 });
197