# lib/goog/style/style.js

    1// Copyright 2006 The Closure Library Authors. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS-IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.1415/**16 * @fileoverview Utilities for element styles.17 *18 * @see ../demos/inline_block_quirks.html19 * @see ../demos/inline_block_standards.html20 * @see ../demos/style_viewport.html21 */2223goog.provide('goog.style');242526goog.require('goog.array');27goog.require('goog.asserts');28goog.require('goog.dom');29goog.require('goog.dom.NodeType');30goog.require('goog.dom.vendor');31goog.require('goog.math.Box');32goog.require('goog.math.Coordinate');33goog.require('goog.math.Rect');34goog.require('goog.math.Size');35goog.require('goog.object');36goog.require('goog.string');37goog.require('goog.userAgent');383940/**41 * @define {boolean} Whether we know at compile time that42 *     getBoundingClientRect() is present and bug-free on the browser.43 */44goog.define('goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS', false);454647/**48 * Sets a style value on an element.49 *50 * This function is not indended to patch issues in the browser's style51 * handling, but to allow easy programmatic access to setting dash-separated52 * style properties.  An example is setting a batch of properties from a data53 * object without overwriting old styles.  When possible, use native APIs:54 * elem.style.propertyKey = 'value' or (if obliterating old styles is fine)55 * elem.style.cssText = 'property1: value1; property2: value2'.56 *57 * @param {Element} element The element to change.58 * @param {string|Object} style If a string, a style name. If an object, a hash59 *     of style names to style values.60 * @param {string|number|boolean=} opt_value If style was a string, then this61 *     should be the value.62 */63goog.style.setStyle = function(element, style, opt_value) {64  if (goog.isString(style)) {65    goog.style.setStyle_(element, opt_value, style);66  } else {67    for (var key in style) {68      goog.style.setStyle_(element, style[key], key);69    }70  }71};727374/**75 * Sets a style value on an element, with parameters swapped to work with76 * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when77 * necessary.78 * @param {Element} element The element to change.79 * @param {string|number|boolean|undefined} value Style value.80 * @param {string} style Style name.81 * @private82 */83goog.style.setStyle_ = function(element, value, style) {84  var propertyName = goog.style.getVendorJsStyleName_(element, style);8586  if (propertyName) {87    element.style[propertyName] = value;88  }89};909192/**93 * Returns the style property name in camel-case. If it does not exist and a94 * vendor-specific version of the property does exist, then return the vendor-95 * specific property name instead.96 * @param {Element} element The element to change.97 * @param {string} style Style name.98 * @return {string} Vendor-specific style.99 * @private100 */101goog.style.getVendorJsStyleName_ = function(element, style) {102  var camelStyle = goog.string.toCamelCase(style);103104  if (element.style[camelStyle] === undefined) {105    var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +106        goog.string.toTitleCase(camelStyle);107108    if (element.style[prefixedStyle] !== undefined) {109      return prefixedStyle;110    }111  }112113  return camelStyle;114};115116117/**118 * Returns the style property name in CSS notation. If it does not exist and a119 * vendor-specific version of the property does exist, then return the vendor-120 * specific property name instead.121 * @param {Element} element The element to change.122 * @param {string} style Style name.123 * @return {string} Vendor-specific style.124 * @private125 */126goog.style.getVendorStyleName_ = function(element, style) {127  var camelStyle = goog.string.toCamelCase(style);128129  if (element.style[camelStyle] === undefined) {130    var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +131        goog.string.toTitleCase(camelStyle);132133    if (element.style[prefixedStyle] !== undefined) {134      return goog.dom.vendor.getVendorPrefix() + '-' + style;135    }136  }137138  return style;139};140141142/**143 * Retrieves an explicitly-set style value of a node. This returns '' if there144 * isn't a style attribute on the element or if this style property has not been145 * explicitly set in script.146 *147 * @param {Element} element Element to get style of.148 * @param {string} property Property to get, css-style (if you have a camel-case149 * property, use element.style[style]).150 * @return {string} Style value.151 */152goog.style.getStyle = function(element, property) {153  // element.style is '' for well-known properties which are unset.154  // For for browser specific styles as 'filter' is undefined155  // so we need to return '' explicitly to make it consistent across156  // browsers.157  var styleValue = element.style[goog.string.toCamelCase(property)];158159  // Using typeof here because of a bug in Safari 5.1, where this value160  // was undefined, but === undefined returned false.161  if (typeof(styleValue) !== 'undefined') {162    return styleValue;163  }164165  return element.style[goog.style.getVendorJsStyleName_(element, property)] ||166      '';167};168169170/**171 * Retrieves a computed style value of a node. It returns empty string if the172 * value cannot be computed (which will be the case in Internet Explorer) or173 * "none" if the property requested is an SVG one and it has not been174 * explicitly set (firefox and webkit).175 *176 * @param {Element} element Element to get style of.177 * @param {string} property Property to get (camel-case).178 * @return {string} Style value.179 */180goog.style.getComputedStyle = function(element, property) {181  var doc = goog.dom.getOwnerDocument(element);182  if (doc.defaultView && doc.defaultView.getComputedStyle) {183    var styles = doc.defaultView.getComputedStyle(element, null);184    if (styles) {185      // element.style[..] is undefined for browser specific styles186      // as 'filter'.187      return styles[property] || styles.getPropertyValue(property) || '';188    }189  }190191  return '';192};193194195/**196 * Gets the cascaded style value of a node, or null if the value cannot be197 * computed (only Internet Explorer can do this).198 *199 * @param {Element} element Element to get style of.200 * @param {string} style Property to get (camel-case).201 * @return {string} Style value.202 */203goog.style.getCascadedStyle = function(element, style) {204  // TODO(nicksantos): This should be documented to return null. #fixTypes205  return element.currentStyle ? element.currentStyle[style] : null;206};207208209/**210 * Cross-browser pseudo get computed style. It returns the computed style where211 * available. If not available it tries the cascaded style value (IE212 * currentStyle) and in worst case the inline style value.  It shouldn't be213 * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for214 * discussion.215 *216 * @param {Element} element Element to get style of.217 * @param {string} style Property to get (must be camelCase, not css-style.).218 * @return {string} Style value.219 * @private220 */221goog.style.getStyle_ = function(element, style) {222  return goog.style.getComputedStyle(element, style) ||223         goog.style.getCascadedStyle(element, style) ||224         (element.style && element.style[style]);225};226227228/**229 * Retrieves the computed value of the box-sizing CSS attribute.230 * Browser support: http://caniuse.com/css3-boxsizing.231 * @param {!Element} element The element whose box-sizing to get.232 * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if233 *     box-sizing is not supported (IE7 and below).234 */235goog.style.getComputedBoxSizing = function(element) {236  return goog.style.getStyle_(element, 'boxSizing') ||237      goog.style.getStyle_(element, 'MozBoxSizing') ||238      goog.style.getStyle_(element, 'WebkitBoxSizing') || null;239};240241242/**243 * Retrieves the computed value of the position CSS attribute.244 * @param {Element} element The element to get the position of.245 * @return {string} Position value.246 */247goog.style.getComputedPosition = function(element) {248  return goog.style.getStyle_(element, 'position');249};250251252/**253 * Retrieves the computed background color string for a given element. The254 * string returned is suitable for assigning to another element's255 * background-color, but is not guaranteed to be in any particular string256 * format. Accessing the color in a numeric form may not be possible in all257 * browsers or with all input.258 *259 * If the background color for the element is defined as a hexadecimal value,260 * the resulting string can be parsed by goog.color.parse in all supported261 * browsers.262 *263 * Whether named colors like "red" or "lightblue" get translated into a264 * format which can be parsed is browser dependent. Calling this function on265 * transparent elements will return "transparent" in most browsers or266 * "rgba(0, 0, 0, 0)" in WebKit.267 * @param {Element} element The element to get the background color of.268 * @return {string} The computed string value of the background color.269 */270goog.style.getBackgroundColor = function(element) {271  return goog.style.getStyle_(element, 'backgroundColor');272};273274275/**276 * Retrieves the computed value of the overflow-x CSS attribute.277 * @param {Element} element The element to get the overflow-x of.278 * @return {string} The computed string value of the overflow-x attribute.279 */280goog.style.getComputedOverflowX = function(element) {281  return goog.style.getStyle_(element, 'overflowX');282};283284285/**286 * Retrieves the computed value of the overflow-y CSS attribute.287 * @param {Element} element The element to get the overflow-y of.288 * @return {string} The computed string value of the overflow-y attribute.289 */290goog.style.getComputedOverflowY = function(element) {291  return goog.style.getStyle_(element, 'overflowY');292};293294295/**296 * Retrieves the computed value of the z-index CSS attribute.297 * @param {Element} element The element to get the z-index of.298 * @return {string|number} The computed value of the z-index attribute.299 */300goog.style.getComputedZIndex = function(element) {301  return goog.style.getStyle_(element, 'zIndex');302};303304305/**306 * Retrieves the computed value of the text-align CSS attribute.307 * @param {Element} element The element to get the text-align of.308 * @return {string} The computed string value of the text-align attribute.309 */310goog.style.getComputedTextAlign = function(element) {311  return goog.style.getStyle_(element, 'textAlign');312};313314315/**316 * Retrieves the computed value of the cursor CSS attribute.317 * @param {Element} element The element to get the cursor of.318 * @return {string} The computed string value of the cursor attribute.319 */320goog.style.getComputedCursor = function(element) {321  return goog.style.getStyle_(element, 'cursor');322};323324325/**326 * Retrieves the computed value of the CSS transform attribute.327 * @param {Element} element The element to get the transform of.328 * @return {string} The computed string representation of the transform matrix.329 */330goog.style.getComputedTransform = function(element) {331  var property = goog.style.getVendorStyleName_(element, 'transform');332  return goog.style.getStyle_(element, property) ||333      goog.style.getStyle_(element, 'transform');334};335336337/**338 * Sets the top/left values of an element.  If no unit is specified in the339 * argument then it will add px. The second argument is required if the first340 * argument is a string or number and is ignored if the first argument341 * is a coordinate.342 * @param {Element} el Element to move.343 * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.344 * @param {string|number=} opt_arg2 Top position.345 */346goog.style.setPosition = function(el, arg1, opt_arg2) {347  var x, y;348  var buggyGeckoSubPixelPos = goog.userAgent.GECKO &&349      (goog.userAgent.MAC || goog.userAgent.X11) &&350      goog.userAgent.isVersionOrHigher('1.9');351352  if (arg1 instanceof goog.math.Coordinate) {353    x = arg1.x;354    y = arg1.y;355  } else {356    x = arg1;357    y = opt_arg2;358  }359360  // Round to the nearest pixel for buggy sub-pixel support.361  el.style.left = goog.style.getPixelStyleValue_(362      /** @type {number|string} */ (x), buggyGeckoSubPixelPos);363  el.style.top = goog.style.getPixelStyleValue_(364      /** @type {number|string} */ (y), buggyGeckoSubPixelPos);365};366367368/**369 * Gets the offsetLeft and offsetTop properties of an element and returns them370 * in a Coordinate object371 * @param {Element} element Element.372 * @return {!goog.math.Coordinate} The position.373 */374goog.style.getPosition = function(element) {375  return new goog.math.Coordinate(element.offsetLeft, element.offsetTop);376};377378379/**380 * Returns the viewport element for a particular document381 * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element382 *     of.383 * @return {Element} document.documentElement or document.body.384 */385goog.style.getClientViewportElement = function(opt_node) {386  var doc;387  if (opt_node) {388    doc = goog.dom.getOwnerDocument(opt_node);389  } else {390    doc = goog.dom.getDocument();391  }392393  // In old IE versions the document.body represented the viewport394  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&395      !goog.dom.getDomHelper(doc).isCss1CompatMode()) {396    return doc.body;397  }398  return doc.documentElement;399};400401402/**403 * Calculates the viewport coordinates relative to the page/document404 * containing the node. The viewport may be the browser viewport for405 * non-iframe document, or the iframe container for iframe'd document.406 * @param {!Document} doc The document to use as the reference point.407 * @return {!goog.math.Coordinate} The page offset of the viewport.408 */409goog.style.getViewportPageOffset = function(doc) {410  var body = doc.body;411  var documentElement = doc.documentElement;412  var scrollLeft = body.scrollLeft || documentElement.scrollLeft;413  var scrollTop = body.scrollTop || documentElement.scrollTop;414  return new goog.math.Coordinate(scrollLeft, scrollTop);415};416417418/**419 * Gets the client rectangle of the DOM element.420 *421 * getBoundingClientRect is part of a new CSS object model draft (with a422 * long-time presence in IE), replacing the error-prone parent offset423 * computation and the now-deprecated Gecko getBoxObjectFor.424 *425 * This utility patches common browser bugs in getBoundingClientRect. It426 * will fail if getBoundingClientRect is unsupported.427 *428 * If the element is not in the DOM, the result is undefined, and an error may429 * be thrown depending on user agent.430 *431 * @param {!Element} el The element whose bounding rectangle is being queried.432 * @return {Object} A native bounding rectangle with numerical left, top,433 *     right, and bottom.  Reported by Firefox to be of object type ClientRect.434 * @private435 */436goog.style.getBoundingClientRect_ = function(el) {437  var rect;438  try {439    rect = el.getBoundingClientRect();440  } catch (e) {441    // In IE < 9, calling getBoundingClientRect on an orphan element raises an442    // "Unspecified Error". All other browsers return zeros.443    return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};444  }445446  // Patch the result in IE only, so that this function can be inlined if447  // compiled for non-IE.448  if (goog.userAgent.IE && el.ownerDocument.body) {449450    // In IE, most of the time, 2 extra pixels are added to the top and left451    // due to the implicit 2-pixel inset border.  In IE6/7 quirks mode and452    // IE6 standards mode, this border can be overridden by setting the453    // document element's border to zero -- thus, we cannot rely on the454    // offset always being 2 pixels.455456    // In quirks mode, the offset can be determined by querying the body's457    // clientLeft/clientTop, but in standards mode, it is found by querying458    // the document element's clientLeft/clientTop.  Since we already called459    // getBoundingClientRect we have already forced a reflow, so it is not460    // too expensive just to query them all.461462    // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx463    var doc = el.ownerDocument;464    rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;465    rect.top -= doc.documentElement.clientTop + doc.body.clientTop;466  }467  return /** @type {Object} */ (rect);468};469470471/**472 * Returns the first parent that could affect the position of a given element.473 * @param {Element} element The element to get the offset parent for.474 * @return {Element} The first offset parent or null if one cannot be found.475 */476goog.style.getOffsetParent = function(element) {477  // element.offsetParent does the right thing in IE7 and below.  In other478  // browsers it only includes elements with position absolute, relative or479  // fixed, not elements with overflow set to auto or scroll.480  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {481    return element.offsetParent;482  }483484  var doc = goog.dom.getOwnerDocument(element);485  var positionStyle = goog.style.getStyle_(element, 'position');486  var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';487  for (var parent = element.parentNode; parent && parent != doc;488       parent = parent.parentNode) {489    positionStyle =490        goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');491    skipStatic = skipStatic && positionStyle == 'static' &&492                 parent != doc.documentElement && parent != doc.body;493    if (!skipStatic && (parent.scrollWidth > parent.clientWidth ||494                        parent.scrollHeight > parent.clientHeight ||495                        positionStyle == 'fixed' ||496                        positionStyle == 'absolute' ||497                        positionStyle == 'relative')) {498      return /** @type {!Element} */ (parent);499    }500  }501  return null;502};503504505/**506 * Calculates and returns the visible rectangle for a given element. Returns a507 * box describing the visible portion of the nearest scrollable offset ancestor.508 * Coordinates are given relative to the document.509 *510 * @param {Element} element Element to get the visible rect for.511 * @return {goog.math.Box} Bounding elementBox describing the visible rect or512 *     null if scrollable ancestor isn't inside the visible viewport.513 */514goog.style.getVisibleRectForElement = function(element) {515  var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);516  var dom = goog.dom.getDomHelper(element);517  var body = dom.getDocument().body;518  var documentElement = dom.getDocument().documentElement;519  var scrollEl = dom.getDocumentScrollElement();520521  // Determine the size of the visible rect by climbing the dom accounting for522  // all scrollable containers.523  for (var el = element; el = goog.style.getOffsetParent(el); ) {524    // clientWidth is zero for inline block elements in IE.525    // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0526    if ((!goog.userAgent.IE || el.clientWidth != 0) &&527        (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&528        // body may have overflow set on it, yet we still get the entire529        // viewport. In some browsers, el.offsetParent may be530        // document.documentElement, so check for that too.531        (el != body && el != documentElement &&532            goog.style.getStyle_(el, 'overflow') != 'visible')) {533      var pos = goog.style.getPageOffset(el);534      var client = goog.style.getClientLeftTop(el);535      pos.x += client.x;536      pos.y += client.y;537538      visibleRect.top = Math.max(visibleRect.top, pos.y);539      visibleRect.right = Math.min(visibleRect.right,540                                   pos.x + el.clientWidth);541      visibleRect.bottom = Math.min(visibleRect.bottom,542                                    pos.y + el.clientHeight);543      visibleRect.left = Math.max(visibleRect.left, pos.x);544    }545  }546547  // Clip by window's viewport.548  var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;549  visibleRect.left = Math.max(visibleRect.left, scrollX);550  visibleRect.top = Math.max(visibleRect.top, scrollY);551  var winSize = dom.getViewportSize();552  visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);553  visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);554  return visibleRect.top >= 0 && visibleRect.left >= 0 &&555         visibleRect.bottom > visibleRect.top &&556         visibleRect.right > visibleRect.left ?557         visibleRect : null;558};559560561/**562 * Calculate the scroll position of {@code container} with the minimum amount so563 * that the content and the borders of the given {@code element} become visible.564 * If the element is bigger than the container, its top left corner will be565 * aligned as close to the container's top left corner as possible.566 *567 * @param {Element} element The element to make visible.568 * @param {Element} container The container to scroll.569 * @param {boolean=} opt_center Whether to center the element in the container.570 *     Defaults to false.571 * @return {!goog.math.Coordinate} The new scroll position of the container,572 *     in form of goog.math.Coordinate(scrollLeft, scrollTop).573 */574goog.style.getContainerOffsetToScrollInto =575    function(element, container, opt_center) {576  // Absolute position of the element's border's top left corner.577  var elementPos = goog.style.getPageOffset(element);578  // Absolute position of the container's border's top left corner.579  var containerPos = goog.style.getPageOffset(container);580  var containerBorder = goog.style.getBorderBox(container);581  // Relative pos. of the element's border box to the container's content box.582  var relX = elementPos.x - containerPos.x - containerBorder.left;583  var relY = elementPos.y - containerPos.y - containerBorder.top;584  // How much the element can move in the container, i.e. the difference between585  // the element's bottom-right-most and top-left-most position where it's586  // fully visible.587  var spaceX = container.clientWidth - element.offsetWidth;588  var spaceY = container.clientHeight - element.offsetHeight;589590  var scrollLeft = container.scrollLeft;591  var scrollTop = container.scrollTop;592  if (opt_center) {593    // All browsers round non-integer scroll positions down.594    scrollLeft += relX - spaceX / 2;595    scrollTop += relY - spaceY / 2;596  } else {597    // This formula was designed to give the correct scroll values in the598    // following cases:599    // - element is higher than container (spaceY < 0) => scroll down by relY600    // - element is not higher that container (spaceY >= 0):601    //   - it is above container (relY < 0) => scroll up by abs(relY)602    //   - it is below container (relY > spaceY) => scroll down by relY - spaceY603    //   - it is in the container => don't scroll604    scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));605    scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));606  }607  return new goog.math.Coordinate(scrollLeft, scrollTop);608};609610611/**612 * Changes the scroll position of {@code container} with the minimum amount so613 * that the content and the borders of the given {@code element} become visible.614 * If the element is bigger than the container, its top left corner will be615 * aligned as close to the container's top left corner as possible.616 *617 * @param {Element} element The element to make visible.618 * @param {Element} container The container to scroll.619 * @param {boolean=} opt_center Whether to center the element in the container.620 *     Defaults to false.621 */622goog.style.scrollIntoContainerView = function(element, container, opt_center) {623  var offset =624      goog.style.getContainerOffsetToScrollInto(element, container, opt_center);625  container.scrollLeft = offset.x;626  container.scrollTop = offset.y;627};628629630/**631 * Returns clientLeft (width of the left border and, if the directionality is632 * right to left, the vertical scrollbar) and clientTop as a coordinate object.633 *634 * @param {Element} el Element to get clientLeft for.635 * @return {!goog.math.Coordinate} Client left and top.636 */637goog.style.getClientLeftTop = function(el) {638  // NOTE(eae): Gecko prior to 1.9 doesn't support clientTop/Left, see639  // https://bugzilla.mozilla.org/show_bug.cgi?id=111207640  if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('1.9')) {641    var left = parseFloat(goog.style.getComputedStyle(el, 'borderLeftWidth'));642    if (goog.style.isRightToLeft(el)) {643      var scrollbarWidth = el.offsetWidth - el.clientWidth - left -644          parseFloat(goog.style.getComputedStyle(el, 'borderRightWidth'));645      left += scrollbarWidth;646    }647    return new goog.math.Coordinate(left,648        parseFloat(goog.style.getComputedStyle(el, 'borderTopWidth')));649  }650651  return new goog.math.Coordinate(el.clientLeft, el.clientTop);652};653654655/**656 * Returns a Coordinate object relative to the top-left of the HTML document.657 * Implemented as a single function to save having to do two recursive loops in658 * opera and safari just to get both coordinates.  If you just want one value do659 * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but660 * note if you call both those methods the tree will be analysed twice.661 *662 * @param {Element} el Element to get the page offset for.663 * @return {!goog.math.Coordinate} The page offset.664 */665goog.style.getPageOffset = function(el) {666  var box, doc = goog.dom.getOwnerDocument(el);667  var positionStyle = goog.style.getStyle_(el, 'position');668  // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.669  goog.asserts.assertObject(el, 'Parameter is required');670671  // NOTE(eae): Gecko pre 1.9 normally use getBoxObjectFor to calculate the672  // position. When invoked for an element with position absolute and a negative673  // position though it can be off by one. Therefor the recursive implementation674  // is used in those (relatively rare) cases.675  var BUGGY_GECKO_BOX_OBJECT =676      !goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS &&677      goog.userAgent.GECKO && doc.getBoxObjectFor &&678      !el.getBoundingClientRect && positionStyle == 'absolute' &&679      (box = doc.getBoxObjectFor(el)) && (box.screenX < 0 || box.screenY < 0);680681  // NOTE(arv): If element is hidden (display none or disconnected or any the682  // ancestors are hidden) we get (0,0) by default but we still do the683  // accumulation of scroll position.684685  // TODO(arv): Should we check if the node is disconnected and in that case686  //            return (0,0)?687688  var pos = new goog.math.Coordinate(0, 0);689  var viewportElement = goog.style.getClientViewportElement(doc);690  if (el == viewportElement) {691    // viewport is always at 0,0 as that defined the coordinate system for this692    // function - this avoids special case checks in the code below693    return pos;694  }695696  // IE, Gecko 1.9+, and most modern WebKit.697  if (goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS ||698      el.getBoundingClientRect) {699    box = goog.style.getBoundingClientRect_(el);700    // Must add the scroll coordinates in to get the absolute page offset701    // of element since getBoundingClientRect returns relative coordinates to702    // the viewport.703    var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();704    pos.x = box.left + scrollCoord.x;705    pos.y = box.top + scrollCoord.y;706707  // Gecko prior to 1.9.708  } else if (doc.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) {709    // Gecko ignores the scroll values for ancestors, up to 1.9.  See:710    // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and711    // https://bugzilla.mozilla.org/show_bug.cgi?id=330619712713    box = doc.getBoxObjectFor(el);714    // TODO(user): Fix the off-by-one error when window is scrolled down715    // or right more than 1 pixel. The viewport offset does not move in lock716    // step with the window scroll; it moves in increments of 2px and at717    // somewhat random intervals.718    var vpBox = doc.getBoxObjectFor(viewportElement);719    pos.x = box.screenX - vpBox.screenX;720    pos.y = box.screenY - vpBox.screenY;721722  // Safari, Opera and Camino up to 1.0.4.723  } else {724    var parent = el;725    do {726      pos.x += parent.offsetLeft;727      pos.y += parent.offsetTop;728      // For safari/chrome, we need to add parent's clientLeft/Top as well.729      if (parent != el) {730        pos.x += parent.clientLeft || 0;731        pos.y += parent.clientTop || 0;732      }733      // In Safari when hit a position fixed element the rest of the offsets734      // are not correct.735      if (goog.userAgent.WEBKIT &&736          goog.style.getComputedPosition(parent) == 'fixed') {737        pos.x += doc.body.scrollLeft;738        pos.y += doc.body.scrollTop;739        break;740      }741      parent = parent.offsetParent;742    } while (parent && parent != el);743744    // Opera & (safari absolute) incorrectly account for body offsetTop.745    if (goog.userAgent.OPERA || (goog.userAgent.WEBKIT &&746        positionStyle == 'absolute')) {747      pos.y -= doc.body.offsetTop;748    }749750    for (parent = el; (parent = goog.style.getOffsetParent(parent)) &&751        parent != doc.body && parent != viewportElement; ) {752      pos.x -= parent.scrollLeft;753      // Workaround for a bug in Opera 9.2 (and earlier) where table rows may754      // report an invalid scroll top value. The bug was fixed in Opera 9.5755      // however as that version supports getBoundingClientRect it won't756      // trigger this code path. https://bugs.opera.com/show_bug.cgi?id=249965757      if (!goog.userAgent.OPERA || parent.tagName != 'TR') {758        pos.y -= parent.scrollTop;759      }760    }761  }762763  return pos;764};765766767/**768 * Returns the left coordinate of an element relative to the HTML document769 * @param {Element} el Elements.770 * @return {number} The left coordinate.771 */772goog.style.getPageOffsetLeft = function(el) {773  return goog.style.getPageOffset(el).x;774};775776777/**778 * Returns the top coordinate of an element relative to the HTML document779 * @param {Element} el Elements.780 * @return {number} The top coordinate.781 */782goog.style.getPageOffsetTop = function(el) {783  return goog.style.getPageOffset(el).y;784};785786787/**788 * Returns a Coordinate object relative to the top-left of an HTML document789 * in an ancestor frame of this element. Used for measuring the position of790 * an element inside a frame relative to a containing frame.791 *792 * @param {Element} el Element to get the page offset for.793 * @param {Window} relativeWin The window to measure relative to. If relativeWin794 *     is not in the ancestor frame chain of the element, we measure relative to795 *     the top-most window.796 * @return {!goog.math.Coordinate} The page offset.797 */798goog.style.getFramedPageOffset = function(el, relativeWin) {799  var position = new goog.math.Coordinate(0, 0);800801  // Iterate up the ancestor frame chain, keeping track of the current window802  // and the current element in that window.803  var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));804  var currentEl = el;805  do {806    // if we're at the top window, we want to get the page offset.807    // if we're at an inner frame, we only want to get the window position808    // so that we can determine the actual page offset in the context of809    // the outer window.810    var offset = currentWin == relativeWin ?811        goog.style.getPageOffset(currentEl) :812        goog.style.getClientPositionForElement_(813            goog.asserts.assert(currentEl));814815    position.x += offset.x;816    position.y += offset.y;817  } while (currentWin && currentWin != relativeWin &&818      (currentEl = currentWin.frameElement) &&819      (currentWin = currentWin.parent));820821  return position;822};823824825/**826 * Translates the specified rect relative to origBase page, for newBase page.827 * If origBase and newBase are the same, this function does nothing.828 *829 * @param {goog.math.Rect} rect The source rectangle relative to origBase page,830 *     and it will have the translated result.831 * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.832 * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant833 *     coordinate.  This must be a DOM for an ancestor frame of origBase834 *     or the same as origBase.835 */836goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {837  if (origBase.getDocument() != newBase.getDocument()) {838    var body = origBase.getDocument().body;839    var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());840841    // Adjust Body's margin.842    pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));843844    if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&845        !origBase.isCss1CompatMode()) {846      pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());847    }848849    rect.left += pos.x;850    rect.top += pos.y;851  }852};853854855/**856 * Returns the position of an element relative to another element in the857 * document.  A relative to B858 * @param {Element|Event|goog.events.Event} a Element or mouse event whose859 *     position we're calculating.860 * @param {Element|Event|goog.events.Event} b Element or mouse event position861 *     is relative to.862 * @return {!goog.math.Coordinate} The relative position.863 */864goog.style.getRelativePosition = function(a, b) {865  var ap = goog.style.getClientPosition(a);866  var bp = goog.style.getClientPosition(b);867  return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);868};869870871/**872 * Returns the position of the event or the element's border box relative to873 * the client viewport.874 * @param {!Element} el Element whose position to get.875 * @return {!goog.math.Coordinate} The position.876 * @private877 */878goog.style.getClientPositionForElement_ = function(el) {879  var pos;880  if (goog.style.GET_BOUNDING_CLIENT_RECT_ALWAYS_EXISTS ||881      el.getBoundingClientRect) {882    // IE, Gecko 1.9+, and most modern WebKit883    var box = goog.style.getBoundingClientRect_(el);884    pos = new goog.math.Coordinate(box.left, box.top);885  } else {886    var scrollCoord = goog.dom.getDomHelper(el).getDocumentScroll();887    var pageCoord = goog.style.getPageOffset(el);888    pos = new goog.math.Coordinate(889        pageCoord.x - scrollCoord.x,890        pageCoord.y - scrollCoord.y);891  }892893  // Gecko below version 12 doesn't add CSS translation to the client position894  // (using either getBoundingClientRect or getBoxOffsetFor) so we need to do895  // so manually.896  if (goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher(12)) {897    return goog.math.Coordinate.sum(pos, goog.style.getCssTranslation(el));898  } else {899    return pos;900  }901};902903904/**905 * Returns the position of the event or the element's border box relative to906 * the client viewport.907 * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.908 * @return {!goog.math.Coordinate} The position.909 */910goog.style.getClientPosition = function(el) {911  goog.asserts.assert(el);912  if (el.nodeType == goog.dom.NodeType.ELEMENT) {913    return goog.style.getClientPositionForElement_(914        /** @type {!Element} */ (el));915  } else {916    var isAbstractedEvent = goog.isFunction(el.getBrowserEvent);917    var be = /** @type {!goog.events.BrowserEvent} */ (el);918    var targetEvent = el;919920    if (el.targetTouches && el.targetTouches.length) {921      targetEvent = el.targetTouches[0];922    } else if (isAbstractedEvent && be.getBrowserEvent().targetTouches &&923        be.getBrowserEvent().targetTouches.length) {924      targetEvent = be.getBrowserEvent().targetTouches[0];925    }926927    return new goog.math.Coordinate(928        targetEvent.clientX,929        targetEvent.clientY);930  }931};932933934/**935 * Moves an element to the given coordinates relative to the client viewport.936 * @param {Element} el Absolutely positioned element to set page offset for.937 *     It must be in the document.938 * @param {number|goog.math.Coordinate} x Left position of the element's margin939 *     box or a coordinate object.940 * @param {number=} opt_y Top position of the element's margin box.941 */942goog.style.setPageOffset = function(el, x, opt_y) {943  // Get current pageoffset944  var cur = goog.style.getPageOffset(el);945946  if (x instanceof goog.math.Coordinate) {947    opt_y = x.y;948    x = x.x;949  }950951  // NOTE(arv): We cannot allow strings for x and y. We could but that would952  // require us to manually transform between different units953954  // Work out deltas955  var dx = x - cur.x;956  var dy = opt_y - cur.y;957958  // Set position to current left/top + delta959  goog.style.setPosition(el, el.offsetLeft + dx, el.offsetTop + dy);960};961962963/**964 * Sets the width/height values of an element.  If an argument is numeric,965 * or a goog.math.Size is passed, it is assumed to be pixels and will add966 * 'px' after converting it to an integer in string form. (This just sets the967 * CSS width and height properties so it might set content-box or border-box968 * size depending on the box model the browser is using.)969 *970 * @param {Element} element Element to set the size of.971 * @param {string|number|goog.math.Size} w Width of the element, or a972 *     size object.973 * @param {string|number=} opt_h Height of the element. Required if w is not a974 *     size object.975 */976goog.style.setSize = function(element, w, opt_h) {977  var h;978  if (w instanceof goog.math.Size) {979    h = w.height;980    w = w.width;981  } else {982    if (opt_h == undefined) {983      throw Error('missing height argument');984    }985    h = opt_h;986  }987988  goog.style.setWidth(element, /** @type {string|number} */ (w));989  goog.style.setHeight(element, /** @type {string|number} */ (h));990};991992993/**994 * Helper function to create a string to be set into a pixel-value style995 * property of an element. Can round to the nearest integer value.996 *997 * @param {string|number} value The style value to be used. If a number,998 *     'px' will be appended, otherwise the value will be applied directly.999 * @param {boolean} round Whether to round the nearest integer (if property1000 *     is a number).1001 * @return {string} The string value for the property.1002 * @private1003 */1004goog.style.getPixelStyleValue_ = function(value, round) {1005  if (typeof value == 'number') {1006    value = (round ? Math.round(value) : value) + 'px';1007  }10081009  return value;1010};101110121013/**1014 * Set the height of an element.  Sets the element's style property.1015 * @param {Element} element Element to set the height of.1016 * @param {string|number} height The height value to set.  If a number, 'px'1017 *     will be appended, otherwise the value will be applied directly.1018 */1019goog.style.setHeight = function(element, height) {1020  element.style.height = goog.style.getPixelStyleValue_(height, true);1021};102210231024/**1025 * Set the width of an element.  Sets the element's style property.1026 * @param {Element} element Element to set the width of.1027 * @param {string|number} width The width value to set.  If a number, 'px'1028 *     will be appended, otherwise the value will be applied directly.1029 */1030goog.style.setWidth = function(element, width) {1031  element.style.width = goog.style.getPixelStyleValue_(width, true);1032};103310341035/**1036 * Gets the height and width of an element, even if its display is none.1037 *1038 * Specifically, this returns the height and width of the border box,1039 * irrespective of the box model in effect.1040 *1041 * Note that this function does not take CSS transforms into account. Please see1042 * {@code goog.style.getTransformedSize}.1043 * @param {Element} element Element to get size of.1044 * @return {!goog.math.Size} Object with width/height properties.1045 */1046goog.style.getSize = function(element) {1047  return goog.style.evaluateWithTemporaryDisplay_(1048      goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));1049};105010511052/**1053 * Call {@code fn} on {@code element} such that {@code element}'s dimensions are1054 * accurate when it's passed to {@code fn}.1055 * @param {function(!Element): T} fn Function to call with {@code element} as1056 *     an argument after temporarily changing {@code element}'s display such1057 *     that its dimensions are accurate.1058 * @param {!Element} element Element (which may have display none) to use as1059 *     argument to {@code fn}.1060 * @return {T} Value returned by calling {@code fn} with {@code element}.1061 * @template T1062 * @private1063 */1064goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {1065  if (goog.style.getStyle_(element, 'display') != 'none') {1066    return fn(element);1067  }10681069  var style = element.style;1070  var originalDisplay = style.display;1071  var originalVisibility = style.visibility;1072  var originalPosition = style.position;10731074  style.visibility = 'hidden';1075  style.position = 'absolute';1076  style.display = 'inline';10771078  var retVal = fn(element);10791080  style.display = originalDisplay;1081  style.position = originalPosition;1082  style.visibility = originalVisibility;10831084  return retVal;1085};108610871088/**1089 * Gets the height and width of an element when the display is not none.1090 * @param {Element} element Element to get size of.1091 * @return {!goog.math.Size} Object with width/height properties.1092 * @private1093 */1094goog.style.getSizeWithDisplay_ = function(element) {1095  var offsetWidth = element.offsetWidth;1096  var offsetHeight = element.offsetHeight;1097  var webkitOffsetsZero =1098      goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;1099  if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) &&1100      element.getBoundingClientRect) {1101    // Fall back to calling getBoundingClientRect when offsetWidth or1102    // offsetHeight are not defined, or when they are zero in WebKit browsers.1103    // This makes sure that we return for the correct size for SVG elements, but1104    // will still return 0 on Webkit prior to 534.8, see1105    // http://trac.webkit.org/changeset/67252.1106    var clientRect = goog.style.getBoundingClientRect_(element);1107    return new goog.math.Size(clientRect.right - clientRect.left,1108        clientRect.bottom - clientRect.top);1109  }1110  return new goog.math.Size(offsetWidth, offsetHeight);1111};111211131114/**1115 * Gets the height and width of an element, post transform, even if its display1116 * is none.1117 *1118 * This is like {@code goog.style.getSize}, except:1119 * <ol>1120 * <li>Takes webkitTransforms such as rotate and scale into account.1121 * <li>Will return null if {@code element} doesn't respond to1122 *     {@code getBoundingClientRect}.1123 * <li>Currently doesn't make sense on non-WebKit browsers which don't support1124 *    webkitTransforms.1125 * </ol>1126 * @param {!Element} element Element to get size of.1127 * @return {goog.math.Size} Object with width/height properties.1128 */1129goog.style.getTransformedSize = function(element) {1130  if (!element.getBoundingClientRect) {1131    return null;1132  }11331134  var clientRect = goog.style.evaluateWithTemporaryDisplay_(1135      goog.style.getBoundingClientRect_, element);1136  return new goog.math.Size(clientRect.right - clientRect.left,1137      clientRect.bottom - clientRect.top);1138};113911401141/**1142 * Returns a bounding rectangle for a given element in page space.1143 * @param {Element} element Element to get bounds of. Must not be display none.1144 * @return {!goog.math.Rect} Bounding rectangle for the element.1145 */1146goog.style.getBounds = function(element) {1147  var o = goog.style.getPageOffset(element);1148  var s = goog.style.getSize(element);1149  return new goog.math.Rect(o.x, o.y, s.width, s.height);1150};115111521153/**1154 * Converts a CSS selector in the form style-property to styleProperty.1155 * @param {*} selector CSS Selector.1156 * @return {string} Camel case selector.1157 * @deprecated Use goog.string.toCamelCase instead.1158 */1159goog.style.toCamelCase = function(selector) {1160  return goog.string.toCamelCase(String(selector));1161};116211631164/**1165 * Converts a CSS selector in the form styleProperty to style-property.1166 * @param {string} selector Camel case selector.1167 * @return {string} Selector cased.1168 * @deprecated Use goog.string.toSelectorCase instead.1169 */1170goog.style.toSelectorCase = function(selector) {1171  return goog.string.toSelectorCase(selector);1172};117311741175/**1176 * Gets the opacity of a node (x-browser). This gets the inline style opacity1177 * of the node, and does not take into account the cascaded or the computed1178 * style for this node.1179 * @param {Element} el Element whose opacity has to be found.1180 * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}1181 *     if the opacity is not set.1182 */1183goog.style.getOpacity = function(el) {1184  var style = el.style;1185  var result = '';1186  if ('opacity' in style) {1187    result = style.opacity;1188  } else if ('MozOpacity' in style) {1189    result = style.MozOpacity;1190  } else if ('filter' in style) {1191    var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);1192    if (match) {1193      result = String(match[1] / 100);1194    }1195  }1196  return result == '' ? result : Number(result);1197};119811991200/**1201 * Sets the opacity of a node (x-browser).1202 * @param {Element} el Elements whose opacity has to be set.1203 * @param {number|string} alpha Opacity between 0 and 1 or an empty string1204 *     {@code ''} to clear the opacity.1205 */1206goog.style.setOpacity = function(el, alpha) {1207  var style = el.style;1208  if ('opacity' in style) {1209    style.opacity = alpha;1210  } else if ('MozOpacity' in style) {1211    style.MozOpacity = alpha;1212  } else if ('filter' in style) {1213    // TODO(arv): Overwriting the filter might have undesired side effects.1214    if (alpha === '') {1215      style.filter = '';1216    } else {1217      style.filter = 'alpha(opacity=' + alpha * 100 + ')';1218    }1219  }1220};122112221223/**1224 * Sets the background of an element to a transparent image in a browser-1225 * independent manner.1226 *1227 * This function does not support repeating backgrounds or alternate background1228 * positions to match the behavior of Internet Explorer. It also does not1229 * support sizingMethods other than crop since they cannot be replicated in1230 * browsers other than Internet Explorer.1231 *1232 * @param {Element} el The element to set background on.1233 * @param {string} src The image source URL.1234 */1235goog.style.setTransparentBackgroundImage = function(el, src) {1236  var style = el.style;1237  // It is safe to use the style.filter in IE only. In Safari 'filter' is in1238  // style object but access to style.filter causes it to throw an exception.1239  // Note: IE8 supports images with an alpha channel.1240  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {1241    // See TODO in setOpacity.1242    style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' +1243        'src="' + src + '", sizingMethod="crop")';1244  } else {1245    // Set style properties individually instead of using background shorthand1246    // to prevent overwriting a pre-existing background color.1247    style.backgroundImage = 'url(' + src + ')';1248    style.backgroundPosition = 'top left';1249    style.backgroundRepeat = 'no-repeat';1250  }1251};125212531254/**1255 * Clears the background image of an element in a browser independent manner.1256 * @param {Element} el The element to clear background image for.1257 */1258goog.style.clearTransparentBackgroundImage = function(el) {1259  var style = el.style;1260  if ('filter' in style) {1261    // See TODO in setOpacity.1262    style.filter = '';1263  } else {1264    // Set style properties individually instead of using background shorthand1265    // to prevent overwriting a pre-existing background color.1266    style.backgroundImage = 'none';1267  }1268};126912701271/**1272 * Shows or hides an element from the page. Hiding the element is done by1273 * setting the display property to "none", removing the element from the1274 * rendering hierarchy so it takes up no space. To show the element, the default1275 * inherited display property is restored (defined either in stylesheets or by1276 * the browser's default style rules.)1277 *1278 * Caveat 1: if the inherited display property for the element is set to "none"1279 * by the stylesheets, that is the property that will be restored by a call to1280 * showElement(), effectively toggling the display between "none" and "none".1281 *1282 * Caveat 2: if the element display style is set inline (by setting either1283 * element.style.display or a style attribute in the HTML), a call to1284 * showElement will clear that setting and defer to the inherited style in the1285 * stylesheet.1286 * @param {Element} el Element to show or hide.1287 * @param {*} display True to render the element in its default style,1288 *     false to disable rendering the element.1289 * @deprecated Use goog.style.setElementShown instead.1290 */1291goog.style.showElement = function(el, display) {1292  goog.style.setElementShown(el, display);1293};129412951296/**1297 * Shows or hides an element from the page. Hiding the element is done by1298 * setting the display property to "none", removing the element from the1299 * rendering hierarchy so it takes up no space. To show the element, the default1300 * inherited display property is restored (defined either in stylesheets or by1301 * the browser's default style rules).1302 *1303 * Caveat 1: if the inherited display property for the element is set to "none"1304 * by the stylesheets, that is the property that will be restored by a call to1305 * setElementShown(), effectively toggling the display between "none" and1306 * "none".1307 *1308 * Caveat 2: if the element display style is set inline (by setting either1309 * element.style.display or a style attribute in the HTML), a call to1310 * setElementShown will clear that setting and defer to the inherited style in1311 * the stylesheet.1312 * @param {Element} el Element to show or hide.1313 * @param {*} isShown True to render the element in its default style,1314 *     false to disable rendering the element.1315 */1316goog.style.setElementShown = function(el, isShown) {1317  el.style.display = isShown ? '' : 'none';1318};131913201321/**1322 * Test whether the given element has been shown or hidden via a call to1323 * {@link #setElementShown}.1324 *1325 * Note this is strictly a companion method for a call1326 * to {@link #setElementShown} and the same caveats apply; in particular, this1327 * method does not guarantee that the return value will be consistent with1328 * whether or not the element is actually visible.1329 *1330 * @param {Element} el The element to test.1331 * @return {boolean} Whether the element has been shown.1332 * @see #setElementShown1333 */1334goog.style.isElementShown = function(el) {1335  return el.style.display != 'none';1336};133713381339/**1340 * Installs the styles string into the window that contains opt_element.  If1341 * opt_element is null, the main window is used.1342 * @param {string} stylesString The style string to install.1343 * @param {Node=} opt_node Node whose parent document should have the1344 *     styles installed.1345 * @return {Element|StyleSheet} The style element created.1346 */1347goog.style.installStyles = function(stylesString, opt_node) {1348  var dh = goog.dom.getDomHelper(opt_node);1349  var styleSheet = null;13501351  // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be1352  // undefined as of IE 11.1353  var doc = dh.getDocument();1354  if (goog.userAgent.IE && doc.createStyleSheet) {1355    styleSheet = doc.createStyleSheet();1356    goog.style.setStyles(styleSheet, stylesString);1357  } else {1358    var head = dh.getElementsByTagNameAndClass('head')[0];13591360    // In opera documents are not guaranteed to have a head element, thus we1361    // have to make sure one exists before using it.1362    if (!head) {1363      var body = dh.getElementsByTagNameAndClass('body')[0];1364      head = dh.createDom('head');1365      body.parentNode.insertBefore(head, body);1366    }1367    styleSheet = dh.createDom('style');1368    // NOTE(user): Setting styles after the style element has been appended1369    // to the head results in a nasty Webkit bug in certain scenarios. Please1370    // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional1371    // details.1372    goog.style.setStyles(styleSheet, stylesString);1373    dh.appendChild(head, styleSheet);1374  }1375  return styleSheet;1376};137713781379/**1380 * Removes the styles added by {@link #installStyles}.1381 * @param {Element|StyleSheet} styleSheet The value returned by1382 *     {@link #installStyles}.1383 */1384goog.style.uninstallStyles = function(styleSheet) {1385  var node = styleSheet.ownerNode || styleSheet.owningElement ||1386      /** @type {Element} */ (styleSheet);1387  goog.dom.removeNode(node);1388};138913901391/**1392 * Sets the content of a style element.  The style element can be any valid1393 * style element.  This element will have its content completely replaced by1394 * the new stylesString.1395 * @param {Element|StyleSheet} element A stylesheet element as returned by1396 *     installStyles.1397 * @param {string} stylesString The new content of the stylesheet.1398 */1399goog.style.setStyles = function(element, stylesString) {1400  if (goog.userAgent.IE && goog.isDef(element.cssText)) {1401    // Adding the selectors individually caused the browser to hang if the1402    // selector was invalid or there were CSS comments.  Setting the cssText of1403    // the style node works fine and ignores CSS that IE doesn't understand.1404    // However IE >= 11 doesn't support cssText any more, so we make sure that1405    // cssText is a defined property and otherwise fall back to innerHTML.1406    element.cssText = stylesString;1407  } else {1408    element.innerHTML = stylesString;1409  }1410};141114121413/**1414 * Sets 'white-space: pre-wrap' for a node (x-browser).1415 *1416 * There are as many ways of specifying pre-wrap as there are browsers.1417 *1418 * CSS3/IE8: white-space: pre-wrap;1419 * Mozilla:  white-space: -moz-pre-wrap;1420 * Opera:    white-space: -o-pre-wrap;1421 * IE6/7:    white-space: pre; word-wrap: break-word;1422 *1423 * @param {Element} el Element to enable pre-wrap for.1424 */1425goog.style.setPreWrap = function(el) {1426  var style = el.style;1427  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {1428    style.whiteSpace = 'pre';1429    style.wordWrap = 'break-word';1430  } else if (goog.userAgent.GECKO) {1431    style.whiteSpace = '-moz-pre-wrap';1432  } else {1433    style.whiteSpace = 'pre-wrap';1434  }1435};143614371438/**1439 * Sets 'display: inline-block' for an element (cross-browser).1440 * @param {Element} el Element to which the inline-block display style is to be1441 *    applied.1442 * @see ../demos/inline_block_quirks.html1443 * @see ../demos/inline_block_standards.html1444 */1445goog.style.setInlineBlock = function(el) {1446  var style = el.style;1447  // Without position:relative, weirdness ensues.  Just accept it and move on.1448  style.position = 'relative';14491450  if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {1451    // IE8 supports inline-block so fall through to the else1452    // Zoom:1 forces hasLayout, display:inline gives inline behavior.1453    style.zoom = '1';1454    style.display = 'inline';1455  } else if (goog.userAgent.GECKO) {1456    // Pre-Firefox 3, Gecko doesn't support inline-block, but -moz-inline-box1457    // is close enough.1458    style.display = goog.userAgent.isVersionOrHigher('1.9a') ? 'inline-block' :1459        '-moz-inline-box';1460  } else {1461    // Opera, Webkit, and Safari seem to do OK with the standard inline-block1462    // style.1463    style.display = 'inline-block';1464  }1465};146614671468/**1469 * Returns true if the element is using right to left (rtl) direction.1470 * @param {Element} el  The element to test.1471 * @return {boolean} True for right to left, false for left to right.1472 */1473goog.style.isRightToLeft = function(el) {1474  return 'rtl' == goog.style.getStyle_(el, 'direction');1475};147614771478/**1479 * The CSS style property corresponding to an element being1480 * unselectable on the current browser platform (null if none).1481 * Opera and IE instead use a DOM attribute 'unselectable'.1482 * @type {?string}1483 * @private1484 */1485goog.style.unselectableStyle_ =1486    goog.userAgent.GECKO ? 'MozUserSelect' :1487    goog.userAgent.WEBKIT ? 'WebkitUserSelect' :1488    null;148914901491/**1492 * Returns true if the element is set to be unselectable, false otherwise.1493 * Note that on some platforms (e.g. Mozilla), even if an element isn't set1494 * to be unselectable, it will behave as such if any of its ancestors is1495 * unselectable.1496 * @param {Element} el  Element to check.1497 * @return {boolean}  Whether the element is set to be unselectable.1498 */1499goog.style.isUnselectable = function(el) {1500  if (goog.style.unselectableStyle_) {1501    return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';1502  } else if (goog.userAgent.IE || goog.userAgent.OPERA) {1503    return el.getAttribute('unselectable') == 'on';1504  }1505  return false;1506};150715081509/**1510 * Makes the element and its descendants selectable or unselectable.  Note1511 * that on some platforms (e.g. Mozilla), even if an element isn't set to1512 * be unselectable, it will behave as such if any of its ancestors is1513 * unselectable.1514 * @param {Element} el  The element to alter.1515 * @param {boolean} unselectable  Whether the element and its descendants1516 *     should be made unselectable.1517 * @param {boolean=} opt_noRecurse  Whether to only alter the element's own1518 *     selectable state, and leave its descendants alone; defaults to false.1519 */1520goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {1521  // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?1522  var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;1523  var name = goog.style.unselectableStyle_;1524  if (name) {1525    // Add/remove the appropriate CSS style to/from the element and its1526    // descendants.1527    var value = unselectable ? 'none' : '';1528    el.style[name] = value;1529    if (descendants) {1530      for (var i = 0, descendant; descendant = descendants[i]; i++) {1531        descendant.style[name] = value;1532      }1533    }1534  } else if (goog.userAgent.IE || goog.userAgent.OPERA) {1535    // Toggle the 'unselectable' attribute on the element and its descendants.1536    var value = unselectable ? 'on' : '';1537    el.setAttribute('unselectable', value);1538    if (descendants) {1539      for (var i = 0, descendant; descendant = descendants[i]; i++) {1540        descendant.setAttribute('unselectable', value);1541      }1542    }1543  }1544};154515461547/**1548 * Gets the border box size for an element.1549 * @param {Element} element  The element to get the size for.1550 * @return {!goog.math.Size} The border box size.1551 */1552goog.style.getBorderBoxSize = function(element) {1553  return new goog.math.Size(element.offsetWidth, element.offsetHeight);1554};155515561557/**1558 * Sets the border box size of an element. This is potentially expensive in IE1559 * if the document is CSS1Compat mode1560 * @param {Element} element  The element to set the size on.1561 * @param {goog.math.Size} size  The new size.1562 */1563goog.style.setBorderBoxSize = function(element, size) {1564  var doc = goog.dom.getOwnerDocument(element);1565  var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();15661567  if (goog.userAgent.IE &&1568      !goog.userAgent.isVersionOrHigher('10') &&1569      (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {1570    var style = element.style;1571    if (isCss1CompatMode) {1572      var paddingBox = goog.style.getPaddingBox(element);1573      var borderBox = goog.style.getBorderBox(element);1574      style.pixelWidth = size.width - borderBox.left - paddingBox.left -1575                         paddingBox.right - borderBox.right;1576      style.pixelHeight = size.height - borderBox.top - paddingBox.top -1577                          paddingBox.bottom - borderBox.bottom;1578    } else {1579      style.pixelWidth = size.width;1580      style.pixelHeight = size.height;1581    }1582  } else {1583    goog.style.setBoxSizingSize_(element, size, 'border-box');1584  }1585};158615871588/**1589 * Gets the content box size for an element.  This is potentially expensive in1590 * all browsers.1591 * @param {Element} element  The element to get the size for.1592 * @return {!goog.math.Size} The content box size.1593 */1594goog.style.getContentBoxSize = function(element) {1595  var doc = goog.dom.getOwnerDocument(element);1596  var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;1597  if (ieCurrentStyle &&1598      goog.dom.getDomHelper(doc).isCss1CompatMode() &&1599      ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&1600      !ieCurrentStyle.boxSizing) {1601    // If IE in CSS1Compat mode than just use the width and height.1602    // If we have a boxSizing then fall back on measuring the borders etc.1603    var width = goog.style.getIePixelValue_(element, ieCurrentStyle.width,1604                                            'width', 'pixelWidth');1605    var height = goog.style.getIePixelValue_(element, ieCurrentStyle.height,1606                                             'height', 'pixelHeight');1607    return new goog.math.Size(width, height);1608  } else {1609    var borderBoxSize = goog.style.getBorderBoxSize(element);1610    var paddingBox = goog.style.getPaddingBox(element);1611    var borderBox = goog.style.getBorderBox(element);1612    return new goog.math.Size(borderBoxSize.width -1613                              borderBox.left - paddingBox.left -1614                              paddingBox.right - borderBox.right,1615                              borderBoxSize.height -1616                              borderBox.top - paddingBox.top -1617                              paddingBox.bottom - borderBox.bottom);1618  }1619};162016211622/**1623 * Sets the content box size of an element. This is potentially expensive in IE1624 * if the document is BackCompat mode.1625 * @param {Element} element  The element to set the size on.1626 * @param {goog.math.Size} size  The new size.1627 */1628goog.style.setContentBoxSize = function(element, size) {1629  var doc = goog.dom.getOwnerDocument(element);1630  var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();1631  if (goog.userAgent.IE &&1632      !goog.userAgent.isVersionOrHigher('10') &&1633      (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {1634    var style = element.style;1635    if (isCss1CompatMode) {1636      style.pixelWidth = size.width;1637      style.pixelHeight = size.height;1638    } else {1639      var paddingBox = goog.style.getPaddingBox(element);1640      var borderBox = goog.style.getBorderBox(element);1641      style.pixelWidth = size.width + borderBox.left + paddingBox.left +1642                         paddingBox.right + borderBox.right;1643      style.pixelHeight = size.height + borderBox.top + paddingBox.top +1644                          paddingBox.bottom + borderBox.bottom;1645    }1646  } else {1647    goog.style.setBoxSizingSize_(element, size, 'content-box');1648  }1649};165016511652/**1653 * Helper function that sets the box sizing as well as the width and height1654 * @param {Element} element  The element to set the size on.1655 * @param {goog.math.Size} size  The new size to set.1656 * @param {string} boxSizing  The box-sizing value.1657 * @private1658 */1659goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {1660  var style = element.style;1661  if (goog.userAgent.GECKO) {1662    style.MozBoxSizing = boxSizing;1663  } else if (goog.userAgent.WEBKIT) {1664    style.WebkitBoxSizing = boxSizing;1665  } else {1666    // Includes IE8 and Opera 9.50+1667    style.boxSizing = boxSizing;1668  }16691670  // Setting this to a negative value will throw an exception on IE1671  // (and doesn't do anything different than setting it to 0).1672  style.width = Math.max(size.width, 0) + 'px';1673  style.height = Math.max(size.height, 0) + 'px';1674};167516761677/**1678 * IE specific function that converts a non pixel unit to pixels.1679 * @param {Element} element  The element to convert the value for.1680 * @param {string} value  The current value as a string. The value must not be1681 *     ''.1682 * @param {string} name  The CSS property name to use for the converstion. This1683 *     should be 'left', 'top', 'width' or 'height'.1684 * @param {string} pixelName  The CSS pixel property name to use to get the1685 *     value in pixels.1686 * @return {number} The value in pixels.1687 * @private1688 */1689goog.style.getIePixelValue_ = function(element, value, name, pixelName) {1690  // Try if we already have a pixel value. IE does not do half pixels so we1691  // only check if it matches a number followed by 'px'.1692  if (/^\d+px?$/.test(value)) {1693    return parseInt(value, 10);1694  } else {1695    var oldStyleValue = element.style[name];1696    var oldRuntimeValue = element.runtimeStyle[name];1697    // set runtime style to prevent changes1698    element.runtimeStyle[name] = element.currentStyle[name];1699    element.style[name] = value;1700    var pixelValue = element.style[pixelName];1701    // restore1702    element.style[name] = oldStyleValue;1703    element.runtimeStyle[name] = oldRuntimeValue;1704    return pixelValue;1705  }1706};170717081709/**1710 * Helper function for getting the pixel padding or margin for IE.1711 * @param {Element} element  The element to get the padding for.1712 * @param {string} propName  The property name.1713 * @return {number} The pixel padding.1714 * @private1715 */1716goog.style.getIePixelDistance_ = function(element, propName) {1717  var value = goog.style.getCascadedStyle(element, propName);1718  return value ?1719      goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') : 0;1720};172117221723/**1724 * Gets the computed paddings or margins (on all sides) in pixels.1725 * @param {Element} element  The element to get the padding for.1726 * @param {string} stylePrefix  Pass 'padding' to retrieve the padding box,1727 *     or 'margin' to retrieve the margin box.1728 * @return {!goog.math.Box} The computed paddings or margins.1729 * @private1730 */1731goog.style.getBox_ = function(element, stylePrefix) {1732  if (goog.userAgent.IE) {1733    var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');1734    var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');1735    var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');1736    var bottom = goog.style.getIePixelDistance_(1737        element, stylePrefix + 'Bottom');1738    return new goog.math.Box(top, right, bottom, left);1739  } else {1740    // On non-IE browsers, getComputedStyle is always non-null.1741    var left = /** @type {string} */ (1742        goog.style.getComputedStyle(element, stylePrefix + 'Left'));1743    var right = /** @type {string} */ (1744        goog.style.getComputedStyle(element, stylePrefix + 'Right'));1745    var top = /** @type {string} */ (1746        goog.style.getComputedStyle(element, stylePrefix + 'Top'));1747    var bottom = /** @type {string} */ (1748        goog.style.getComputedStyle(element, stylePrefix + 'Bottom'));17491750    // NOTE(arv): Gecko can return floating point numbers for the computed1751    // style values.1752    return new goog.math.Box(parseFloat(top),1753                             parseFloat(right),1754                             parseFloat(bottom),1755                             parseFloat(left));1756  }1757};175817591760/**1761 * Gets the computed paddings (on all sides) in pixels.1762 * @param {Element} element  The element to get the padding for.1763 * @return {!goog.math.Box} The computed paddings.1764 */1765goog.style.getPaddingBox = function(element) {1766  return goog.style.getBox_(element, 'padding');1767};176817691770/**1771 * Gets the computed margins (on all sides) in pixels.1772 * @param {Element} element  The element to get the margins for.1773 * @return {!goog.math.Box} The computed margins.1774 */1775goog.style.getMarginBox = function(element) {1776  return goog.style.getBox_(element, 'margin');1777};177817791780/**1781 * A map used to map the border width keywords to a pixel width.1782 * @type {Object}1783 * @private1784 */1785goog.style.ieBorderWidthKeywords_ = {1786  'thin': 2,1787  'medium': 4,1788  'thick': 61789};179017911792/**1793 * Helper function for IE to get the pixel border.1794 * @param {Element} element  The element to get the pixel border for.1795 * @param {string} prop  The part of the property name.1796 * @return {number} The value in pixels.1797 * @private1798 */1799goog.style.getIePixelBorder_ = function(element, prop) {1800  if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {1801    return 0;1802  }1803  var width = goog.style.getCascadedStyle(element, prop + 'Width');1804  if (width in goog.style.ieBorderWidthKeywords_) {1805    return goog.style.ieBorderWidthKeywords_[width];1806  }1807  return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');1808};180918101811/**1812 * Gets the computed border widths (on all sides) in pixels1813 * @param {Element} element  The element to get the border widths for.1814 * @return {!goog.math.Box} The computed border widths.1815 */1816goog.style.getBorderBox = function(element) {1817  if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {1818    var left = goog.style.getIePixelBorder_(element, 'borderLeft');1819    var right = goog.style.getIePixelBorder_(element, 'borderRight');1820    var top = goog.style.getIePixelBorder_(element, 'borderTop');1821    var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');1822    return new goog.math.Box(top, right, bottom, left);1823  } else {1824    // On non-IE browsers, getComputedStyle is always non-null.1825    var left = /** @type {string} */ (1826        goog.style.getComputedStyle(element, 'borderLeftWidth'));1827    var right = /** @type {string} */ (1828        goog.style.getComputedStyle(element, 'borderRightWidth'));1829    var top = /** @type {string} */ (1830        goog.style.getComputedStyle(element, 'borderTopWidth'));1831    var bottom = /** @type {string} */ (1832        goog.style.getComputedStyle(element, 'borderBottomWidth'));18331834    return new goog.math.Box(parseFloat(top),1835                             parseFloat(right),1836                             parseFloat(bottom),1837                             parseFloat(left));1838  }1839};184018411842/**1843 * Returns the font face applied to a given node. Opera and IE should return1844 * the font actually displayed. Firefox returns the author's most-preferred1845 * font (whether the browser is capable of displaying it or not.)1846 * @param {Element} el  The element whose font family is returned.1847 * @return {string} The font family applied to el.1848 */1849goog.style.getFontFamily = function(el) {1850  var doc = goog.dom.getOwnerDocument(el);1851  var font = '';1852  // The moveToElementText method from the TextRange only works if the element1853  // is attached to the owner document.1854  if (doc.body.createTextRange && goog.dom.contains(doc, el)) {1855    var range = doc.body.createTextRange();1856    range.moveToElementText(el);1857    /** @preserveTry */1858    try {1859      font = range.queryCommandValue('FontName');1860    } catch (e) {1861      // This is a workaround for a awkward exception.1862      // On some IE, there is an exception coming from it.1863      // The error description from this exception is:1864      // This window has already been registered as a drop target1865      // This is bogus description, likely due to a bug in ie.1866      font = '';1867    }1868  }1869  if (!font) {1870    // Note if for some reason IE can't derive FontName with a TextRange, we1871    // fallback to using currentStyle1872    font = goog.style.getStyle_(el, 'fontFamily');1873  }18741875  // Firefox returns the applied font-family string (author's list of1876  // preferred fonts.) We want to return the most-preferred font, in lieu of1877  // the *actually* applied font.1878  var fontsArray = font.split(',');1879  if (fontsArray.length > 1) font = fontsArray[0];18801881  // Sanitize for x-browser consistency:1882  // Strip quotes because browsers aren't consistent with how they're1883  // applied; Opera always encloses, Firefox sometimes, and IE never.1884  return goog.string.stripQuotes(font, '"\'');1885};188618871888/**1889 * Regular expression used for getLengthUnits.1890 * @type {RegExp}1891 * @private1892 */1893goog.style.lengthUnitRegex_ = /[^\d]+$/;189418951896/**1897 * Returns the units used for a CSS length measurement.1898 * @param {string} value  A CSS length quantity.1899 * @return {?string} The units of measurement.1900 */1901goog.style.getLengthUnits = function(value) {1902  var units = value.match(goog.style.lengthUnitRegex_);1903  return units && units[0] || null;1904};190519061907/**1908 * Map of absolute CSS length units1909 * @type {Object}1910 * @private1911 */1912goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {1913  'cm' : 1,1914  'in' : 1,1915  'mm' : 1,1916  'pc' : 1,1917  'pt' : 11918};191919201921/**1922 * Map of relative CSS length units that can be accurately converted to px1923 * font-size values using getIePixelValue_. Only units that are defined in1924 * relation to a font size are convertible (%, small, etc. are not).1925 * @type {Object}1926 * @private1927 */1928goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {1929  'em' : 1,1930  'ex' : 11931};193219331934/**1935 * Returns the font size, in pixels, of text in an element.1936 * @param {Element} el  The element whose font size is returned.1937 * @return {number} The font size (in pixels).1938 */1939goog.style.getFontSize = function(el) {1940  var fontSize = goog.style.getStyle_(el, 'fontSize');1941  var sizeUnits = goog.style.getLengthUnits(fontSize);1942  if (fontSize && 'px' == sizeUnits) {1943    // NOTE(user): This could be parseFloat instead, but IE doesn't return1944    // decimal fractions in getStyle_ and Firefox reports the fractions, but1945    // ignores them when rendering. Interestingly enough, when we force the1946    // issue and size something to e.g., 50% of 25px, the browsers round in1947    // opposite directions with Firefox reporting 12px and IE 13px. I punt.1948    return parseInt(fontSize, 10);1949  }19501951  // In IE, we can convert absolute length units to a px value using1952  // goog.style.getIePixelValue_. Units defined in relation to a font size1953  // (em, ex) are applied relative to the element's parentNode and can also1954  // be converted.1955  if (goog.userAgent.IE) {1956    if (sizeUnits in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {1957      return goog.style.getIePixelValue_(el,1958                                         fontSize,1959                                         'left',1960                                         'pixelLeft');1961    } else if (el.parentNode &&1962               el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&1963               sizeUnits in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {1964      // Check the parent size - if it is the same it means the relative size1965      // value is inherited and we therefore don't want to count it twice.  If1966      // it is different, this element either has explicit style or has a CSS1967      // rule applying to it.1968      var parentElement = /** @type {Element} */ (el.parentNode);1969      var parentSize = goog.style.getStyle_(parentElement, 'fontSize');1970      return goog.style.getIePixelValue_(parentElement,1971                                         fontSize == parentSize ?1972                                             '1em' : fontSize,1973                                         'left',1974                                         'pixelLeft');1975    }1976  }19771978  // Sometimes we can't cleanly find the font size (some units relative to a1979  // node's parent's font size are difficult: %, smaller et al), so we create1980  // an invisible, absolutely-positioned span sized to be the height of an 'M'1981  // rendered in its parent's (i.e., our target element's) font size. This is1982  // the definition of CSS's font size attribute.1983  var sizeElement = goog.dom.createDom(1984      'span',1985      {'style': 'visibility:hidden;position:absolute;' +1986            'line-height:0;padding:0;margin:0;border:0;height:1em;'});1987  goog.dom.appendChild(el, sizeElement);1988  fontSize = sizeElement.offsetHeight;1989  goog.dom.removeNode(sizeElement);19901991  return fontSize;1992};199319941995/**1996 * Parses a style attribute value.  Converts CSS property names to camel case.1997 * @param {string} value The style attribute value.1998 * @return {!Object} Map of CSS properties to string values.1999 */2000goog.style.parseStyleAttribute = function(value) {2001  var result = {};2002  goog.array.forEach(value.split(/\s*;\s*/), function(pair) {2003    var keyValue = pair.split(/\s*:\s*/);2004    if (keyValue.length == 2) {2005      result[goog.string.toCamelCase(keyValue[0].toLowerCase())] = keyValue[1];2006    }2007  });2008  return result;2009};201020112012/**2013 * Reverse of parseStyleAttribute; that is, takes a style object and returns the2014 * corresponding attribute value.  Converts camel case property names to proper2015 * CSS selector names.2016 * @param {Object} obj Map of CSS properties to values.2017 * @return {string} The style attribute value.2018 */2019goog.style.toStyleAttribute = function(obj) {2020  var buffer = [];2021  goog.object.forEach(obj, function(value, key) {2022    buffer.push(goog.string.toSelectorCase(key), ':', value, ';');2023  });2024  return buffer.join('');2025};202620272028/**2029 * Sets CSS float property on an element.2030 * @param {Element} el The element to set float property on.2031 * @param {string} value The value of float CSS property to set on this element.2032 */2033goog.style.setFloat = function(el, value) {2034  el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;2035};203620372038/**2039 * Gets value of explicitly-set float CSS property on an element.2040 * @param {Element} el The element to get float property of.2041 * @return {string} The value of explicitly-set float CSS property on this2042 *     element.2043 */2044goog.style.getFloat = function(el) {2045  return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';2046};204720482049/**2050 * Returns the scroll bar width (represents the width of both horizontal2051 * and vertical scroll).2052 *2053 * @param {string=} opt_className An optional class name (or names) to apply2054 *     to the invisible div created to measure the scrollbar. This is necessary2055 *     if some scrollbars are styled differently than others.2056 * @return {number} The scroll bar width in px.2057 */2058goog.style.getScrollbarWidth = function(opt_className) {2059  // Add two hidden divs.  The child div is larger than the parent and2060  // forces scrollbars to appear on it.2061  // Using overflow:scroll does not work consistently with scrollbars that2062  // are styled with ::-webkit-scrollbar.2063  var outerDiv = goog.dom.createElement('div');2064  if (opt_className) {2065    outerDiv.className = opt_className;2066  }2067  outerDiv.style.cssText = 'overflow:auto;' +2068      'position:absolute;top:0;width:100px;height:100px';2069  var innerDiv = goog.dom.createElement('div');2070  goog.style.setSize(innerDiv, '200px', '200px');2071  outerDiv.appendChild(innerDiv);2072  goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);2073  var width = outerDiv.offsetWidth - outerDiv.clientWidth;2074  goog.dom.removeNode(outerDiv);2075  return width;2076};207720782079/**2080 * Regular expression to extract x and y translation components from a CSS2081 * transform Matrix representation.2082 *2083 * @type {!RegExp}2084 * @const2085 * @private2086 */2087goog.style.MATRIX_TRANSLATION_REGEX_ =2088    new RegExp('matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +2089               '[0-9\\.\\-]+, [0-9\\.\\-]+, ' +2090               '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');209120922093/**2094 * Returns the x,y translation component of any CSS transforms applied to the2095 * element, in pixels.2096 *2097 * @param {!Element} element The element to get the translation of.2098 * @return {!goog.math.Coordinate} The CSS translation of the element in px.2099 */2100goog.style.getCssTranslation = function(element) {2101  var transform = goog.style.getComputedTransform(element);2102  if (!transform) {2103    return new goog.math.Coordinate(0, 0);2104  }2105  var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);2106  if (!matches) {2107    return new goog.math.Coordinate(0, 0);2108  }2109  return new goog.math.Coordinate(parseFloat(matches[1]),2110                                  parseFloat(matches[2]));2111};