<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-grid-Scroller'>/**
-</span> * @class Ext.grid.Scroller
- * @extends Ext.Component
- *
- * Docked in an Ext.grid.Panel, controls virtualized scrolling and synchronization
- * across different sections.
- *
+ <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
*/
-Ext.define('Ext.grid.Scroller', {
- extend: 'Ext.Component',
- alias: 'widget.gridscroller',
- weight: 110,
- cls: 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',
- 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] = me.scrollerSize = Ext.getScrollbarSize()[sizeProp];
+Ext.define('Ext.layout.container.boxOverflow.Scroller', {
- me.cls += (' ' + cls);
+ /* Begin Definitions */
- Ext.applyIf(me.renderSelectors, {
- stretchEl: '.' + Ext.baseCSSPrefix + 'stretcher',
- scrollEl: '.' + Ext.baseCSSPrefix + 'scroller-ct'
- });
- me.callParent();
+ extend: 'Ext.layout.container.boxOverflow.None',
+ requires: ['Ext.util.ClickRepeater', 'Ext.Element'],
+ alternateClassName: 'Ext.layout.boxOverflow.Scroller',
+ mixins: {
+ observable: 'Ext.util.Observable'
},
+
+ /* End Definitions */
- initRenderData: function () {
- var me = this,
- ret = me.callParent(arguments) || {};
+<span id='Ext-layout-container-boxOverflow-Scroller-cfg-animateScroll'> /**
+</span> * @cfg {Boolean} animateScroll
+ * True to animate the scrolling of items within the layout (ignored if enableScroll is false)
+ */
+ animateScroll: false,
- ret.baseId = me.id;
+<span id='Ext-layout-container-boxOverflow-Scroller-cfg-scrollIncrement'> /**
+</span> * @cfg {Number} scrollIncrement
+ * The number of pixels to scroll by on scroller click
+ */
+ scrollIncrement: 20,
- return ret;
- },
+<span id='Ext-layout-container-boxOverflow-Scroller-cfg-wheelIncrement'> /**
+</span> * @cfg {Number} wheelIncrement
+ * The number of pixels to increment on mouse wheel scrolling.
+ */
+ wheelIncrement: 10,
- 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-scrollRepeatInterval'> /**
+</span> * @cfg {Number} scrollRepeatInterval
+ * Number of milliseconds between each scroll while a scroller button is held down
+ */
+ scrollRepeatInterval: 60,
- 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-scrollDuration'> /**
+</span> * @cfg {Number} scrollDuration
+ * Number of milliseconds that each scroll animation lasts
+ */
+ scrollDuration: 400,
- 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-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
+ */
- 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-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
+ */
- // 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;
+<span id='Ext-layout-container-boxOverflow-Scroller-cfg-scrollerCls'> /**
+</span> * @cfg {String} [scrollerCls='x-box-scroller']
+ * CSS class added to both scroller elements if enableScroll is used
+ */
+ scrollerCls: Ext.baseCSSPrefix + 'box-scroller',
+
+<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
+ */
+
+<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;
}
- return {
- width: width,
- height: height
- };
},
- invalidate: function(firstPass) {
+ handleOverflow: function(calculations, targetSize) {
var me = this,
- stretchEl = me.stretchEl;
+ 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 };
+ },
- if (!stretchEl || !me.ownerCt) {
- return;
+<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();
}
+ },
- var size = me.getSizeCalculation(),
- scrollEl = me.scrollEl,
- elDom = scrollEl.dom,
- reservedSpace = me.reservedSpace,
- pos,
- extra = 5;
-
- if (size) {
- stretchEl.setSize(size);
-
- size = me.el.getSize(true);
+<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();
- if (me.vertical) {
- size.width += extra;
- size.height -= reservedSpace;
- pos = 'left';
- } else {
- size.width -= reservedSpace;
- size.height += extra;
- pos = 'top';
+ this.scrollBy(e.getWheelDelta() * this.wheelIncrement * -1, false);
}
-
- scrollEl.setSize(size);
- elDom.style[pos] = (-extra) + 'px';
-
- // BrowserBug: IE7
- // This makes the scroller enabled, when initially rendering.
- elDom.scrollTop = elDom.scrollTop;
- }
+ });
},
- afterComponentLayout: function() {
- this.callParent(arguments);
- this.invalidate();
+<span id='Ext-layout-container-boxOverflow-Scroller-method-clearOverflow'> /**
+</span> * @private
+ */
+ clearOverflow: function() {
+ this.hideScrollers();
},
- restoreScrollPos: function () {
- var me = this,
- el = this.scrollEl,
- elDom = el && el.dom;
+<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._scrollPos !== null && elDom) {
- elDom[me.scrollProp] = me._scrollPos;
- me._scrollPos = null;
+<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');
}
},
- setReservedSpace: function (reservedSpace) {
- var me = this;
- if (me.reservedSpace !== reservedSpace) {
- me.reservedSpace = reservedSpace;
- me.invalidate();
+<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.Element.DISPLAY);
+ after.setVisibilityMode(Ext.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.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.Element
+ * The left scroller element. Only created when needed.
+ */
+ this.afterScroller = after;
}
},
- saveScrollPos: function () {
- var me = this,
- el = this.scrollEl,
- elDom = el && el.dom;
-
- me._scrollPos = elDom ? elDom[me.scrollProp] : null;
+<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);
},
-<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
+<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
*/
- setScrollTop: function(scrollTop) {
- var el = this.scrollEl,
- elDom = el && el.dom;
-
- if (elDom) {
- return elDom.scrollTop = Ext.Number.constrain(scrollTop, 0, elDom.scrollHeight - elDom.clientHeight);
- }
+ scrollBy: function(delta, animate) {
+ this.scrollTo(this.getScrollPosition() + delta, animate);
},
-<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
+<span id='Ext-layout-container-boxOverflow-Scroller-method-getScrollAnim'> /**
+</span> * @private
+ * @return {Object} Object passed to scrollTo when scrolling
*/
- setScrollLeft: function(scrollLeft) {
- var el = this.scrollEl,
- elDom = el && el.dom;
+ getScrollAnim: function() {
+ return {
+ duration: this.scrollDuration,
+ callback: this.updateScrollButtons,
+ scope : this
+ };
+ },
- if (elDom) {
- return elDom.scrollLeft = Ext.Number.constrain(scrollLeft, 0, elDom.scrollWidth - elDom.clientWidth);
+<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;
}
+
+ 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-grid-Scroller-method-scrollByDeltaY'> /**
-</span> * Scroll by deltaY
- * @param {Number} delta
- * @return {Number} The resulting scrollTop value
+<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
*/
- scrollByDeltaY: function(delta) {
- var el = this.scrollEl,
- elDom = el && el.dom;
+ atExtremeBefore: function() {
+ return this.getScrollPosition() === 0;
+ },
- if (elDom) {
- return this.setScrollTop(elDom.scrollTop + delta);
- }
+<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);
},
-<span id='Ext-grid-Scroller-method-scrollByDeltaX'> /**
-</span> * Scroll by deltaX
- * @param {Number} delta
- * @return {Number} The resulting scrollLeft value
+<span id='Ext-layout-container-boxOverflow-Scroller-method-scrollRight'> /**
+</span> * @private
+ * Scrolls to the right by the configured amount
*/
- scrollByDeltaX: function(delta) {
- var el = this.scrollEl,
- elDom = el && el.dom;
+ scrollRight: function() {
+ this.scrollBy(this.scrollIncrement, false);
+ },
- if (elDom) {
- return this.setScrollLeft(elDom.scrollLeft + delta);
- }
+<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;
},
+<span id='Ext-layout-container-boxOverflow-Scroller-method-getMaxScrollPosition'> /**
+</span> * @private
+ * Returns the maximum value we can scrollTo
+ * @return {Number} The max scroll value
+ */
+ getMaxScrollPosition: function() {
+ var layout = this.layout;
+ return layout.innerCt.dom['scroll' + layout.parallelPrefixCap] - this.layout.innerCt['get' + layout.parallelPrefixCap]();
+ },
-<span id='Ext-grid-Scroller-method-scrollToTop'> /**
-</span> * Scroll to the top.
+<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
*/
- scrollToTop : function(){
- this.setScrollTop(0);
+ atExtremeAfter: function() {
+ return this.getScrollPosition() >= this.getMaxScrollPosition();
},
- // synchronize the scroller with the bound gridviews
- onElScroll: function(event, target) {
- this.fireEvent('bodyscroll', event, target);
+<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
+ */
+ 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;
+ }
+
+ 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);
+ }
},
- getPanel: function() {
- var me = this;
- if (!me.panel) {
- me.panel = this.up('[scrollerOwner]');
+<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
+ */
+ 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);
+ }
}
- return me.panel;
- }
-});
+ },
-</pre>
+<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
+ */
+ 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]();
+
+ return {
+ hiddenStart : itemStart < scrollStart,
+ hiddenEnd : itemEnd > scrollEnd,
+ fullyVisible: itemStart > scrollStart && itemEnd < scrollEnd
+ };
+ }
+});</pre>
</body>
</html>