Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / grid / Scroller.js
index 5f03158..b917091 100644 (file)
@@ -1,3 +1,17 @@
+/*
+
+This file is part of Ext JS 4
+
+Copyright (c) 2011 Sencha Inc
+
+Contact:  http://www.sencha.com/contact
+
+GNU General Public License Usage
+This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
+
+If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
+
+*/
 /**
  * @class Ext.grid.Scroller
  * @extends Ext.Component
@@ -13,60 +27,79 @@ Ext.define('Ext.grid.Scroller', {
     weight: 110,
     cls: Ext.baseCSSPrefix + 'scroller',
     focusable: false,
-    
-    renderTpl: ['<div class="' + Ext.baseCSSPrefix + 'stretcher"></div>'],
-    
+    reservedSpace: 0,
+
+    renderTpl: [
+        '<div class="' + Ext.baseCSSPrefix + 'scroller-ct" id="{baseId}_ct">',
+            '<div class="' + Ext.baseCSSPrefix + 'stretcher" id="{baseId}_stretch"></div>',
+        '</div>'
+    ],
+
     initComponent: function() {
         var me       = this,
             dock     = me.dock,
             cls      = Ext.baseCSSPrefix + 'scroller-vertical',
-            sizeProp = 'width',
-            // Subtracting 2px would give us a perfect fit of the scroller
-            // however, some browsers wont allow us to scroll content thats not
-            // visible, therefore we use 1px.
-            // Note: This 1px offset matches code in Ext.grid.ColumnLayout when
-            // reserving room for the scrollbar
-            scrollbarWidth = Ext.getScrollBarWidth() + (Ext.isIE ? 1 : -1);
+            sizeProp = 'width';
 
         me.offsets = {bottom: 0};
+        me.scrollProp = 'scrollTop';
+        me.vertical = true;
 
         if (dock === 'top' || dock === 'bottom') {
             cls = Ext.baseCSSPrefix + 'scroller-horizontal';
             sizeProp = 'height';
+            me.scrollProp = 'scrollLeft';
+            me.vertical = false;
+            me.weight += 5;
         }
-        me[sizeProp] = scrollbarWidth;
-        
+
+        me[sizeProp] = me.scrollerSize = Ext.getScrollbarSize()[sizeProp];
+
         me.cls += (' ' + cls);
-        
+
         Ext.applyIf(me.renderSelectors, {
-            stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher'
+            stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher',
+            scrollEl: '.' + Ext.baseCSSPrefix + 'scroller-ct'
         });
         me.callParent();
     },
-    
-    
+
+    initRenderData: function () {
+        var me = this,
+            ret = me.callParent(arguments) || {};
+
+        ret.baseId = me.id;
+
+        return ret;
+    },
+
     afterRender: function() {
         var me = this;
         me.callParent();
-        me.ownerCt.on('afterlayout', me.onOwnerAfterLayout, me);
-        me.mon(me.el, 'scroll', me.onElScroll, me);
+        
+        me.mon(me.scrollEl, 'scroll', me.onElScroll, me);
         Ext.cache[me.el.id].skipGarbageCollection = true;
     },
-    
+
+    onAdded: function(container) {
+        // Capture the controlling grid Panel so that we can use it even when we are undocked, and don't have an ownerCt
+        this.ownerGrid = container;
+        this.callParent(arguments);
+    },
+
     getSizeCalculation: function() {
-        var owner  = this.getPanel(),
-            dock   = this.dock,
-            elDom  = this.el.dom,
+        var me     = this,
+            owner  = me.getPanel(),
             width  = 1,
             height = 1,
             view, tbl;
-            
-        if (dock === 'top' || dock === 'bottom') {
+
+        if (!me.vertical) {
             // TODO: Must gravitate to a single region..
             // Horizontal scrolling only scrolls virtualized region
             var items  = owner.query('tableview'),
                 center = items[1] || items[0];
-            
+
             if (!center) {
                 return false;
             }
@@ -74,25 +107,23 @@ Ext.define('Ext.grid.Scroller', {
             // are zero rows in the grid/tree. We read the width from the
             // headerCt instead.
             width = center.headerCt.getFullWidth();
-            
+
             if (Ext.isIEQuirks) {
                 width--;
             }
-            // Account for the 1px removed in Scroller.
-            width--;
-        } else {            
+        } else {
             view = owner.down('tableview:not([lockableInjected])');
-            if (!view) {
+            if (!view || !view.el) {
                 return false;
             }
-            tbl = view.el;
+            tbl = view.el.child('table', true);
             if (!tbl) {
                 return false;
             }
-            
+
             // needs to also account for header and scroller (if still in picture)
             // should calculate from headerCt.
-            height = tbl.dom.scrollHeight;
+            height = tbl.offsetHeight;
         }
         if (isNaN(width)) {
             width = 1;
@@ -105,34 +136,88 @@ Ext.define('Ext.grid.Scroller', {
             height: height
         };
     },
-    
+
     invalidate: function(firstPass) {
-        if (!this.stretchEl || !this.ownerCt) {
+        var me = this,
+            stretchEl = me.stretchEl;
+
+        if (!stretchEl || !me.ownerCt) {
             return;
         }
-        var size  = this.getSizeCalculation(),
-            elDom = this.el.dom;
+
+        var size  = me.getSizeCalculation(),
+            scrollEl = me.scrollEl,
+            elDom = scrollEl.dom,
+            reservedSpace = me.reservedSpace,
+            pos,
+            extra = 5;
+
         if (size) {
-            this.stretchEl.setSize(size);
-        
+            stretchEl.setSize(size);
+
+            size = me.el.getSize(true);
+
+            if (me.vertical) {
+                size.width += extra;
+                size.height -= reservedSpace;
+                pos = 'left';
+            } else {
+                size.width -= reservedSpace;
+                size.height += extra;
+                pos = 'top';
+            }
+
+            scrollEl.setSize(size);
+            elDom.style[pos] = (-extra) + 'px';
+
             // BrowserBug: IE7
             // This makes the scroller enabled, when initially rendering.
             elDom.scrollTop = elDom.scrollTop;
         }
     },
 
-    onOwnerAfterLayout: function(owner, layout) {
+    afterComponentLayout: function() {
+        this.callParent(arguments);
         this.invalidate();
     },
 
+    restoreScrollPos: function () {
+        var me = this,
+            el = this.scrollEl,
+            elDom = el && el.dom;
+
+        if (me._scrollPos !== null && elDom) {
+            elDom[me.scrollProp] = me._scrollPos;
+            me._scrollPos = null;
+        }
+    },
+
+    setReservedSpace: function (reservedSpace) {
+        var me = this;
+        if (me.reservedSpace !== reservedSpace) {
+            me.reservedSpace = reservedSpace;
+            me.invalidate();
+        }
+    },
+
+    saveScrollPos: function () {
+        var me = this,
+            el = this.scrollEl,
+            elDom = el && el.dom;
+
+        me._scrollPos = elDom ? elDom[me.scrollProp] : null;
+    },
+
     /**
      * Sets the scrollTop and constrains the value between 0 and max.
      * @param {Number} scrollTop
      * @return {Number} The resulting scrollTop value after being constrained
      */
     setScrollTop: function(scrollTop) {
-        if (this.el) {
-            var elDom = this.el.dom;
+        var el = this.scrollEl,
+            elDom = el && el.dom;
+
+        if (elDom) {
             return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight);
         }
     },
