1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// 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');
goog.require('goog.userAgent.platform');
goog.require('goog.userAgent.product');
goog.require('goog.userAgent.product.isVersion');
/**
* 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.style.bidi.usesNegativeScrollLeftInRtl_()) {
return -element.scrollLeft;
} else if (
isRtl &&
!(goog.userAgent.EDGE_OR_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, analogous 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) {
element = /** @type {!HTMLElement} */ (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.style.bidi.usesNegativeScrollLeftInRtl_()) {
element.scrollLeft = -offsetStart;
} else if (
!(goog.userAgent.EDGE_OR_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;
}
};
/**
* @return {boolean} Whether the current browser returns negative scrollLeft
* values for RTL elements. If true, then scrollLeft starts at 0 and then
* becomes more negative as the element is scrolled towards the left.
* @private
*/
goog.style.bidi.usesNegativeScrollLeftInRtl_ = function() {
var isSafari10Plus =
goog.userAgent.product.SAFARI && goog.userAgent.product.isVersion(10);
var isIOS10Plus = goog.userAgent.IOS && goog.userAgent.platform.isVersion(10);
return goog.userAgent.GECKO || isSafari10Plus || isIOS10Plus;
};
/**
* 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 = '';
}
};
|