<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
- <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
- <script type="text/javascript" src="../prettify/prettify.js"></script>
+ <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
</script>
</head>
<body onload="prettyPrint(); highlight();">
- <pre class="prettyprint lang-js"><span id='Ext-layout-container-boxOverflow-Scroller'>/**
-</span> * @class Ext.layout.container.boxOverflow.Scroller
- * @extends Ext.layout.container.boxOverflow.None
- * @private
+ <pre class="prettyprint lang-js"><span id='Ext-grid-Scroller'>/**
+</span> * Docked in an Ext.grid.Panel, controls virtualized scrolling and synchronization
+ * across different sections.
*/
-Ext.define('Ext.layout.container.boxOverflow.Scroller', {
+Ext.define('Ext.grid.Scroller', {
+ extend: 'Ext.Component',
+ alias: 'widget.gridscroller',
+ weight: 110,
+ baseCls: Ext.baseCSSPrefix + 'scroller',
+ focusable: false,
+ 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';
+
+ me.offsets = {bottom: 0};
+ me.scrollProp = 'scrollTop';
+ me.vertical = true;
+ me.sizeProp = 'width';
+
+ if (dock === 'top' || dock === 'bottom') {
+ cls = Ext.baseCSSPrefix + 'scroller-horizontal';
+ me.sizeProp = 'height';
+ me.scrollProp = 'scrollLeft';
+ me.vertical = false;
+ me.weight += 5;
+ }
- /* Begin Definitions */
+ me.cls += (' ' + cls);
- extend: 'Ext.layout.container.boxOverflow.None',
- requires: ['Ext.util.ClickRepeater', 'Ext.core.Element'],
- alternateClassName: 'Ext.layout.boxOverflow.Scroller',
- mixins: {
- observable: 'Ext.util.Observable'
+ Ext.applyIf(me.renderSelectors, {
+ stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher',
+ scrollEl: '.' + Ext.baseCSSPrefix + 'scroller-ct'
+ });
+ me.callParent();
},
- /* End Definitions */
-
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-animateScroll'> /**
-</span> * @cfg {Boolean} animateScroll
- * True to animate the scrolling of items within the layout (defaults to true, ignored if enableScroll is false)
- */
- animateScroll: false,
-
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-scrollIncrement'> /**
-</span> * @cfg {Number} scrollIncrement
- * The number of pixels to scroll by on scroller click (defaults to 24)
- */
- scrollIncrement: 20,
+ ensureDimension: function(){
+ var me = this,
+ sizeProp = me.sizeProp;
+
+ me[sizeProp] = me.scrollerSize = Ext.getScrollbarSize()[sizeProp];
+ },
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-wheelIncrement'> /**
-</span> * @cfg {Number} wheelIncrement
- * The number of pixels to increment on mouse wheel scrolling (defaults to <tt>3</tt>).
- */
- wheelIncrement: 10,
+ initRenderData: function () {
+ var me = this,
+ ret = me.callParent(arguments) || {};
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-scrollRepeatInterval'> /**
-</span> * @cfg {Number} scrollRepeatInterval
- * Number of milliseconds between each scroll while a scroller button is held down (defaults to 20)
- */
- scrollRepeatInterval: 60,
+ ret.baseId = me.id;
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-scrollDuration'> /**
-</span> * @cfg {Number} scrollDuration
- * Number of milliseconds that each scroll animation lasts (defaults to 400)
- */
- scrollDuration: 400,
+ return ret;
+ },
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-beforeCtCls'> /**
-</span> * @cfg {String} beforeCtCls
- * CSS class added to the beforeCt element. This is the element that holds any special items such as scrollers,
- * which must always be present at the leftmost edge of the Container
- */
+ afterRender: function() {
+ var me = this;
+ me.callParent();
+
+ me.mon(me.scrollEl, 'scroll', me.onElScroll, me);
+ Ext.cache[me.el.id].skipGarbageCollection = true;
+ },
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-afterCtCls'> /**
-</span> * @cfg {String} afterCtCls
- * CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
- * which must always be present at the rightmost edge of the Container
- */
+ 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);
+ },
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-scrollerCls'> /**
-</span> * @cfg {String} scrollerCls
- * CSS class added to both scroller elements if enableScroll is used
- */
- scrollerCls: Ext.baseCSSPrefix + 'box-scroller',
+ getSizeCalculation: function() {
+ var me = this,
+ owner = me.getPanel(),
+ width = 1,
+ height = 1,
+ view, tbl;
+
+ 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;
+ }
+ // center is not guaranteed to have content, such as when there
+ // are zero rows in the grid/tree. We read the width from the
+ // headerCt instead.
+ width = center.headerCt.getFullWidth();
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-beforeScrollerCls'> /**
-</span> * @cfg {String} beforeScrollerCls
- * CSS class added to the left scroller element if enableScroll is used
- */
+ if (Ext.isIEQuirks) {
+ width--;
+ }
+ } else {
+ view = owner.down('tableview:not([lockableInjected])');
+ if (!view || !view.el) {
+ return false;
+ }
+ tbl = view.el.child('table', true);
+ if (!tbl) {
+ return false;
+ }
-<span id='Ext-layout-container-boxOverflow-Scroller-cfg-afterScrollerCls'> /**
-</span> * @cfg {String} afterScrollerCls
- * CSS class added to the right scroller element if enableScroll is used
- */
-
- constructor: function(layout, config) {
- this.layout = layout;
- Ext.apply(this, config || {});
-
- this.addEvents(
-<span id='Ext-layout-container-boxOverflow-Scroller-event-scroll'> /**
-</span> * @event scroll
- * @param {Ext.layout.container.boxOverflow.Scroller} scroller The layout scroller
- * @param {Number} newPosition The new position of the scroller
- * @param {Boolean/Object} animate If animating or not. If true, it will be a animation configuration, else it will be false
- */
- 'scroll'
- );
- },
-
- initCSSClasses: function() {
- var me = this,
- layout = me.layout;
-
- if (!me.CSSinitialized) {
- me.beforeCtCls = me.beforeCtCls || Ext.baseCSSPrefix + 'box-scroller-' + layout.parallelBefore;
- me.afterCtCls = me.afterCtCls || Ext.baseCSSPrefix + 'box-scroller-' + layout.parallelAfter;
- me.beforeScrollerCls = me.beforeScrollerCls || Ext.baseCSSPrefix + layout.owner.getXType() + '-scroll-' + layout.parallelBefore;
- me.afterScrollerCls = me.afterScrollerCls || Ext.baseCSSPrefix + layout.owner.getXType() + '-scroll-' + layout.parallelAfter;
- me.CSSinitializes = true;
+ // needs to also account for header and scroller (if still in picture)
+ // should calculate from headerCt.
+ height = tbl.offsetHeight;
+ }
+ if (isNaN(width)) {
+ width = 1;
+ }
+ if (isNaN(height)) {
+ height = 1;
}
+ return {
+ width: width,
+ height: height
+ };
},
- handleOverflow: function(calculations, targetSize) {
+ invalidate: function(firstPass) {
var me = this,
- layout = me.layout,
- methodName = 'get' + layout.parallelPrefixCap,
- newSize = {};
-
- me.initCSSClasses();
- me.callParent(arguments);
- this.createInnerElements();
- this.showScrollers();
- newSize[layout.perpendicularPrefix] = targetSize[layout.perpendicularPrefix];
- newSize[layout.parallelPrefix] = targetSize[layout.parallelPrefix] - (me.beforeCt[methodName]() + me.afterCt[methodName]());
- return { targetSize: newSize };
- },
+ stretchEl = me.stretchEl;
-<span id='Ext-layout-container-boxOverflow-Scroller-method-createInnerElements'> /**
-</span> * @private
- * Creates the beforeCt and afterCt elements if they have not already been created
- */
- createInnerElements: function() {
- var me = this,
- target = me.layout.getRenderTarget();
-
- //normal items will be rendered to the innerCt. beforeCt and afterCt allow for fixed positioning of
- //special items such as scrollers or dropdown menu triggers
- if (!me.beforeCt) {
- target.addCls(Ext.baseCSSPrefix + me.layout.direction + '-box-overflow-body');
- me.beforeCt = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.beforeCtCls}, 'before');
- me.afterCt = target.insertSibling({cls: Ext.layout.container.Box.prototype.innerCls + ' ' + me.afterCtCls}, 'after');
- me.createWheelListener();
+ if (!stretchEl || !me.ownerCt) {
+ return;
}
- },
-<span id='Ext-layout-container-boxOverflow-Scroller-method-createWheelListener'> /**
-</span> * @private
- * Sets up an listener to scroll on the layout's innerCt mousewheel event
- */
- createWheelListener: function() {
- this.layout.innerCt.on({
- scope : this,
- mousewheel: function(e) {
- e.stopEvent();
+ var size = me.getSizeCalculation(),
+ scrollEl = me.scrollEl,
+ elDom = scrollEl.dom,
+ reservedSpace = me.reservedSpace,
+ pos,
+ extra = 5;
- this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false);
- }
- });
- },
+ if (size) {
+ stretchEl.setSize(size);
-<span id='Ext-layout-container-boxOverflow-Scroller-method-clearOverflow'> /**
-</span> * @private
- */
- clearOverflow: function() {
- this.hideScrollers();
- },
+ size = me.el.getSize(true);
-<span id='Ext-layout-container-boxOverflow-Scroller-method-showScrollers'> /**
-</span> * @private
- * Shows the scroller elements in the beforeCt and afterCt. Creates the scrollers first if they are not already
- * present.
- */
- showScrollers: function() {
- this.createScrollers();
- this.beforeScroller.show();
- this.afterScroller.show();
- this.updateScrollButtons();
-
- this.layout.owner.addClsWithUI('scroller');
- },
+ if (me.vertical) {
+ size.width += extra;
+ size.height -= reservedSpace;
+ pos = 'left';
+ } else {
+ size.width -= reservedSpace;
+ size.height += extra;
+ pos = 'top';
+ }
-<span id='Ext-layout-container-boxOverflow-Scroller-method-hideScrollers'> /**
-</span> * @private
- * Hides the scroller elements in the beforeCt and afterCt
- */
- hideScrollers: function() {
- if (this.beforeScroller != undefined) {
- this.beforeScroller.hide();
- this.afterScroller.hide();
-
- this.layout.owner.removeClsWithUI('scroller');
- }
- },
+ scrollEl.setSize(size);
+ elDom.style[pos] = (-extra) + 'px';
-<span id='Ext-layout-container-boxOverflow-Scroller-method-createScrollers'> /**
-</span> * @private
- * Creates the clickable scroller elements and places them into the beforeCt and afterCt
- */
- createScrollers: function() {
- if (!this.beforeScroller && !this.afterScroller) {
- var before = this.beforeCt.createChild({
- cls: Ext.String.format("{0} {1} ", this.scrollerCls, this.beforeScrollerCls)
- });
-
- var after = this.afterCt.createChild({
- cls: Ext.String.format("{0} {1}", this.scrollerCls, this.afterScrollerCls)
- });
-
- before.addClsOnOver(this.beforeScrollerCls + '-hover');
- after.addClsOnOver(this.afterScrollerCls + '-hover');
-
- before.setVisibilityMode(Ext.core.Element.DISPLAY);
- after.setVisibilityMode(Ext.core.Element.DISPLAY);
-
- this.beforeRepeater = Ext.create('Ext.util.ClickRepeater', before, {
- interval: this.scrollRepeatInterval,
- handler : this.scrollLeft,
- scope : this
- });
-
- this.afterRepeater = Ext.create('Ext.util.ClickRepeater', after, {
- interval: this.scrollRepeatInterval,
- handler : this.scrollRight,
- scope : this
- });
-
-<span id='Ext-layout-container-boxOverflow-Scroller-property-beforeScroller'> /**
-</span> * @property beforeScroller
- * @type Ext.core.Element
- * The left scroller element. Only created when needed.
- */
- this.beforeScroller = before;
-
-<span id='Ext-layout-container-boxOverflow-Scroller-property-afterScroller'> /**
-</span> * @property afterScroller
- * @type Ext.core.Element
- * The left scroller element. Only created when needed.
- */
- this.afterScroller = after;
+ // BrowserBug: IE7
+ // This makes the scroller enabled, when initially rendering.
+ elDom.scrollTop = elDom.scrollTop;
}
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-destroy'> /**
-</span> * @private
- */
- destroy: function() {
- Ext.destroy(this.beforeRepeater, this.afterRepeater, this.beforeScroller, this.afterScroller, this.beforeCt, this.afterCt);
+ afterComponentLayout: function() {
+ this.callParent(arguments);
+ this.invalidate();
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-scrollBy'> /**
-</span> * @private
- * Scrolls left or right by the number of pixels specified
- * @param {Number} delta Number of pixels to scroll to the right by. Use a negative number to scroll left
- */
- scrollBy: function(delta, animate) {
- this.scrollTo(this.getScrollPosition() + delta, animate);
- },
+ restoreScrollPos: function () {
+ var me = this,
+ el = this.scrollEl,
+ elDom = el && el.dom;
-<span id='Ext-layout-container-boxOverflow-Scroller-method-getScrollAnim'> /**
-</span> * @private
- * @return {Object} Object passed to scrollTo when scrolling
- */
- getScrollAnim: function() {
- return {
- duration: this.scrollDuration,
- callback: this.updateScrollButtons,
- scope : this
- };
+ if (me._scrollPos !== null && elDom) {
+ elDom[me.scrollProp] = me._scrollPos;
+ me._scrollPos = null;
+ }
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-updateScrollButtons'> /**
-</span> * @private
- * Enables or disables each scroller button based on the current scroll position
- */
- updateScrollButtons: function() {
- if (this.beforeScroller == undefined || this.afterScroller == undefined) {
- return;
+ setReservedSpace: function (reservedSpace) {
+ var me = this;
+ if (me.reservedSpace !== reservedSpace) {
+ me.reservedSpace = reservedSpace;
+ me.invalidate();
}
-
- var beforeMeth = this.atExtremeBefore() ? 'addCls' : 'removeCls',
- afterMeth = this.atExtremeAfter() ? 'addCls' : 'removeCls',
- beforeCls = this.beforeScrollerCls + '-disabled',
- afterCls = this.afterScrollerCls + '-disabled';
-
- this.beforeScroller[beforeMeth](beforeCls);
- this.afterScroller[afterMeth](afterCls);
- this.scrolling = false;
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-atExtremeBefore'> /**
-</span> * @private
- * Returns true if the innerCt scroll is already at its left-most point
- * @return {Boolean} True if already at furthest left point
- */
- atExtremeBefore: function() {
- return this.getScrollPosition() === 0;
- },
+ saveScrollPos: function () {
+ var me = this,
+ el = this.scrollEl,
+ elDom = el && el.dom;
-<span id='Ext-layout-container-boxOverflow-Scroller-method-scrollLeft'> /**
-</span> * @private
- * Scrolls to the left by the configured amount
- */
- scrollLeft: function() {
- this.scrollBy(-this.scrollIncrement, false);
+ me._scrollPos = elDom ? elDom[me.scrollProp] : null;
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-scrollRight'> /**
-</span> * @private
- * Scrolls to the right by the configured amount
+<span id='Ext-grid-Scroller-method-setScrollTop'> /**
+</span> * Sets the scrollTop and constrains the value between 0 and max.
+ * @param {Number} scrollTop
+ * @return {Number} The resulting scrollTop value after being constrained
*/
- scrollRight: function() {
- this.scrollBy(this.scrollIncrement, false);
- },
+ setScrollTop: function(scrollTop) {
+ var el = this.scrollEl,
+ elDom = el && el.dom;
-<span id='Ext-layout-container-boxOverflow-Scroller-method-getScrollPosition'> /**
-</span> * Returns the current scroll position of the innerCt element
- * @return {Number} The current scroll position
- */
- getScrollPosition: function(){
- var layout = this.layout;
- return parseInt(layout.innerCt.dom['scroll' + layout.parallelBeforeCap], 10) || 0;
+ if (elDom) {
+ return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight);
+ }
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-getMaxScrollPosition'> /**
-</span> * @private
- * Returns the maximum value we can scrollTo
- * @return {Number} The max scroll value
+<span id='Ext-grid-Scroller-method-setScrollLeft'> /**
+</span> * Sets the scrollLeft and constrains the value between 0 and max.
+ * @param {Number} scrollLeft
+ * @return {Number} The resulting scrollLeft value after being constrained
*/
- getMaxScrollPosition: function() {
- var layout = this.layout;
- return layout.innerCt.dom['scroll' + layout.parallelPrefixCap] - this.layout.innerCt['get' + layout.parallelPrefixCap]();
- },
+ setScrollLeft: function(scrollLeft) {
+ var el = this.scrollEl,
+ elDom = el && el.dom;
-<span id='Ext-layout-container-boxOverflow-Scroller-method-atExtremeAfter'> /**
-</span> * @private
- * Returns true if the innerCt scroll is already at its right-most point
- * @return {Boolean} True if already at furthest right point
- */
- atExtremeAfter: function() {
- return this.getScrollPosition() >= this.getMaxScrollPosition();
+ if (elDom) {
+ return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth);
+ }
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-scrollTo'> /**
-</span> * @private
- * Scrolls to the given position. Performs bounds checking.
- * @param {Number} position The position to scroll to. This is constrained.
- * @param {Boolean} animate True to animate. If undefined, falls back to value of this.animateScroll
+<span id='Ext-grid-Scroller-method-scrollByDeltaY'> /**
+</span> * Scroll by deltaY
+ * @param {Number} delta
+ * @return {Number} The resulting scrollTop value
*/
- scrollTo: function(position, animate) {
- var me = this,
- layout = me.layout,
- oldPosition = me.getScrollPosition(),
- newPosition = Ext.Number.constrain(position, 0, me.getMaxScrollPosition());
-
- if (newPosition != oldPosition && !me.scrolling) {
- if (animate == undefined) {
- animate = me.animateScroll;
- }
+ scrollByDeltaY: function(delta) {
+ var el = this.scrollEl,
+ elDom = el && el.dom;
- layout.innerCt.scrollTo(layout.parallelBefore, newPosition, animate ? me.getScrollAnim() : false);
- if (animate) {
- me.scrolling = true;
- } else {
- me.scrolling = false;
- me.updateScrollButtons();
- }
-
- me.fireEvent('scroll', me, newPosition, animate ? me.getScrollAnim() : false);
+ if (elDom) {
+ return this.setScrollTop(elDom.scrollTop + delta);
}
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-scrollToItem'> /**
-</span> * Scrolls to the given component.
- * @param {String|Number|Ext.Component} item The item to scroll to. Can be a numerical index, component id
- * or a reference to the component itself.
- * @param {Boolean} animate True to animate the scrolling
+<span id='Ext-grid-Scroller-method-scrollByDeltaX'> /**
+</span> * Scroll by deltaX
+ * @param {Number} delta
+ * @return {Number} The resulting scrollLeft value
*/
- scrollToItem: function(item, animate) {
- var me = this,
- layout = me.layout,
- visibility,
- box,
- newPos;
-
- item = me.getItem(item);
- if (item != undefined) {
- visibility = this.getItemVisibility(item);
- if (!visibility.fullyVisible) {
- box = item.getBox(true, true);
- newPos = box[layout.parallelPosition];
- if (visibility.hiddenEnd) {
- newPos -= (this.layout.innerCt['get' + layout.parallelPrefixCap]() - box[layout.parallelPrefix]);
- }
- this.scrollTo(newPos, animate);
- }
+ scrollByDeltaX: function(delta) {
+ var el = this.scrollEl,
+ elDom = el && el.dom;
+
+ if (elDom) {
+ return this.setScrollLeft(elDom.scrollLeft + delta);
}
},
-<span id='Ext-layout-container-boxOverflow-Scroller-method-getItemVisibility'> /**
-</span> * @private
- * For a given item in the container, return an object with information on whether the item is visible
- * with the current innerCt scroll value.
- * @param {Ext.Component} item The item
- * @return {Object} Values for fullyVisible, hiddenStart and hiddenEnd
+
+<span id='Ext-grid-Scroller-method-scrollToTop'> /**
+</span> * Scroll to the top.
*/
- getItemVisibility: function(item) {
- var me = this,
- box = me.getItem(item).getBox(true, true),
- layout = me.layout,
- itemStart = box[layout.parallelPosition],
- itemEnd = itemStart + box[layout.parallelPrefix],
- scrollStart = me.getScrollPosition(),
- scrollEnd = scrollStart + layout.innerCt['get' + layout.parallelPrefixCap]();
+ scrollToTop : function(){
+ this.setScrollTop(0);
+ },
- return {
- hiddenStart : itemStart < scrollStart,
- hiddenEnd : itemEnd > scrollEnd,
- fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd
- };
+ // synchronize the scroller with the bound gridviews
+ onElScroll: function(event, target) {
+ this.fireEvent('bodyscroll', event, target);
+ },
+
+ getPanel: function() {
+ var me = this;
+ if (!me.panel) {
+ me.panel = this.up('[scrollerOwner]');
+ }
+ return me.panel;
}
-});</pre>
+});
+
+</pre>
</body>
</html>