@@ -143,8 +228,10 @@ Ext.define('Ext.grid.Scroller', {
      * @return {Number} The resulting scrollLeft value after being constrained
      */
     setScrollLeft: function(scrollLeft) {
-        if (this.el) {
-            var elDom = this.el.dom;
+        var el = this.scrollEl,
+            elDom = el && el.dom;
+
+        if (elDom) {
             return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth);
         }
     },
@@ -155,8 +242,10 @@ Ext.define('Ext.grid.Scroller', {
      * @return {Number} The resulting scrollTop value
      */
     scrollByDeltaY: function(delta) {
-        if (this.el) {
-            var elDom = this.el.dom;
+        var el = this.scrollEl,
+            elDom = el && el.dom;
+
+        if (elDom) {
             return this.setScrollTop(elDom.scrollTop + delta);
         }
     },
@@ -167,20 +256,22 @@ Ext.define('Ext.grid.Scroller', {
      * @return {Number} The resulting scrollLeft value
      */
     scrollByDeltaX: function(delta) {
-        if (this.el) {
-            var elDom = this.el.dom;
+        var el = this.scrollEl,
+            elDom = el && el.dom;
+
+        if (elDom) {
             return this.setScrollLeft(elDom.scrollLeft + delta);
         }
     },
-    
-    
+
+
     /**
      * Scroll to the top.
      */
     scrollToTop : function(){
         this.setScrollTop(0);
     },
-    
+
     // synchronize the scroller with the bound gridviews
     onElScroll: function(event, target) {
         this.fireEvent('bodyscroll', event, target);
@@ -195,3 +286,4 @@ Ext.define('Ext.grid.Scroller', {
     }
 });
 
+