Ext.define('Ext.rtl.dom.Element_scroll', { override: 'Ext.dom.Element', rtlGetScroll: function() { var me = this, dom = me.dom, doc = document, body = doc.body, scroll = me.getScroll(), // The left value returned from getScroll() may be a negative number. In rtl // mode left should always be reported as a positive number of pixels from the // right, so use the absolute value of left. left = Math.abs(scroll.left), isDocOrBody = (dom === doc || dom === body); if (isDocOrBody ? (3 & me._rtlDocScrollFlag) : (me._rtlScrollFlag === 1)) { // If the browser reports scrollLeft as the number of pixels from left // (same as ltr) we need to convert it to a rtl position by subtracting it // from scrollWidth if (isDocOrBody) { dom = body; } left = dom.scrollWidth - left - (isDocOrBody ? Ext.Element.getViewportWidth() : dom.clientWidth); } scroll.left = left; return scroll; }, rtlGetScrollLeft: function() { return this.rtlGetScroll().left; }, rtlSetScrollLeft: function(left){ var me = this; me.dom.scrollLeft = me.rtlNormalizeScrollLeft(left); return me; }, rtlScrollTo: function(side, value, animate) { if (side === 'left') { value = this.rtlNormalizeScrollLeft(value); } return this.scrollTo(side, value, animate); }, rtlScrollBy: function(deltaX, deltaY, animate) { var me = this, dom = me.dom, left, maxScroll; // Extract args if deltas were passed as an Array. if (deltaX.length) { animate = deltaY; deltaY = deltaX[1]; deltaX = deltaX[0]; } else if (typeof deltaX != 'number') { // or an object animate = deltaY; deltaY = deltaX.y; deltaX = deltaX.x; } if (deltaX) { left = me.rtlNormalizeScrollLeft( me.constrainScrollLeft(me.rtlGetScrollLeft() + deltaX) ); me.scrollTo('left', left, animate); } if (deltaY) { me.scrollTo('top', me.constrainScrollTop(dom.scrollTop + deltaY), animate); } return me; }, rtlScroll: function(direction, distance, animate) { if (!this.isScrollable()) { return false; } var me = this, side = direction === 'r' || direction === 'l' ? 'left' : 'top', scrolled = false, currentScroll, constrainedScroll; if (direction === 'r') { distance = -distance; } if (side === 'left') { currentScroll = me.rtlGetScrollLeft(); constrainedScroll = me.constrainScrollLeft(currentScroll + distance); } else { currentScroll = me.dom.scrollTop; constrainedScroll = me.constrainScrollTop(currentScroll + distance); } if (constrainedScroll !== currentScroll) { this.rtlScrollTo(side, constrainedScroll, animate); scrolled = true; } return scrolled; }, rtlNormalizeScrollLeft: function(left){ var dom = this.dom, flag = this._rtlScrollFlag; if (flag === 0) { left = -left; } else if (flag === 1) { left = dom.scrollWidth - left - dom.clientWidth; } return left; } }, function() { var Element = this; /* * Sets a _rtlScrollFlag property on the Element class prototype indicating how the * browser reports scrollLeft on overflowing rtl elements. This method cannot be used * reliably on the documentElement or document.body because the behavior of these * elements can be different from other elements in some browsers. * * 0: offset from right (negative number) - firefox * 1: offset from left (positive number) - IE6, IE7, IE6 - IE9 quirks, and Webkit * 2: offset from right (positive number) - IE8 - IE10 & IE10 quirks */ function cacheRtlScrollFlag() { var el = Ext.getBody().createChild({ tag: 'div', style: 'direction:rtl;position:absolute;overflow:auto;height:100px;width:100px;', children: [{ tag: 'div', style: 'height:30px;width:150px;' }] }), dom = el.dom, flag = 2; if (dom.scrollLeft === 50) { flag = 1; } else { dom.scrollLeft = -1; if (dom.scrollLeft) { flag = 0; } } el.remove(); Element.prototype._rtlScrollFlag = flag; } /* * scrollLeft on the document/body is reported differently from ordinary * overflowing elements in many browsers (see getRtlScrollFlag).There are 2 * separate things we have to detect: * 1. The element that overflows - when the document overflows some browsers * set scrollLeft on the document body (webkit and IE quirks), while other * browsers set scrollLeft on the documentElement (all other supported browsers * at the time of this writing). * 2. The scrollLeft of the overflowing document/body can be one of the * following: * a. number of pixels offset from right expressed as a negative number * (Webkit, Firefox) * b. number of pixels offset from right expressed as a positive number * (IE8 - IE10 strict mode, and IE10 quirks mode. * c. number of pixels offset from left expressed as a positive number * (IE6 - IE9 quirks mode, and IE6/IE7 strict mode. * * The following logic feture detects the handling of scrollLeft and sets the * _rtlDocScrollFlag property on this class' prototype as a bit flag which has * the following values: * * 0 - docEl, negative right * 1 - docEl, positive left * 2 - docEl, positive right * 4 - body, negative right * 5 - body, positive left */ function cacheRtlDocScrollFlag() { var doc = document, docEl = doc.documentElement, body = doc.body, // flag defaults to body, negative right (webkit) so no detection needed // is needed for this scenario flag = 4, bodyStyle = body.style, // save the direction property so we can set it back when we are done. direction = bodyStyle.direction, el = Ext.getBody().createChild( '<div style="height:20000px;width:20000px;"></div>' ), dom = el.dom, ltrRight, rtlRight bodyStyle.direction = 'ltr'; ltrRight = dom.getBoundingClientRect().right; bodyStyle.direction = 'rtl'; rtlRight = dom.getBoundingClientRect().right; // when the body has vertical overflow some browser continue to show the // vertical scrollbar on the right side of the page even in rtl mode. Element.prototype._rtlBodyScrollbarOnRight = (ltrRight === rtlRight); // First, check if scrollLeft is a non-zero value on the documentElement or // body. This means scrollLeft is a positive number offset from the left. if (docEl.scrollLeft > 0) { // IE6/7 strict flag = 1; } else if (body.scrollLeft > 0) { // IE6 - IE9 quirks flag = 5; } else { // The next step is to attempt to set scrollLeft values, This allows us to // test for non-zero values to see if the value was valid (scrollLeft // resets to 0 when a non-valid value is set). // attempt to set the documentElement's scrollLeft to a negative number docEl.scrollLeft = -1; if (docEl.scrollLeft) { // it worked! we were able to set a negative scroll left on the // documentElement (firefox) flag = 0; } else { // attempt to set the documentElement's scrollLeft to a positive number docEl.scrollLeft = 1; if (docEl.scrollLeft) { // success setting scroll left to a positive number on // documentElement (IE8 strict, IE9 strict, and IE10 quirks) flag = 2; } } } el.remove(); if (!direction) { // if direction is an empty string, we set it back to "ltr", because once // the direction style on the body element is changed to "rtl" in webkit, // it becomes permanent, even after it is set back to "", unless it is first // explicitly set back to "ltr" bodyStyle.direction = 'ltr'; // read the scroll width before setting the direction back to "". // This forces webkit to update its computed direction style to ltr body.scrollWidth; } // set direction back to its original value bodyStyle.direction = direction; Element.prototype._rtlDocScrollFlag = flag; } Ext.on({ ready: function() { // This function attaches to onReady with a priority of 1000 so that we can // detect how the browser reports scrollLeft by manipulating the document/body // before any components have been rendered to the page. cacheRtlDocScrollFlag(); cacheRtlScrollFlag(); }, single: true, priority: 1001 }); });