| // Copyright 2006 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 Utilities for element styles. |
| * |
| * @author arv@google.com (Erik Arvidsson) |
| * @author eae@google.com (Emil A Eklund) |
| * @see ../demos/inline_block_quirks.html |
| * @see ../demos/inline_block_standards.html |
| * @see ../demos/style_viewport.html |
| */ |
| |
| goog.provide('goog.style'); |
| |
| |
| goog.require('goog.array'); |
| goog.require('goog.asserts'); |
| goog.require('goog.dom'); |
| goog.require('goog.dom.NodeType'); |
| goog.require('goog.dom.vendor'); |
| goog.require('goog.math.Box'); |
| goog.require('goog.math.Coordinate'); |
| goog.require('goog.math.Rect'); |
| goog.require('goog.math.Size'); |
| goog.require('goog.object'); |
| goog.require('goog.string'); |
| goog.require('goog.userAgent'); |
| |
| |
| /** |
| * Sets a style value on an element. |
| * |
| * This function is not indended to patch issues in the browser's style |
| * handling, but to allow easy programmatic access to setting dash-separated |
| * style properties. An example is setting a batch of properties from a data |
| * object without overwriting old styles. When possible, use native APIs: |
| * elem.style.propertyKey = 'value' or (if obliterating old styles is fine) |
| * elem.style.cssText = 'property1: value1; property2: value2'. |
| * |
| * @param {Element} element The element to change. |
| * @param {string|Object} style If a string, a style name. If an object, a hash |
| * of style names to style values. |
| * @param {string|number|boolean=} opt_value If style was a string, then this |
| * should be the value. |
| */ |
| goog.style.setStyle = function(element, style, opt_value) { |
| if (goog.isString(style)) { |
| goog.style.setStyle_(element, opt_value, style); |
| } else { |
| for (var key in style) { |
| goog.style.setStyle_(element, style[key], key); |
| } |
| } |
| }; |
| |
| |
| /** |
| * Sets a style value on an element, with parameters swapped to work with |
| * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when |
| * necessary. |
| * @param {Element} element The element to change. |
| * @param {string|number|boolean|undefined} value Style value. |
| * @param {string} style Style name. |
| * @private |
| */ |
| goog.style.setStyle_ = function(element, value, style) { |
| var propertyName = goog.style.getVendorJsStyleName_(element, style); |
| |
| if (propertyName) { |
| element.style[propertyName] = value; |
| } |
| }; |
| |
| |
| /** |
| * Style name cache that stores previous property name lookups. |
| * |
| * This is used by setStyle to speed up property lookups, entries look like: |
| * { StyleName: ActualPropertyName } |
| * |
| * @private {!Object<string, string>} |
| */ |
| goog.style.styleNameCache_ = {}; |
| |
| |
| /** |
| * Returns the style property name in camel-case. If it does not exist and a |
| * vendor-specific version of the property does exist, then return the vendor- |
| * specific property name instead. |
| * @param {Element} element The element to change. |
| * @param {string} style Style name. |
| * @return {string} Vendor-specific style. |
| * @private |
| */ |
| goog.style.getVendorJsStyleName_ = function(element, style) { |
| var propertyName = goog.style.styleNameCache_[style]; |
| if (!propertyName) { |
| var camelStyle = goog.string.toCamelCase(style); |
| propertyName = camelStyle; |
| |
| if (element.style[camelStyle] === undefined) { |
| var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() + |
| goog.string.toTitleCase(camelStyle); |
| |
| if (element.style[prefixedStyle] !== undefined) { |
| propertyName = prefixedStyle; |
| } |
| } |
| goog.style.styleNameCache_[style] = propertyName; |
| } |
| |
| return propertyName; |
| }; |
| |
| |
| /** |
| * Returns the style property name in CSS notation. If it does not exist and a |
| * vendor-specific version of the property does exist, then return the vendor- |
| * specific property name instead. |
| * @param {Element} element The element to change. |
| * @param {string} style Style name. |
| * @return {string} Vendor-specific style. |
| * @private |
| */ |
| goog.style.getVendorStyleName_ = function(element, style) { |
| var camelStyle = goog.string.toCamelCase(style); |
| |
| if (element.style[camelStyle] === undefined) { |
| var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() + |
| goog.string.toTitleCase(camelStyle); |
| |
| if (element.style[prefixedStyle] !== undefined) { |
| return goog.dom.vendor.getVendorPrefix() + '-' + style; |
| } |
| } |
| |
| return style; |
| }; |
| |
| |
| /** |
| * Retrieves an explicitly-set style value of a node. This returns '' if there |
| * isn't a style attribute on the element or if this style property has not been |
| * explicitly set in script. |
| * |
| * @param {Element} element Element to get style of. |
| * @param {string} property Property to get, css-style (if you have a camel-case |
| * property, use element.style[style]). |
| * @return {string} Style value. |
| */ |
| goog.style.getStyle = function(element, property) { |
| // element.style is '' for well-known properties which are unset. |
| // For for browser specific styles as 'filter' is undefined |
| // so we need to return '' explicitly to make it consistent across |
| // browsers. |
| var styleValue = element.style[goog.string.toCamelCase(property)]; |
| |
| // Using typeof here because of a bug in Safari 5.1, where this value |
| // was undefined, but === undefined returned false. |
| if (typeof(styleValue) !== 'undefined') { |
| return styleValue; |
| } |
| |
| return element.style[goog.style.getVendorJsStyleName_(element, property)] || |
| ''; |
| }; |
| |
| |
| /** |
| * Retrieves a computed style value of a node. It returns empty string if the |
| * value cannot be computed (which will be the case in Internet Explorer) or |
| * "none" if the property requested is an SVG one and it has not been |
| * explicitly set (firefox and webkit). |
| * |
| * @param {Element} element Element to get style of. |
| * @param {string} property Property to get (camel-case). |
| * @return {string} Style value. |
| */ |
| goog.style.getComputedStyle = function(element, property) { |
| var doc = goog.dom.getOwnerDocument(element); |
| if (doc.defaultView && doc.defaultView.getComputedStyle) { |
| var styles = doc.defaultView.getComputedStyle(element, null); |
| if (styles) { |
| // element.style[..] is undefined for browser specific styles |
| // as 'filter'. |
| return styles[property] || styles.getPropertyValue(property) || ''; |
| } |
| } |
| |
| return ''; |
| }; |
| |
| |
| /** |
| * Gets the cascaded style value of a node, or null if the value cannot be |
| * computed (only Internet Explorer can do this). |
| * |
| * @param {Element} element Element to get style of. |
| * @param {string} style Property to get (camel-case). |
| * @return {string} Style value. |
| */ |
| goog.style.getCascadedStyle = function(element, style) { |
| // TODO(nicksantos): This should be documented to return null. #fixTypes |
| return element.currentStyle ? element.currentStyle[style] : null; |
| }; |
| |
| |
| /** |
| * Cross-browser pseudo get computed style. It returns the computed style where |
| * available. If not available it tries the cascaded style value (IE |
| * currentStyle) and in worst case the inline style value. It shouldn't be |
| * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for |
| * discussion. |
| * |
| * @param {Element} element Element to get style of. |
| * @param {string} style Property to get (must be camelCase, not css-style.). |
| * @return {string} Style value. |
| * @private |
| */ |
| goog.style.getStyle_ = function(element, style) { |
| return goog.style.getComputedStyle(element, style) || |
| goog.style.getCascadedStyle(element, style) || |
| (element.style && element.style[style]); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the box-sizing CSS attribute. |
| * Browser support: http://caniuse.com/css3-boxsizing. |
| * @param {!Element} element The element whose box-sizing to get. |
| * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if |
| * box-sizing is not supported (IE7 and below). |
| */ |
| goog.style.getComputedBoxSizing = function(element) { |
| return goog.style.getStyle_(element, 'boxSizing') || |
| goog.style.getStyle_(element, 'MozBoxSizing') || |
| goog.style.getStyle_(element, 'WebkitBoxSizing') || null; |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the position CSS attribute. |
| * @param {Element} element The element to get the position of. |
| * @return {string} Position value. |
| */ |
| goog.style.getComputedPosition = function(element) { |
| return goog.style.getStyle_(element, 'position'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed background color string for a given element. The |
| * string returned is suitable for assigning to another element's |
| * background-color, but is not guaranteed to be in any particular string |
| * format. Accessing the color in a numeric form may not be possible in all |
| * browsers or with all input. |
| * |
| * If the background color for the element is defined as a hexadecimal value, |
| * the resulting string can be parsed by goog.color.parse in all supported |
| * browsers. |
| * |
| * Whether named colors like "red" or "lightblue" get translated into a |
| * format which can be parsed is browser dependent. Calling this function on |
| * transparent elements will return "transparent" in most browsers or |
| * "rgba(0, 0, 0, 0)" in WebKit. |
| * @param {Element} element The element to get the background color of. |
| * @return {string} The computed string value of the background color. |
| */ |
| goog.style.getBackgroundColor = function(element) { |
| return goog.style.getStyle_(element, 'backgroundColor'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the overflow-x CSS attribute. |
| * @param {Element} element The element to get the overflow-x of. |
| * @return {string} The computed string value of the overflow-x attribute. |
| */ |
| goog.style.getComputedOverflowX = function(element) { |
| return goog.style.getStyle_(element, 'overflowX'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the overflow-y CSS attribute. |
| * @param {Element} element The element to get the overflow-y of. |
| * @return {string} The computed string value of the overflow-y attribute. |
| */ |
| goog.style.getComputedOverflowY = function(element) { |
| return goog.style.getStyle_(element, 'overflowY'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the z-index CSS attribute. |
| * @param {Element} element The element to get the z-index of. |
| * @return {string|number} The computed value of the z-index attribute. |
| */ |
| goog.style.getComputedZIndex = function(element) { |
| return goog.style.getStyle_(element, 'zIndex'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the text-align CSS attribute. |
| * @param {Element} element The element to get the text-align of. |
| * @return {string} The computed string value of the text-align attribute. |
| */ |
| goog.style.getComputedTextAlign = function(element) { |
| return goog.style.getStyle_(element, 'textAlign'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the cursor CSS attribute. |
| * @param {Element} element The element to get the cursor of. |
| * @return {string} The computed string value of the cursor attribute. |
| */ |
| goog.style.getComputedCursor = function(element) { |
| return goog.style.getStyle_(element, 'cursor'); |
| }; |
| |
| |
| /** |
| * Retrieves the computed value of the CSS transform attribute. |
| * @param {Element} element The element to get the transform of. |
| * @return {string} The computed string representation of the transform matrix. |
| */ |
| goog.style.getComputedTransform = function(element) { |
| var property = goog.style.getVendorStyleName_(element, 'transform'); |
| return goog.style.getStyle_(element, property) || |
| goog.style.getStyle_(element, 'transform'); |
| }; |
| |
| |
| /** |
| * Sets the top/left values of an element. If no unit is specified in the |
| * argument then it will add px. The second argument is required if the first |
| * argument is a string or number and is ignored if the first argument |
| * is a coordinate. |
| * @param {Element} el Element to move. |
| * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate. |
| * @param {string|number=} opt_arg2 Top position. |
| */ |
| goog.style.setPosition = function(el, arg1, opt_arg2) { |
| var x, y; |
| |
| if (arg1 instanceof goog.math.Coordinate) { |
| x = arg1.x; |
| y = arg1.y; |
| } else { |
| x = arg1; |
| y = opt_arg2; |
| } |
| |
| el.style.left = goog.style.getPixelStyleValue_( |
| /** @type {number|string} */ (x), false); |
| el.style.top = goog.style.getPixelStyleValue_( |
| /** @type {number|string} */ (y), false); |
| }; |
| |
| |
| /** |
| * Gets the offsetLeft and offsetTop properties of an element and returns them |
| * in a Coordinate object |
| * @param {Element} element Element. |
| * @return {!goog.math.Coordinate} The position. |
| */ |
| goog.style.getPosition = function(element) { |
| return new goog.math.Coordinate(element.offsetLeft, element.offsetTop); |
| }; |
| |
| |
| /** |
| * Returns the viewport element for a particular document |
| * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element |
| * of. |
| * @return {Element} document.documentElement or document.body. |
| */ |
| goog.style.getClientViewportElement = function(opt_node) { |
| var doc; |
| if (opt_node) { |
| doc = goog.dom.getOwnerDocument(opt_node); |
| } else { |
| doc = goog.dom.getDocument(); |
| } |
| |
| // In old IE versions the document.body represented the viewport |
| if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) && |
| !goog.dom.getDomHelper(doc).isCss1CompatMode()) { |
| return doc.body; |
| } |
| return doc.documentElement; |
| }; |
| |
| |
| /** |
| * Calculates the viewport coordinates relative to the page/document |
| * containing the node. The viewport may be the browser viewport for |
| * non-iframe document, or the iframe container for iframe'd document. |
| * @param {!Document} doc The document to use as the reference point. |
| * @return {!goog.math.Coordinate} The page offset of the viewport. |
| */ |
| goog.style.getViewportPageOffset = function(doc) { |
| var body = doc.body; |
| var documentElement = doc.documentElement; |
| var scrollLeft = body.scrollLeft || documentElement.scrollLeft; |
| var scrollTop = body.scrollTop || documentElement.scrollTop; |
| return new goog.math.Coordinate(scrollLeft, scrollTop); |
| }; |
| |
| |
| /** |
| * Gets the client rectangle of the DOM element. |
| * |
| * getBoundingClientRect is part of a new CSS object model draft (with a |
| * long-time presence in IE), replacing the error-prone parent offset |
| * computation and the now-deprecated Gecko getBoxObjectFor. |
| * |
| * This utility patches common browser bugs in getBoundingClientRect. It |
| * will fail if getBoundingClientRect is unsupported. |
| * |
| * If the element is not in the DOM, the result is undefined, and an error may |
| * be thrown depending on user agent. |
| * |
| * @param {!Element} el The element whose bounding rectangle is being queried. |
| * @return {Object} A native bounding rectangle with numerical left, top, |
| * right, and bottom. Reported by Firefox to be of object type ClientRect. |
| * @private |
| */ |
| goog.style.getBoundingClientRect_ = function(el) { |
| var rect; |
| try { |
| rect = el.getBoundingClientRect(); |
| } catch (e) { |
| // In IE < 9, calling getBoundingClientRect on an orphan element raises an |
| // "Unspecified Error". All other browsers return zeros. |
| return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0}; |
| } |
| |
| // Patch the result in IE only, so that this function can be inlined if |
| // compiled for non-IE. |
| if (goog.userAgent.IE && el.ownerDocument.body) { |
| |
| // In IE, most of the time, 2 extra pixels are added to the top and left |
| // due to the implicit 2-pixel inset border. In IE6/7 quirks mode and |
| // IE6 standards mode, this border can be overridden by setting the |
| // document element's border to zero -- thus, we cannot rely on the |
| // offset always being 2 pixels. |
| |
| // In quirks mode, the offset can be determined by querying the body's |
| // clientLeft/clientTop, but in standards mode, it is found by querying |
| // the document element's clientLeft/clientTop. Since we already called |
| // getBoundingClientRect we have already forced a reflow, so it is not |
| // too expensive just to query them all. |
| |
| // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx |
| var doc = el.ownerDocument; |
| rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft; |
| rect.top -= doc.documentElement.clientTop + doc.body.clientTop; |
| } |
| return /** @type {Object} */ (rect); |
| }; |
| |
| |
| /** |
| * Returns the first parent that could affect the position of a given element. |
| * @param {Element} element The element to get the offset parent for. |
| * @return {Element} The first offset parent or null if one cannot be found. |
| */ |
| goog.style.getOffsetParent = function(element) { |
| // element.offsetParent does the right thing in IE7 and below. In other |
| // browsers it only includes elements with position absolute, relative or |
| // fixed, not elements with overflow set to auto or scroll. |
| if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) { |
| return element.offsetParent; |
| } |
| |
| var doc = goog.dom.getOwnerDocument(element); |
| var positionStyle = goog.style.getStyle_(element, 'position'); |
| var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute'; |
| for (var parent = element.parentNode; parent && parent != doc; |
| parent = parent.parentNode) { |
| // Skip shadowDOM roots. |
| if (parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT && |
| parent.host) { |
| parent = parent.host; |
| } |
| positionStyle = |
| goog.style.getStyle_(/** @type {!Element} */ (parent), 'position'); |
| skipStatic = skipStatic && positionStyle == 'static' && |
| parent != doc.documentElement && parent != doc.body; |
| if (!skipStatic && (parent.scrollWidth > parent.clientWidth || |
| parent.scrollHeight > parent.clientHeight || |
| positionStyle == 'fixed' || |
| positionStyle == 'absolute' || |
| positionStyle == 'relative')) { |
| return /** @type {!Element} */ (parent); |
| } |
| } |
| return null; |
| }; |
| |
| |
| /** |
| * Calculates and returns the visible rectangle for a given element. Returns a |
| * box describing the visible portion of the nearest scrollable offset ancestor. |
| * Coordinates are given relative to the document. |
| * |
| * @param {Element} element Element to get the visible rect for. |
| * @return {goog.math.Box} Bounding elementBox describing the visible rect or |
| * null if scrollable ancestor isn't inside the visible viewport. |
| */ |
| goog.style.getVisibleRectForElement = function(element) { |
| var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0); |
| var dom = goog.dom.getDomHelper(element); |
| var body = dom.getDocument().body; |
| var documentElement = dom.getDocument().documentElement; |
| var scrollEl = dom.getDocumentScrollElement(); |
| |
| // Determine the size of the visible rect by climbing the dom accounting for |
| // all scrollable containers. |
| for (var el = element; el = goog.style.getOffsetParent(el); ) { |
| // clientWidth is zero for inline block elements in IE. |
| // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0 |
| if ((!goog.userAgent.IE || el.clientWidth != 0) && |
| (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) && |
| // body may have overflow set on it, yet we still get the entire |
| // viewport. In some browsers, el.offsetParent may be |
| // document.documentElement, so check for that too. |
| (el != body && el != documentElement && |
| goog.style.getStyle_(el, 'overflow') != 'visible')) { |
| var pos = goog.style.getPageOffset(el); |
| var client = goog.style.getClientLeftTop(el); |
| pos.x += client.x; |
| pos.y += client.y; |
| |
| visibleRect.top = Math.max(visibleRect.top, pos.y); |
| visibleRect.right = Math.min(visibleRect.right, |
| pos.x + el.clientWidth); |
| visibleRect.bottom = Math.min(visibleRect.bottom, |
| pos.y + el.clientHeight); |
| visibleRect.left = Math.max(visibleRect.left, pos.x); |
| } |
| } |
| |
| // Clip by window's viewport. |
| var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop; |
| visibleRect.left = Math.max(visibleRect.left, scrollX); |
| visibleRect.top = Math.max(visibleRect.top, scrollY); |
| var winSize = dom.getViewportSize(); |
| visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width); |
| visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height); |
| return visibleRect.top >= 0 && visibleRect.left >= 0 && |
| visibleRect.bottom > visibleRect.top && |
| visibleRect.right > visibleRect.left ? |
| visibleRect : null; |
| }; |
| |
| |
| /** |
| * Calculate the scroll position of {@code container} with the minimum amount so |
| * that the content and the borders of the given {@code element} become visible. |
| * If the element is bigger than the container, its top left corner will be |
| * aligned as close to the container's top left corner as possible. |
| * |
| * @param {Element} element The element to make visible. |
| * @param {Element} container The container to scroll. |
| * @param {boolean=} opt_center Whether to center the element in the container. |
| * Defaults to false. |
| * @return {!goog.math.Coordinate} The new scroll position of the container, |
| * in form of goog.math.Coordinate(scrollLeft, scrollTop). |
| */ |
| goog.style.getContainerOffsetToScrollInto = |
| function(element, container, opt_center) { |
| // Absolute position of the element's border's top left corner. |
| var elementPos = goog.style.getPageOffset(element); |
| // Absolute position of the container's border's top left corner. |
| var containerPos = goog.style.getPageOffset(container); |
| var containerBorder = goog.style.getBorderBox(container); |
| // Relative pos. of the element's border box to the container's content box. |
| var relX = elementPos.x - containerPos.x - containerBorder.left; |
| var relY = elementPos.y - containerPos.y - containerBorder.top; |
| // How much the element can move in the container, i.e. the difference between |
| // the element's bottom-right-most and top-left-most position where it's |
| // fully visible. |
| var spaceX = container.clientWidth - element.offsetWidth; |
| var spaceY = container.clientHeight - element.offsetHeight; |
| |
| var scrollLeft = container.scrollLeft; |
| var scrollTop = container.scrollTop; |
| if (container == goog.dom.getDocument().body || |
| container == goog.dom.getDocument().documentElement) { |
| // If the container is the document scroll element (usually <body>), |
| // getPageOffset(element) is already relative to it and there is no need to |
| // consider the current scroll. |
| scrollLeft = containerPos.x + containerBorder.left; |
| scrollTop = containerPos.y + containerBorder.top; |
| |
| if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) { |
| // In older versions of IE getPageOffset(element) does not include the |
| // continaer border so it has to be added to accomodate. |
| scrollLeft += containerBorder.left; |
| scrollTop += containerBorder.top; |
| } |
| } |
| if (opt_center) { |
| // All browsers round non-integer scroll positions down. |
| scrollLeft += relX - spaceX / 2; |
| scrollTop += relY - spaceY / 2; |
| } else { |
| // This formula was designed to give the correct scroll values in the |
| // following cases: |
| // - element is higher than container (spaceY < 0) => scroll down by relY |
| // - element is not higher that container (spaceY >= 0): |
| // - it is above container (relY < 0) => scroll up by abs(relY) |
| // - it is below container (relY > spaceY) => scroll down by relY - spaceY |
| // - it is in the container => don't scroll |
| scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0)); |
| scrollTop += Math.min(relY, Math.max(relY - spaceY, 0)); |
| } |
| return new goog.math.Coordinate(scrollLeft, scrollTop); |
| }; |
| |
| |
| /** |
| * Changes the scroll position of {@code container} with the minimum amount so |
| * that the content and the borders of the given {@code element} become visible. |
| * If the element is bigger than the container, its top left corner will be |
| * aligned as close to the container's top left corner as possible. |
| * |
| * @param {Element} element The element to make visible. |
| * @param {Element} container The container to scroll. |
| * @param {boolean=} opt_center Whether to center the element in the container. |
| * Defaults to false. |
| */ |
| goog.style.scrollIntoContainerView = function(element, container, opt_center) { |
| var offset = |
| goog.style.getContainerOffsetToScrollInto(element, container, opt_center); |
| container.scrollLeft = offset.x; |
| container.scrollTop = offset.y; |
| }; |
| |
| |
| /** |
| * Returns clientLeft (width of the left border and, if the directionality is |
| * right to left, the vertical scrollbar) and clientTop as a coordinate object. |
| * |
| * @param {Element} el Element to get clientLeft for. |
| * @return {!goog.math.Coordinate} Client left and top. |
| */ |
| goog.style.getClientLeftTop = function(el) { |
| return new goog.math.Coordinate(el.clientLeft, el.clientTop); |
| }; |
| |
| |
| /** |
| * Returns a Coordinate object relative to the top-left of the HTML document. |
| * Implemented as a single function to save having to do two recursive loops in |
| * opera and safari just to get both coordinates. If you just want one value do |
| * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but |
| * note if you call both those methods the tree will be analysed twice. |
| * |
| * @param {Element} el Element to get the page offset for. |
| * @return {!goog.math.Coordinate} The page offset. |
| */ |
| goog.style.getPageOffset = function(el) { |
| var doc = goog.dom.getOwnerDocument(el); |
| // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe. |
| goog.asserts.assertObject(el, 'Parameter is required'); |
| |
| // NOTE(arv): If element is hidden (display none or disconnected or any the |
| // ancestors are hidden) we get (0,0) by default but we still do the |
| // accumulation of scroll position. |
| |
| // TODO(arv): Should we check if the node is disconnected and in that case |
| // return (0,0)? |
| |
| var pos = new goog.math.Coordinate(0, 0); |
| var viewportElement = goog.style.getClientViewportElement(doc); |
| if (el == viewportElement) { |
| // viewport is always at 0,0 as that defined the coordinate system for this |
| // function - this avoids special case checks in the code below |
| return pos; |
| } |
| |
| var box = goog.style.getBoundingClientRect_(el); |
| // Must add the scroll coordinates in to get the absolute page offset |
| // of element since getBoundingClientRect returns relative coordinates to |
| // the viewport. |
| var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll(); |
| pos.x = box.left + scrollCoord.x; |
| pos.y = box.top + scrollCoord.y; |
| |
| return pos; |
| }; |
| |
| |
| /** |
| * Returns the left coordinate of an element relative to the HTML document |
| * @param {Element} el Elements. |
| * @return {number} The left coordinate. |
| */ |
| goog.style.getPageOffsetLeft = function(el) { |
| return goog.style.getPageOffset(el).x; |
| }; |
| |
| |
| /** |
| * Returns the top coordinate of an element relative to the HTML document |
| * @param {Element} el Elements. |
| * @return {number} The top coordinate. |
| */ |
| goog.style.getPageOffsetTop = function(el) { |
| return goog.style.getPageOffset(el).y; |
| }; |
| |
| |
| /** |
| * Returns a Coordinate object relative to the top-left of an HTML document |
| * in an ancestor frame of this element. Used for measuring the position of |
| * an element inside a frame relative to a containing frame. |
| * |
| * @param {Element} el Element to get the page offset for. |
| * @param {Window} relativeWin The window to measure relative to. If relativeWin |
| * is not in the ancestor frame chain of the element, we measure relative to |
| * the top-most window. |
| * @return {!goog.math.Coordinate} The page offset. |
| */ |
| goog.style.getFramedPageOffset = function(el, relativeWin) { |
| var position = new goog.math.Coordinate(0, 0); |
| |
| // Iterate up the ancestor frame chain, keeping track of the current window |
| // and the current element in that window. |
| var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el)); |
| var currentEl = el; |
| do { |
| // if we're at the top window, we want to get the page offset. |
| // if we're at an inner frame, we only want to get the window position |
| // so that we can determine the actual page offset in the context of |
| // the outer window. |
| var offset = currentWin == relativeWin ? |
| goog.style.getPageOffset(currentEl) : |
| goog.style.getClientPositionForElement_( |
| goog.asserts.assert(currentEl)); |
| |
| position.x += offset.x; |
| position.y += offset.y; |
| } while (currentWin && currentWin != relativeWin && |
| currentWin != currentWin.parent && |
| (currentEl = currentWin.frameElement) && |
| (currentWin = currentWin.parent)); |
| |
| return position; |
| }; |
| |
| |
| /** |
| * Translates the specified rect relative to origBase page, for newBase page. |
| * If origBase and newBase are the same, this function does nothing. |
| * |
| * @param {goog.math.Rect} rect The source rectangle relative to origBase page, |
| * and it will have the translated result. |
| * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle. |
| * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant |
| * coordinate. This must be a DOM for an ancestor frame of origBase |
| * or the same as origBase. |
| */ |
| goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) { |
| if (origBase.getDocument() != newBase.getDocument()) { |
| var body = origBase.getDocument().body; |
| var pos = goog.style.getFramedPageOffset(body, newBase.getWindow()); |
| |
| // Adjust Body's margin. |
| pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body)); |
| |
| if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) && |
| !origBase.isCss1CompatMode()) { |
| pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll()); |
| } |
| |
| rect.left += pos.x; |
| rect.top += pos.y; |
| } |
| }; |
| |
| |
| /** |
| * Returns the position of an element relative to another element in the |
| * document. A relative to B |
| * @param {Element|Event|goog.events.Event} a Element or mouse event whose |
| * position we're calculating. |
| * @param {Element|Event|goog.events.Event} b Element or mouse event position |
| * is relative to. |
| * @return {!goog.math.Coordinate} The relative position. |
| */ |
| goog.style.getRelativePosition = function(a, b) { |
| var ap = goog.style.getClientPosition(a); |
| var bp = goog.style.getClientPosition(b); |
| return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y); |
| }; |
| |
| |
| /** |
| * Returns the position of the event or the element's border box relative to |
| * the client viewport. |
| * @param {!Element} el Element whose position to get. |
| * @return {!goog.math.Coordinate} The position. |
| * @private |
| */ |
| goog.style.getClientPositionForElement_ = function(el) { |
| var box = goog.style.getBoundingClientRect_(el); |
| return new goog.math.Coordinate(box.left, box.top); |
| }; |
| |
| |
| /** |
| * Returns the position of the event or the element's border box relative to |
| * the client viewport. |
| * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event. |
| * @return {!goog.math.Coordinate} The position. |
| */ |
| goog.style.getClientPosition = function(el) { |
| goog.asserts.assert(el); |
| if (el.nodeType == goog.dom.NodeType.ELEMENT) { |
| return goog.style.getClientPositionForElement_( |
| /** @type {!Element} */ (el)); |
| } else { |
| var isAbstractedEvent = goog.isFunction(el.getBrowserEvent); |
| var be = /** @type {!goog.events.BrowserEvent} */ (el); |
| var targetEvent = el; |
| |
| if (el.targetTouches && el.targetTouches.length) { |
| targetEvent = el.targetTouches[0]; |
| } else if (isAbstractedEvent && be.getBrowserEvent().targetTouches && |
| be.getBrowserEvent().targetTouches.length) { |
| targetEvent = be.getBrowserEvent().targetTouches[0]; |
| } |
| |
| return new goog.math.Coordinate( |
| targetEvent.clientX, |
| targetEvent.clientY); |
| } |
| }; |
| |
| |
| /** |
| * Moves an element to the given coordinates relative to the client viewport. |
| * @param {Element} el Absolutely positioned element to set page offset for. |
| * It must be in the document. |
| * @param {number|goog.math.Coordinate} x Left position of the element's margin |
| * box or a coordinate object. |
| * @param {number=} opt_y Top position of the element's margin box. |
| */ |
| goog.style.setPageOffset = function(el, x, opt_y) { |
| // Get current pageoffset |
| var cur = goog.style.getPageOffset(el); |
| |
| if (x instanceof goog.math.Coordinate) { |
| opt_y = x.y; |
| x = x.x; |
| } |
| |
| // NOTE(arv): We cannot allow strings for x and y. We could but that would |
| // require us to manually transform between different units |
| |
| // Work out deltas |
| var dx = x - cur.x; |
| var dy = opt_y - cur.y; |
| |
| // Set position to current left/top + delta |
| goog.style.setPosition(el, el.offsetLeft + dx, el.offsetTop + dy); |
| }; |
| |
| |
| /** |
| * Sets the width/height values of an element. If an argument is numeric, |
| * or a goog.math.Size is passed, it is assumed to be pixels and will add |
| * 'px' after converting it to an integer in string form. (This just sets the |
| * CSS width and height properties so it might set content-box or border-box |
| * size depending on the box model the browser is using.) |
| * |
| * @param {Element} element Element to set the size of. |
| * @param {string|number|goog.math.Size} w Width of the element, or a |
| * size object. |
| * @param {string|number=} opt_h Height of the element. Required if w is not a |
| * size object. |
| */ |
| goog.style.setSize = function(element, w, opt_h) { |
| var h; |
| if (w instanceof goog.math.Size) { |
| h = w.height; |
| w = w.width; |
| } else { |
| if (opt_h == undefined) { |
| throw Error('missing height argument'); |
| } |
| h = opt_h; |
| } |
| |
| goog.style.setWidth(element, /** @type {string|number} */ (w)); |
| goog.style.setHeight(element, /** @type {string|number} */ (h)); |
| }; |
| |
| |
| /** |
| * Helper function to create a string to be set into a pixel-value style |
| * property of an element. Can round to the nearest integer value. |
| * |
| * @param {string|number} value The style value to be used. If a number, |
| * 'px' will be appended, otherwise the value will be applied directly. |
| * @param {boolean} round Whether to round the nearest integer (if property |
| * is a number). |
| * @return {string} The string value for the property. |
| * @private |
| */ |
| goog.style.getPixelStyleValue_ = function(value, round) { |
| if (typeof value == 'number') { |
| value = (round ? Math.round(value) : value) + 'px'; |
| } |
| |
| return value; |
| }; |
| |
| |
| /** |
| * Set the height of an element. Sets the element's style property. |
| * @param {Element} element Element to set the height of. |
| * @param {string|number} height The height value to set. If a number, 'px' |
| * will be appended, otherwise the value will be applied directly. |
| */ |
| goog.style.setHeight = function(element, height) { |
| element.style.height = goog.style.getPixelStyleValue_(height, true); |
| }; |
| |
| |
| /** |
| * Set the width of an element. Sets the element's style property. |
| * @param {Element} element Element to set the width of. |
| * @param {string|number} width The width value to set. If a number, 'px' |
| * will be appended, otherwise the value will be applied directly. |
| */ |
| goog.style.setWidth = function(element, width) { |
| element.style.width = goog.style.getPixelStyleValue_(width, true); |
| }; |
| |
| |
| /** |
| * Gets the height and width of an element, even if its display is none. |
| * |
| * Specifically, this returns the height and width of the border box, |
| * irrespective of the box model in effect. |
| * |
| * Note that this function does not take CSS transforms into account. Please see |
| * {@code goog.style.getTransformedSize}. |
| * @param {Element} element Element to get size of. |
| * @return {!goog.math.Size} Object with width/height properties. |
| */ |
| goog.style.getSize = function(element) { |
| return goog.style.evaluateWithTemporaryDisplay_( |
| goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element)); |
| }; |
| |
| |
| /** |
| * Call {@code fn} on {@code element} such that {@code element}'s dimensions are |
| * accurate when it's passed to {@code fn}. |
| * @param {function(!Element): T} fn Function to call with {@code element} as |
| * an argument after temporarily changing {@code element}'s display such |
| * that its dimensions are accurate. |
| * @param {!Element} element Element (which may have display none) to use as |
| * argument to {@code fn}. |
| * @return {T} Value returned by calling {@code fn} with {@code element}. |
| * @template T |
| * @private |
| */ |
| goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) { |
| if (goog.style.getStyle_(element, 'display') != 'none') { |
| return fn(element); |
| } |
| |
| var style = element.style; |
| var originalDisplay = style.display; |
| var originalVisibility = style.visibility; |
| var originalPosition = style.position; |
| |
| style.visibility = 'hidden'; |
| style.position = 'absolute'; |
| style.display = 'inline'; |
| |
| var retVal = fn(element); |
| |
| style.display = originalDisplay; |
| style.position = originalPosition; |
| style.visibility = originalVisibility; |
| |
| return retVal; |
| }; |
| |
| |
| /** |
| * Gets the height and width of an element when the display is not none. |
| * @param {Element} element Element to get size of. |
| * @return {!goog.math.Size} Object with width/height properties. |
| * @private |
| */ |
| goog.style.getSizeWithDisplay_ = function(element) { |
| var offsetWidth = element.offsetWidth; |
| var offsetHeight = element.offsetHeight; |
| var webkitOffsetsZero = |
| goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight; |
| if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) && |
| element.getBoundingClientRect) { |
| // Fall back to calling getBoundingClientRect when offsetWidth or |
| // offsetHeight are not defined, or when they are zero in WebKit browsers. |
| // This makes sure that we return for the correct size for SVG elements, but |
| // will still return 0 on Webkit prior to 534.8, see |
| // http://trac.webkit.org/changeset/67252. |
| var clientRect = goog.style.getBoundingClientRect_(element); |
| return new goog.math.Size(clientRect.right - clientRect.left, |
| clientRect.bottom - clientRect.top); |
| } |
| return new goog.math.Size(offsetWidth, offsetHeight); |
| }; |
| |
| |
| /** |
| * Gets the height and width of an element, post transform, even if its display |
| * is none. |
| * |
| * This is like {@code goog.style.getSize}, except: |
| * <ol> |
| * <li>Takes webkitTransforms such as rotate and scale into account. |
| * <li>Will return null if {@code element} doesn't respond to |
| * {@code getBoundingClientRect}. |
| * <li>Currently doesn't make sense on non-WebKit browsers which don't support |
| * webkitTransforms. |
| * </ol> |
| * @param {!Element} element Element to get size of. |
| * @return {goog.math.Size} Object with width/height properties. |
| */ |
| goog.style.getTransformedSize = function(element) { |
| if (!element.getBoundingClientRect) { |
| return null; |
| } |
| |
| var clientRect = goog.style.evaluateWithTemporaryDisplay_( |
| goog.style.getBoundingClientRect_, element); |
| return new goog.math.Size(clientRect.right - clientRect.left, |
| clientRect.bottom - clientRect.top); |
| }; |
| |
| |
| /** |
| * Returns a bounding rectangle for a given element in page space. |
| * @param {Element} element Element to get bounds of. Must not be display none. |
| * @return {!goog.math.Rect} Bounding rectangle for the element. |
| */ |
| goog.style.getBounds = function(element) { |
| var o = goog.style.getPageOffset(element); |
| var s = goog.style.getSize(element); |
| return new goog.math.Rect(o.x, o.y, s.width, s.height); |
| }; |
| |
| |
| /** |
| * Converts a CSS selector in the form style-property to styleProperty. |
| * @param {*} selector CSS Selector. |
| * @return {string} Camel case selector. |
| * @deprecated Use goog.string.toCamelCase instead. |
| */ |
| goog.style.toCamelCase = function(selector) { |
| return goog.string.toCamelCase(String(selector)); |
| }; |
| |
| |
| /** |
| * Converts a CSS selector in the form styleProperty to style-property. |
| * @param {string} selector Camel case selector. |
| * @return {string} Selector cased. |
| * @deprecated Use goog.string.toSelectorCase instead. |
| */ |
| goog.style.toSelectorCase = function(selector) { |
| return goog.string.toSelectorCase(selector); |
| }; |
| |
| |
| /** |
| * Gets the opacity of a node (x-browser). This gets the inline style opacity |
| * of the node, and does not take into account the cascaded or the computed |
| * style for this node. |
| * @param {Element} el Element whose opacity has to be found. |
| * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''} |
| * if the opacity is not set. |
| */ |
| goog.style.getOpacity = function(el) { |
| var style = el.style; |
| var result = ''; |
| if ('opacity' in style) { |
| result = style.opacity; |
| } else if ('MozOpacity' in style) { |
| result = style.MozOpacity; |
| } else if ('filter' in style) { |
| var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/); |
| if (match) { |
| result = String(match[1] / 100); |
| } |
| } |
| return result == '' ? result : Number(result); |
| }; |
| |
| |
| /** |
| * Sets the opacity of a node (x-browser). |
| * @param {Element} el Elements whose opacity has to be set. |
| * @param {number|string} alpha Opacity between 0 and 1 or an empty string |
| * {@code ''} to clear the opacity. |
| */ |
| goog.style.setOpacity = function(el, alpha) { |
| var style = el.style; |
| if ('opacity' in style) { |
| style.opacity = alpha; |
| } else if ('MozOpacity' in style) { |
| style.MozOpacity = alpha; |
| } else if ('filter' in style) { |
| // TODO(arv): Overwriting the filter might have undesired side effects. |
| if (alpha === '') { |
| style.filter = ''; |
| } else { |
| style.filter = 'alpha(opacity=' + alpha * 100 + ')'; |
| } |
| } |
| }; |
| |
| |
| /** |
| * Sets the background of an element to a transparent image in a browser- |
| * independent manner. |
| * |
| * This function does not support repeating backgrounds or alternate background |
| * positions to match the behavior of Internet Explorer. It also does not |
| * support sizingMethods other than crop since they cannot be replicated in |
| * browsers other than Internet Explorer. |
| * |
| * @param {Element} el The element to set background on. |
| * @param {string} src The image source URL. |
| */ |
| goog.style.setTransparentBackgroundImage = function(el, src) { |
| var style = el.style; |
| // It is safe to use the style.filter in IE only. In Safari 'filter' is in |
| // style object but access to style.filter causes it to throw an exception. |
| // Note: IE8 supports images with an alpha channel. |
| if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { |
| // See TODO in setOpacity. |
| style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' + |
| 'src="' + src + '", sizingMethod="crop")'; |
| } else { |
| // Set style properties individually instead of using background shorthand |
| // to prevent overwriting a pre-existing background color. |
| style.backgroundImage = 'url(' + src + ')'; |
| style.backgroundPosition = 'top left'; |
| style.backgroundRepeat = 'no-repeat'; |
| } |
| }; |
| |
| |
| /** |
| * Clears the background image of an element in a browser independent manner. |
| * @param {Element} el The element to clear background image for. |
| */ |
| goog.style.clearTransparentBackgroundImage = function(el) { |
| var style = el.style; |
| if ('filter' in style) { |
| // See TODO in setOpacity. |
| style.filter = ''; |
| } else { |
| // Set style properties individually instead of using background shorthand |
| // to prevent overwriting a pre-existing background color. |
| style.backgroundImage = 'none'; |
| } |
| }; |
| |
| |
| /** |
| * Shows or hides an element from the page. Hiding the element is done by |
| * setting the display property to "none", removing the element from the |
| * rendering hierarchy so it takes up no space. To show the element, the default |
| * inherited display property is restored (defined either in stylesheets or by |
| * the browser's default style rules.) |
| * |
| * Caveat 1: if the inherited display property for the element is set to "none" |
| * by the stylesheets, that is the property that will be restored by a call to |
| * showElement(), effectively toggling the display between "none" and "none". |
| * |
| * Caveat 2: if the element display style is set inline (by setting either |
| * element.style.display or a style attribute in the HTML), a call to |
| * showElement will clear that setting and defer to the inherited style in the |
| * stylesheet. |
| * @param {Element} el Element to show or hide. |
| * @param {*} display True to render the element in its default style, |
| * false to disable rendering the element. |
| * @deprecated Use goog.style.setElementShown instead. |
| */ |
| goog.style.showElement = function(el, display) { |
| goog.style.setElementShown(el, display); |
| }; |
| |
| |
| /** |
| * Shows or hides an element from the page. Hiding the element is done by |
| * setting the display property to "none", removing the element from the |
| * rendering hierarchy so it takes up no space. To show the element, the default |
| * inherited display property is restored (defined either in stylesheets or by |
| * the browser's default style rules). |
| * |
| * Caveat 1: if the inherited display property for the element is set to "none" |
| * by the stylesheets, that is the property that will be restored by a call to |
| * setElementShown(), effectively toggling the display between "none" and |
| * "none". |
| * |
| * Caveat 2: if the element display style is set inline (by setting either |
| * element.style.display or a style attribute in the HTML), a call to |
| * setElementShown will clear that setting and defer to the inherited style in |
| * the stylesheet. |
| * @param {Element} el Element to show or hide. |
| * @param {*} isShown True to render the element in its default style, |
| * false to disable rendering the element. |
| */ |
| goog.style.setElementShown = function(el, isShown) { |
| el.style.display = isShown ? '' : 'none'; |
| }; |
| |
| |
| /** |
| * Test whether the given element has been shown or hidden via a call to |
| * {@link #setElementShown}. |
| * |
| * Note this is strictly a companion method for a call |
| * to {@link #setElementShown} and the same caveats apply; in particular, this |
| * method does not guarantee that the return value will be consistent with |
| * whether or not the element is actually visible. |
| * |
| * @param {Element} el The element to test. |
| * @return {boolean} Whether the element has been shown. |
| * @see #setElementShown |
| */ |
| goog.style.isElementShown = function(el) { |
| return el.style.display != 'none'; |
| }; |
| |
| |
| /** |
| * Installs the styles string into the window that contains opt_element. If |
| * opt_element is null, the main window is used. |
| * @param {string} stylesString The style string to install. |
| * @param {Node=} opt_node Node whose parent document should have the |
| * styles installed. |
| * @return {Element|StyleSheet} The style element created. |
| */ |
| goog.style.installStyles = function(stylesString, opt_node) { |
| var dh = goog.dom.getDomHelper(opt_node); |
| var styleSheet = null; |
| |
| // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be |
| // undefined as of IE 11. |
| var doc = dh.getDocument(); |
| if (goog.userAgent.IE && doc.createStyleSheet) { |
| styleSheet = doc.createStyleSheet(); |
| goog.style.setStyles(styleSheet, stylesString); |
| } else { |
| var head = dh.getElementsByTagNameAndClass('head')[0]; |
| |
| // In opera documents are not guaranteed to have a head element, thus we |
| // have to make sure one exists before using it. |
| if (!head) { |
| var body = dh.getElementsByTagNameAndClass('body')[0]; |
| head = dh.createDom('head'); |
| body.parentNode.insertBefore(head, body); |
| } |
| styleSheet = dh.createDom('style'); |
| // NOTE(user): Setting styles after the style element has been appended |
| // to the head results in a nasty Webkit bug in certain scenarios. Please |
| // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional |
| // details. |
| goog.style.setStyles(styleSheet, stylesString); |
| dh.appendChild(head, styleSheet); |
| } |
| return styleSheet; |
| }; |
| |
| |
| /** |
| * Removes the styles added by {@link #installStyles}. |
| * @param {Element|StyleSheet} styleSheet The value returned by |
| * {@link #installStyles}. |
| */ |
| goog.style.uninstallStyles = function(styleSheet) { |
| var node = styleSheet.ownerNode || styleSheet.owningElement || |
| /** @type {Element} */ (styleSheet); |
| goog.dom.removeNode(node); |
| }; |
| |
| |
| /** |
| * Sets the content of a style element. The style element can be any valid |
| * style element. This element will have its content completely replaced by |
| * the new stylesString. |
| * @param {Element|StyleSheet} element A stylesheet element as returned by |
| * installStyles. |
| * @param {string} stylesString The new content of the stylesheet. |
| */ |
| goog.style.setStyles = function(element, stylesString) { |
| if (goog.userAgent.IE && goog.isDef(element.cssText)) { |
| // Adding the selectors individually caused the browser to hang if the |
| // selector was invalid or there were CSS comments. Setting the cssText of |
| // the style node works fine and ignores CSS that IE doesn't understand. |
| // However IE >= 11 doesn't support cssText any more, so we make sure that |
| // cssText is a defined property and otherwise fall back to innerHTML. |
| element.cssText = stylesString; |
| } else { |
| element.innerHTML = stylesString; |
| } |
| }; |
| |
| |
| /** |
| * Sets 'white-space: pre-wrap' for a node (x-browser). |
| * |
| * There are as many ways of specifying pre-wrap as there are browsers. |
| * |
| * CSS3/IE8: white-space: pre-wrap; |
| * Mozilla: white-space: -moz-pre-wrap; |
| * Opera: white-space: -o-pre-wrap; |
| * IE6/7: white-space: pre; word-wrap: break-word; |
| * |
| * @param {Element} el Element to enable pre-wrap for. |
| */ |
| goog.style.setPreWrap = function(el) { |
| var style = el.style; |
| if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { |
| style.whiteSpace = 'pre'; |
| style.wordWrap = 'break-word'; |
| } else if (goog.userAgent.GECKO) { |
| style.whiteSpace = '-moz-pre-wrap'; |
| } else { |
| style.whiteSpace = 'pre-wrap'; |
| } |
| }; |
| |
| |
| /** |
| * Sets 'display: inline-block' for an element (cross-browser). |
| * @param {Element} el Element to which the inline-block display style is to be |
| * applied. |
| * @see ../demos/inline_block_quirks.html |
| * @see ../demos/inline_block_standards.html |
| */ |
| goog.style.setInlineBlock = function(el) { |
| var style = el.style; |
| // Without position:relative, weirdness ensues. Just accept it and move on. |
| style.position = 'relative'; |
| |
| if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) { |
| // IE8 supports inline-block so fall through to the else |
| // Zoom:1 forces hasLayout, display:inline gives inline behavior. |
| style.zoom = '1'; |
| style.display = 'inline'; |
| } else { |
| // Opera, Webkit, and Safari seem to do OK with the standard inline-block |
| // style. |
| style.display = 'inline-block'; |
| } |
| }; |
| |
| |
| /** |
| * Returns true if the element is using right to left (rtl) direction. |
| * @param {Element} el The element to test. |
| * @return {boolean} True for right to left, false for left to right. |
| */ |
| goog.style.isRightToLeft = function(el) { |
| return 'rtl' == goog.style.getStyle_(el, 'direction'); |
| }; |
| |
| |
| /** |
| * The CSS style property corresponding to an element being |
| * unselectable on the current browser platform (null if none). |
| * Opera and IE instead use a DOM attribute 'unselectable'. |
| * @type {?string} |
| * @private |
| */ |
| goog.style.unselectableStyle_ = |
| goog.userAgent.GECKO ? 'MozUserSelect' : |
| goog.userAgent.WEBKIT ? 'WebkitUserSelect' : |
| null; |
| |
| |
| /** |
| * Returns true if the element is set to be unselectable, false otherwise. |
| * Note that on some platforms (e.g. Mozilla), even if an element isn't set |
| * to be unselectable, it will behave as such if any of its ancestors is |
| * unselectable. |
| * @param {Element} el Element to check. |
| * @return {boolean} Whether the element is set to be unselectable. |
| */ |
| goog.style.isUnselectable = function(el) { |
| if (goog.style.unselectableStyle_) { |
| return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none'; |
| } else if (goog.userAgent.IE || goog.userAgent.OPERA) { |
| return el.getAttribute('unselectable') == 'on'; |
| } |
| return false; |
| }; |
| |
| |
| /** |
| * Makes the element and its descendants selectable or unselectable. Note |
| * that on some platforms (e.g. Mozilla), even if an element isn't set to |
| * be unselectable, it will behave as such if any of its ancestors is |
| * unselectable. |
| * @param {Element} el The element to alter. |
| * @param {boolean} unselectable Whether the element and its descendants |
| * should be made unselectable. |
| * @param {boolean=} opt_noRecurse Whether to only alter the element's own |
| * selectable state, and leave its descendants alone; defaults to false. |
| */ |
| goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) { |
| // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure? |
| var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null; |
| var name = goog.style.unselectableStyle_; |
| if (name) { |
| // Add/remove the appropriate CSS style to/from the element and its |
| // descendants. |
| var value = unselectable ? 'none' : ''; |
| el.style[name] = value; |
| if (descendants) { |
| for (var i = 0, descendant; descendant = descendants[i]; i++) { |
| descendant.style[name] = value; |
| } |
| } |
| } else if (goog.userAgent.IE || goog.userAgent.OPERA) { |
| // Toggle the 'unselectable' attribute on the element and its descendants. |
| var value = unselectable ? 'on' : ''; |
| el.setAttribute('unselectable', value); |
| if (descendants) { |
| for (var i = 0, descendant; descendant = descendants[i]; i++) { |
| descendant.setAttribute('unselectable', value); |
| } |
| } |
| } |
| }; |
| |
| |
| /** |
| * Gets the border box size for an element. |
| * @param {Element} element The element to get the size for. |
| * @return {!goog.math.Size} The border box size. |
| */ |
| goog.style.getBorderBoxSize = function(element) { |
| return new goog.math.Size(element.offsetWidth, element.offsetHeight); |
| }; |
| |
| |
| /** |
| * Sets the border box size of an element. This is potentially expensive in IE |
| * if the document is CSS1Compat mode |
| * @param {Element} element The element to set the size on. |
| * @param {goog.math.Size} size The new size. |
| */ |
| goog.style.setBorderBoxSize = function(element, size) { |
| var doc = goog.dom.getOwnerDocument(element); |
| var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode(); |
| |
| if (goog.userAgent.IE && |
| !goog.userAgent.isVersionOrHigher('10') && |
| (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) { |
| var style = element.style; |
| if (isCss1CompatMode) { |
| var paddingBox = goog.style.getPaddingBox(element); |
| var borderBox = goog.style.getBorderBox(element); |
| style.pixelWidth = size.width - borderBox.left - paddingBox.left - |
| paddingBox.right - borderBox.right; |
| style.pixelHeight = size.height - borderBox.top - paddingBox.top - |
| paddingBox.bottom - borderBox.bottom; |
| } else { |
| style.pixelWidth = size.width; |
| style.pixelHeight = size.height; |
| } |
| } else { |
| goog.style.setBoxSizingSize_(element, size, 'border-box'); |
| } |
| }; |
| |
| |
| /** |
| * Gets the content box size for an element. This is potentially expensive in |
| * all browsers. |
| * @param {Element} element The element to get the size for. |
| * @return {!goog.math.Size} The content box size. |
| */ |
| goog.style.getContentBoxSize = function(element) { |
| var doc = goog.dom.getOwnerDocument(element); |
| var ieCurrentStyle = goog.userAgent.IE && element.currentStyle; |
| if (ieCurrentStyle && |
| goog.dom.getDomHelper(doc).isCss1CompatMode() && |
| ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' && |
| !ieCurrentStyle.boxSizing) { |
| // If IE in CSS1Compat mode than just use the width and height. |
| // If we have a boxSizing then fall back on measuring the borders etc. |
| var width = goog.style.getIePixelValue_(element, ieCurrentStyle.width, |
| 'width', 'pixelWidth'); |
| var height = goog.style.getIePixelValue_(element, ieCurrentStyle.height, |
| 'height', 'pixelHeight'); |
| return new goog.math.Size(width, height); |
| } else { |
| var borderBoxSize = goog.style.getBorderBoxSize(element); |
| var paddingBox = goog.style.getPaddingBox(element); |
| var borderBox = goog.style.getBorderBox(element); |
| return new goog.math.Size(borderBoxSize.width - |
| borderBox.left - paddingBox.left - |
| paddingBox.right - borderBox.right, |
| borderBoxSize.height - |
| borderBox.top - paddingBox.top - |
| paddingBox.bottom - borderBox.bottom); |
| } |
| }; |
| |
| |
| /** |
| * Sets the content box size of an element. This is potentially expensive in IE |
| * if the document is BackCompat mode. |
| * @param {Element} element The element to set the size on. |
| * @param {goog.math.Size} size The new size. |
| */ |
| goog.style.setContentBoxSize = function(element, size) { |
| var doc = goog.dom.getOwnerDocument(element); |
| var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode(); |
| if (goog.userAgent.IE && |
| !goog.userAgent.isVersionOrHigher('10') && |
| (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) { |
| var style = element.style; |
| if (isCss1CompatMode) { |
| style.pixelWidth = size.width; |
| style.pixelHeight = size.height; |
| } else { |
| var paddingBox = goog.style.getPaddingBox(element); |
| var borderBox = goog.style.getBorderBox(element); |
| style.pixelWidth = size.width + borderBox.left + paddingBox.left + |
| paddingBox.right + borderBox.right; |
| style.pixelHeight = size.height + borderBox.top + paddingBox.top + |
| paddingBox.bottom + borderBox.bottom; |
| } |
| } else { |
| goog.style.setBoxSizingSize_(element, size, 'content-box'); |
| } |
| }; |
| |
| |
| /** |
| * Helper function that sets the box sizing as well as the width and height |
| * @param {Element} element The element to set the size on. |
| * @param {goog.math.Size} size The new size to set. |
| * @param {string} boxSizing The box-sizing value. |
| * @private |
| */ |
| goog.style.setBoxSizingSize_ = function(element, size, boxSizing) { |
| var style = element.style; |
| if (goog.userAgent.GECKO) { |
| style.MozBoxSizing = boxSizing; |
| } else if (goog.userAgent.WEBKIT) { |
| style.WebkitBoxSizing = boxSizing; |
| } else { |
| // Includes IE8 and Opera 9.50+ |
| style.boxSizing = boxSizing; |
| } |
| |
| // Setting this to a negative value will throw an exception on IE |
| // (and doesn't do anything different than setting it to 0). |
| style.width = Math.max(size.width, 0) + 'px'; |
| style.height = Math.max(size.height, 0) + 'px'; |
| }; |
| |
| |
| /** |
| * IE specific function that converts a non pixel unit to pixels. |
| * @param {Element} element The element to convert the value for. |
| * @param {string} value The current value as a string. The value must not be |
| * ''. |
| * @param {string} name The CSS property name to use for the converstion. This |
| * should be 'left', 'top', 'width' or 'height'. |
| * @param {string} pixelName The CSS pixel property name to use to get the |
| * value in pixels. |
| * @return {number} The value in pixels. |
| * @private |
| */ |
| goog.style.getIePixelValue_ = function(element, value, name, pixelName) { |
| // Try if we already have a pixel value. IE does not do half pixels so we |
| // only check if it matches a number followed by 'px'. |
| if (/^\d+px?$/.test(value)) { |
| return parseInt(value, 10); |
| } else { |
| var oldStyleValue = element.style[name]; |
| var oldRuntimeValue = element.runtimeStyle[name]; |
| // set runtime style to prevent changes |
| element.runtimeStyle[name] = element.currentStyle[name]; |
| element.style[name] = value; |
| var pixelValue = element.style[pixelName]; |
| // restore |
| element.style[name] = oldStyleValue; |
| element.runtimeStyle[name] = oldRuntimeValue; |
| return pixelValue; |
| } |
| }; |
| |
| |
| /** |
| * Helper function for getting the pixel padding or margin for IE. |
| * @param {Element} element The element to get the padding for. |
| * @param {string} propName The property name. |
| * @return {number} The pixel padding. |
| * @private |
| */ |
| goog.style.getIePixelDistance_ = function(element, propName) { |
| var value = goog.style.getCascadedStyle(element, propName); |
| return value ? |
| goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') : 0; |
| }; |
| |
| |
| /** |
| * Gets the computed paddings or margins (on all sides) in pixels. |
| * @param {Element} element The element to get the padding for. |
| * @param {string} stylePrefix Pass 'padding' to retrieve the padding box, |
| * or 'margin' to retrieve the margin box. |
| * @return {!goog.math.Box} The computed paddings or margins. |
| * @private |
| */ |
| goog.style.getBox_ = function(element, stylePrefix) { |
| if (goog.userAgent.IE) { |
| var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left'); |
| var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right'); |
| var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top'); |
| var bottom = goog.style.getIePixelDistance_( |
| element, stylePrefix + 'Bottom'); |
| return new goog.math.Box(top, right, bottom, left); |
| } else { |
| // On non-IE browsers, getComputedStyle is always non-null. |
| var left = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, stylePrefix + 'Left')); |
| var right = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, stylePrefix + 'Right')); |
| var top = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, stylePrefix + 'Top')); |
| var bottom = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, stylePrefix + 'Bottom')); |
| |
| // NOTE(arv): Gecko can return floating point numbers for the computed |
| // style values. |
| return new goog.math.Box(parseFloat(top), |
| parseFloat(right), |
| parseFloat(bottom), |
| parseFloat(left)); |
| } |
| }; |
| |
| |
| /** |
| * Gets the computed paddings (on all sides) in pixels. |
| * @param {Element} element The element to get the padding for. |
| * @return {!goog.math.Box} The computed paddings. |
| */ |
| goog.style.getPaddingBox = function(element) { |
| return goog.style.getBox_(element, 'padding'); |
| }; |
| |
| |
| /** |
| * Gets the computed margins (on all sides) in pixels. |
| * @param {Element} element The element to get the margins for. |
| * @return {!goog.math.Box} The computed margins. |
| */ |
| goog.style.getMarginBox = function(element) { |
| return goog.style.getBox_(element, 'margin'); |
| }; |
| |
| |
| /** |
| * A map used to map the border width keywords to a pixel width. |
| * @type {Object} |
| * @private |
| */ |
| goog.style.ieBorderWidthKeywords_ = { |
| 'thin': 2, |
| 'medium': 4, |
| 'thick': 6 |
| }; |
| |
| |
| /** |
| * Helper function for IE to get the pixel border. |
| * @param {Element} element The element to get the pixel border for. |
| * @param {string} prop The part of the property name. |
| * @return {number} The value in pixels. |
| * @private |
| */ |
| goog.style.getIePixelBorder_ = function(element, prop) { |
| if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') { |
| return 0; |
| } |
| var width = goog.style.getCascadedStyle(element, prop + 'Width'); |
| if (width in goog.style.ieBorderWidthKeywords_) { |
| return goog.style.ieBorderWidthKeywords_[width]; |
| } |
| return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft'); |
| }; |
| |
| |
| /** |
| * Gets the computed border widths (on all sides) in pixels |
| * @param {Element} element The element to get the border widths for. |
| * @return {!goog.math.Box} The computed border widths. |
| */ |
| goog.style.getBorderBox = function(element) { |
| if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) { |
| var left = goog.style.getIePixelBorder_(element, 'borderLeft'); |
| var right = goog.style.getIePixelBorder_(element, 'borderRight'); |
| var top = goog.style.getIePixelBorder_(element, 'borderTop'); |
| var bottom = goog.style.getIePixelBorder_(element, 'borderBottom'); |
| return new goog.math.Box(top, right, bottom, left); |
| } else { |
| // On non-IE browsers, getComputedStyle is always non-null. |
| var left = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, 'borderLeftWidth')); |
| var right = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, 'borderRightWidth')); |
| var top = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, 'borderTopWidth')); |
| var bottom = /** @type {string} */ ( |
| goog.style.getComputedStyle(element, 'borderBottomWidth')); |
| |
| return new goog.math.Box(parseFloat(top), |
| parseFloat(right), |
| parseFloat(bottom), |
| parseFloat(left)); |
| } |
| }; |
| |
| |
| /** |
| * Returns the font face applied to a given node. Opera and IE should return |
| * the font actually displayed. Firefox returns the author's most-preferred |
| * font (whether the browser is capable of displaying it or not.) |
| * @param {Element} el The element whose font family is returned. |
| * @return {string} The font family applied to el. |
| */ |
| goog.style.getFontFamily = function(el) { |
| var doc = goog.dom.getOwnerDocument(el); |
| var font = ''; |
| // The moveToElementText method from the TextRange only works if the element |
| // is attached to the owner document. |
| if (doc.body.createTextRange && goog.dom.contains(doc, el)) { |
| var range = doc.body.createTextRange(); |
| range.moveToElementText(el); |
| /** @preserveTry */ |
| try { |
| font = range.queryCommandValue('FontName'); |
| } catch (e) { |
| // This is a workaround for a awkward exception. |
| // On some IE, there is an exception coming from it. |
| // The error description from this exception is: |
| // This window has already been registered as a drop target |
| // This is bogus description, likely due to a bug in ie. |
| font = ''; |
| } |
| } |
| if (!font) { |
| // Note if for some reason IE can't derive FontName with a TextRange, we |
| // fallback to using currentStyle |
| font = goog.style.getStyle_(el, 'fontFamily'); |
| } |
| |
| // Firefox returns the applied font-family string (author's list of |
| // preferred fonts.) We want to return the most-preferred font, in lieu of |
| // the *actually* applied font. |
| var fontsArray = font.split(','); |
| if (fontsArray.length > 1) font = fontsArray[0]; |
| |
| // Sanitize for x-browser consistency: |
| // Strip quotes because browsers aren't consistent with how they're |
| // applied; Opera always encloses, Firefox sometimes, and IE never. |
| return goog.string.stripQuotes(font, '"\''); |
| }; |
| |
| |
| /** |
| * Regular expression used for getLengthUnits. |
| * @type {RegExp} |
| * @private |
| */ |
| goog.style.lengthUnitRegex_ = /[^\d]+$/; |
| |
| |
| /** |
| * Returns the units used for a CSS length measurement. |
| * @param {string} value A CSS length quantity. |
| * @return {?string} The units of measurement. |
| */ |
| goog.style.getLengthUnits = function(value) { |
| var units = value.match(goog.style.lengthUnitRegex_); |
| return units && units[0] || null; |
| }; |
| |
| |
| /** |
| * Map of absolute CSS length units |
| * @type {Object} |
| * @private |
| */ |
| goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = { |
| 'cm' : 1, |
| 'in' : 1, |
| 'mm' : 1, |
| 'pc' : 1, |
| 'pt' : 1 |
| }; |
| |
| |
| /** |
| * Map of relative CSS length units that can be accurately converted to px |
| * font-size values using getIePixelValue_. Only units that are defined in |
| * relation to a font size are convertible (%, small, etc. are not). |
| * @type {Object} |
| * @private |
| */ |
| goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = { |
| 'em' : 1, |
| 'ex' : 1 |
| }; |
| |
| |
| /** |
| * Returns the font size, in pixels, of text in an element. |
| * @param {Element} el The element whose font size is returned. |
| * @return {number} The font size (in pixels). |
| */ |
| goog.style.getFontSize = function(el) { |
| var fontSize = goog.style.getStyle_(el, 'fontSize'); |
| var sizeUnits = goog.style.getLengthUnits(fontSize); |
| if (fontSize && 'px' == sizeUnits) { |
| // NOTE(user): This could be parseFloat instead, but IE doesn't return |
| // decimal fractions in getStyle_ and Firefox reports the fractions, but |
| // ignores them when rendering. Interestingly enough, when we force the |
| // issue and size something to e.g., 50% of 25px, the browsers round in |
| // opposite directions with Firefox reporting 12px and IE 13px. I punt. |
| return parseInt(fontSize, 10); |
| } |
| |
| // In IE, we can convert absolute length units to a px value using |
| // goog.style.getIePixelValue_. Units defined in relation to a font size |
| // (em, ex) are applied relative to the element's parentNode and can also |
| // be converted. |
| if (goog.userAgent.IE) { |
| if (sizeUnits in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) { |
| return goog.style.getIePixelValue_(el, |
| fontSize, |
| 'left', |
| 'pixelLeft'); |
| } else if (el.parentNode && |
| el.parentNode.nodeType == goog.dom.NodeType.ELEMENT && |
| sizeUnits in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) { |
| // Check the parent size - if it is the same it means the relative size |
| // value is inherited and we therefore don't want to count it twice. If |
| // it is different, this element either has explicit style or has a CSS |
| // rule applying to it. |
| var parentElement = /** @type {!Element} */ (el.parentNode); |
| var parentSize = goog.style.getStyle_(parentElement, 'fontSize'); |
| return goog.style.getIePixelValue_(parentElement, |
| fontSize == parentSize ? |
| '1em' : fontSize, |
| 'left', |
| 'pixelLeft'); |
| } |
| } |
| |
| // Sometimes we can't cleanly find the font size (some units relative to a |
| // node's parent's font size are difficult: %, smaller et al), so we create |
| // an invisible, absolutely-positioned span sized to be the height of an 'M' |
| // rendered in its parent's (i.e., our target element's) font size. This is |
| // the definition of CSS's font size attribute. |
| var sizeElement = goog.dom.createDom( |
| 'span', |
| {'style': 'visibility:hidden;position:absolute;' + |
| 'line-height:0;padding:0;margin:0;border:0;height:1em;'}); |
| goog.dom.appendChild(el, sizeElement); |
| fontSize = sizeElement.offsetHeight; |
| goog.dom.removeNode(sizeElement); |
| |
| return fontSize; |
| }; |
| |
| |
| /** |
| * Parses a style attribute value. Converts CSS property names to camel case. |
| * @param {string} value The style attribute value. |
| * @return {!Object} Map of CSS properties to string values. |
| */ |
| goog.style.parseStyleAttribute = function(value) { |
| var result = {}; |
| goog.array.forEach(value.split(/\s*;\s*/), function(pair) { |
| var keyValue = pair.split(/\s*:\s*/); |
| if (keyValue.length == 2) { |
| result[goog.string.toCamelCase(keyValue[0].toLowerCase())] = keyValue[1]; |
| } |
| }); |
| return result; |
| }; |
| |
| |
| /** |
| * Reverse of parseStyleAttribute; that is, takes a style object and returns the |
| * corresponding attribute value. Converts camel case property names to proper |
| * CSS selector names. |
| * @param {Object} obj Map of CSS properties to values. |
| * @return {string} The style attribute value. |
| */ |
| goog.style.toStyleAttribute = function(obj) { |
| var buffer = []; |
| goog.object.forEach(obj, function(value, key) { |
| buffer.push(goog.string.toSelectorCase(key), ':', value, ';'); |
| }); |
| return buffer.join(''); |
| }; |
| |
| |
| /** |
| * Sets CSS float property on an element. |
| * @param {Element} el The element to set float property on. |
| * @param {string} value The value of float CSS property to set on this element. |
| */ |
| goog.style.setFloat = function(el, value) { |
| el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value; |
| }; |
| |
| |
| /** |
| * Gets value of explicitly-set float CSS property on an element. |
| * @param {Element} el The element to get float property of. |
| * @return {string} The value of explicitly-set float CSS property on this |
| * element. |
| */ |
| goog.style.getFloat = function(el) { |
| return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || ''; |
| }; |
| |
| |
| /** |
| * Returns the scroll bar width (represents the width of both horizontal |
| * and vertical scroll). |
| * |
| * @param {string=} opt_className An optional class name (or names) to apply |
| * to the invisible div created to measure the scrollbar. This is necessary |
| * if some scrollbars are styled differently than others. |
| * @return {number} The scroll bar width in px. |
| */ |
| goog.style.getScrollbarWidth = function(opt_className) { |
| // Add two hidden divs. The child div is larger than the parent and |
| // forces scrollbars to appear on it. |
| // Using overflow:scroll does not work consistently with scrollbars that |
| // are styled with ::-webkit-scrollbar. |
| var outerDiv = goog.dom.createElement('div'); |
| if (opt_className) { |
| outerDiv.className = opt_className; |
| } |
| outerDiv.style.cssText = 'overflow:auto;' + |
| 'position:absolute;top:0;width:100px;height:100px'; |
| var innerDiv = goog.dom.createElement('div'); |
| goog.style.setSize(innerDiv, '200px', '200px'); |
| outerDiv.appendChild(innerDiv); |
| goog.dom.appendChild(goog.dom.getDocument().body, outerDiv); |
| var width = outerDiv.offsetWidth - outerDiv.clientWidth; |
| goog.dom.removeNode(outerDiv); |
| return width; |
| }; |
| |
| |
| /** |
| * Regular expression to extract x and y translation components from a CSS |
| * transform Matrix representation. |
| * |
| * @type {!RegExp} |
| * @const |
| * @private |
| */ |
| goog.style.MATRIX_TRANSLATION_REGEX_ = |
| new RegExp('matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' + |
| '[0-9\\.\\-]+, [0-9\\.\\-]+, ' + |
| '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)'); |
| |
| |
| /** |
| * Returns the x,y translation component of any CSS transforms applied to the |
| * element, in pixels. |
| * |
| * @param {!Element} element The element to get the translation of. |
| * @return {!goog.math.Coordinate} The CSS translation of the element in px. |
| */ |
| goog.style.getCssTranslation = function(element) { |
| var transform = goog.style.getComputedTransform(element); |
| if (!transform) { |
| return new goog.math.Coordinate(0, 0); |
| } |
| var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_); |
| if (!matches) { |
| return new goog.math.Coordinate(0, 0); |
| } |
| return new goog.math.Coordinate(parseFloat(matches[1]), |
| parseFloat(matches[2])); |
| }; |