| // Copyright 2012 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Bidi utility functions. |
| * |
| */ |
| |
| goog.provide('goog.style.bidi'); |
| |
| goog.require('goog.dom'); |
| goog.require('goog.style'); |
| goog.require('goog.userAgent'); |
| |
| |
| /** |
| * Returns the normalized scrollLeft position for a scrolled element. |
| * @param {Element} element The scrolled element. |
| * @return {number} The number of pixels the element is scrolled. 0 indicates |
| * that the element is not scrolled at all (which, in general, is the |
| * left-most position in ltr and the right-most position in rtl). |
| */ |
| goog.style.bidi.getScrollLeft = function(element) { |
| var isRtl = goog.style.isRightToLeft(element); |
| if (isRtl && goog.userAgent.GECKO) { |
| // ScrollLeft starts at 0 and then goes negative as the element is scrolled |
| // towards the left. |
| return -element.scrollLeft; |
| } else if (isRtl && |
| !(goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8'))) { |
| // ScrollLeft starts at the maximum positive value and decreases towards |
| // 0 as the element is scrolled towards the left. However, for overflow |
| // visible, there is no scrollLeft and the value always stays correctly at 0 |
| var overflowX = goog.style.getComputedOverflowX(element); |
| if (overflowX == 'visible') { |
| return element.scrollLeft; |
| } else { |
| return element.scrollWidth - element.clientWidth - element.scrollLeft; |
| } |
| } |
| // ScrollLeft behavior is identical in rtl and ltr, it starts at 0 and |
| // increases as the element is scrolled away from the start. |
| return element.scrollLeft; |
| }; |
| |
| |
| /** |
| * Returns the "offsetStart" of an element, analagous to offsetLeft but |
| * normalized for right-to-left environments and various browser |
| * inconsistencies. This value returned can always be passed to setScrollOffset |
| * to scroll to an element's left edge in a left-to-right offsetParent or |
| * right edge in a right-to-left offsetParent. |
| * |
| * For example, here offsetStart is 10px in an LTR environment and 5px in RTL: |
| * |
| * <pre> |
| * | xxxxxxxxxx | |
| * ^^^^^^^^^^ ^^^^ ^^^^^ |
| * 10px elem 5px |
| * </pre> |
| * |
| * If an element is positioned before the start of its offsetParent, the |
| * startOffset may be negative. This can be used with setScrollOffset to |
| * reliably scroll to an element: |
| * |
| * <pre> |
| * var scrollOffset = goog.style.bidi.getOffsetStart(element); |
| * goog.style.bidi.setScrollOffset(element.offsetParent, scrollOffset); |
| * </pre> |
| * |
| * @see setScrollOffset |
| * |
| * @param {Element} element The element for which we need to determine the |
| * offsetStart position. |
| * @return {number} The offsetStart for that element. |
| */ |
| goog.style.bidi.getOffsetStart = function(element) { |
| var offsetLeftForReal = element.offsetLeft; |
| |
| // The element might not have an offsetParent. |
| // For example, the node might not be attached to the DOM tree, |
| // and position:fixed children do not have an offset parent. |
| // Just try to do the best we can with what we have. |
| var bestParent = element.offsetParent; |
| |
| if (!bestParent && goog.style.getComputedPosition(element) == 'fixed') { |
| bestParent = goog.dom.getOwnerDocument(element).documentElement; |
| } |
| |
| // Just give up in this case. |
| if (!bestParent) { |
| return offsetLeftForReal; |
| } |
| |
| if (goog.userAgent.GECKO) { |
| // When calculating an element's offsetLeft, Firefox erroneously subtracts |
| // the border width from the actual distance. So we need to add it back. |
| var borderWidths = goog.style.getBorderBox(bestParent); |
| offsetLeftForReal += borderWidths.left; |
| } else if (goog.userAgent.isDocumentModeOrHigher(8) && |
| !goog.userAgent.isDocumentModeOrHigher(9)) { |
| // When calculating an element's offsetLeft, IE8/9-Standards Mode |
| // erroneously adds the border width to the actual distance. So we need to |
| // subtract it. |
| var borderWidths = goog.style.getBorderBox(bestParent); |
| offsetLeftForReal -= borderWidths.left; |
| } |
| |
| if (goog.style.isRightToLeft(bestParent)) { |
| // Right edge of the element relative to the left edge of its parent. |
| var elementRightOffset = offsetLeftForReal + element.offsetWidth; |
| |
| // Distance from the parent's right edge to the element's right edge. |
| return bestParent.clientWidth - elementRightOffset; |
| } |
| |
| return offsetLeftForReal; |
| }; |
| |
| |
| /** |
| * Sets the element's scrollLeft attribute so it is correctly scrolled by |
| * offsetStart pixels. This takes into account whether the element is RTL and |
| * the nuances of different browsers. To scroll to the "beginning" of an |
| * element use getOffsetStart to obtain the element's offsetStart value and then |
| * pass the value to setScrollOffset. |
| * @see getOffsetStart |
| * @param {Element} element The element to set scrollLeft on. |
| * @param {number} offsetStart The number of pixels to scroll the element. |
| * If this value is < 0, 0 is used. |
| */ |
| goog.style.bidi.setScrollOffset = function(element, offsetStart) { |
| offsetStart = Math.max(offsetStart, 0); |
| // In LTR and in "mirrored" browser RTL (such as IE), we set scrollLeft to |
| // the number of pixels to scroll. |
| // Otherwise, in RTL, we need to account for different browser behavior. |
| if (!goog.style.isRightToLeft(element)) { |
| element.scrollLeft = offsetStart; |
| } else if (goog.userAgent.GECKO) { |
| // Negative scroll-left positions in RTL. |
| element.scrollLeft = -offsetStart; |
| } else if (!(goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8'))) { |
| // Take the current scrollLeft value and move to the right by the |
| // offsetStart to get to the left edge of the element, and then by |
| // the clientWidth of the element to get to the right edge. |
| element.scrollLeft = |
| element.scrollWidth - offsetStart - element.clientWidth; |
| } else { |
| element.scrollLeft = offsetStart; |
| } |
| }; |
| |
| |
| /** |
| * Sets the element's left style attribute in LTR or right style attribute in |
| * RTL. Also clears the left attribute in RTL and the right attribute in LTR. |
| * @param {Element} elem The element to position. |
| * @param {number} left The left position in LTR; will be set as right in RTL. |
| * @param {?number} top The top position. If null only the left/right is set. |
| * @param {boolean} isRtl Whether we are in RTL mode. |
| */ |
| goog.style.bidi.setPosition = function(elem, left, top, isRtl) { |
| if (!goog.isNull(top)) { |
| elem.style.top = top + 'px'; |
| } |
| if (isRtl) { |
| elem.style.right = left + 'px'; |
| elem.style.left = ''; |
| } else { |
| elem.style.left = left + 'px'; |
| elem.style.right = ''; |
| } |
| }; |