Path: blob/trunk/third_party/closure/goog/style/style.js
4079 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Utilities for element styles.8*9* @see ../demos/inline_block_quirks.html10* @see ../demos/inline_block_standards.html11* @see ../demos/style_viewport.html12*/1314goog.provide('goog.style');151617goog.require('goog.asserts');18goog.require('goog.dom');19goog.require('goog.dom.NodeType');20goog.require('goog.dom.TagName');21goog.require('goog.dom.safe');22goog.require('goog.dom.vendor');23goog.require('goog.html.SafeStyleSheet');24goog.require('goog.math.Box');25goog.require('goog.math.Coordinate');26goog.require('goog.math.Rect');27goog.require('goog.math.Size');28goog.require('goog.object');29goog.require('goog.reflect');30goog.require('goog.string');31goog.require('goog.userAgent');32goog.requireType('goog.events.Event');333435/**36* Sets a style value on an element.37*38* This function is not indended to patch issues in the browser's style39* handling, but to allow easy programmatic access to setting dash-separated40* style properties. An example is setting a batch of properties from a data41* object without overwriting old styles. When possible, use native APIs:42* elem.style.propertyKey = 'value' or (if obliterating old styles is fine)43* elem.style.cssText = 'property1: value1; property2: value2'.44*45* @param {Element} element The element to change.46* @param {string|Object} style If a string, a style name. If an object, a hash47* of style names to style values.48* @param {string|number|boolean=} opt_value If style was a string, then this49* should be the value.50* @return {void}51*/52goog.style.setStyle = function(element, style, opt_value) {53'use strict';54if (typeof style === 'string') {55goog.style.setStyle_(element, opt_value, style);56} else {57for (var key in style) {58goog.style.setStyle_(element, style[key], key);59}60}61};626364/**65* Sets a style value on an element, with parameters swapped to work with66* `goog.object.forEach()`. Prepends a vendor-specific prefix when67* necessary.68* @param {Element} element The element to change.69* @param {string|number|boolean|undefined} value Style value.70* @param {string} style Style name.71* @private72*/73goog.style.setStyle_ = function(element, value, style) {74'use strict';75var propertyName = goog.style.getVendorJsStyleName_(element, style);7677if (propertyName) {78// TODO(johnlenz): coerce to string?79element.style[propertyName] = /** @type {?} */ (value);80}81};828384/**85* Style name cache that stores previous property name lookups.86*87* This is used by setStyle to speed up property lookups, entries look like:88* { StyleName: ActualPropertyName }89*90* @private {!Object<string, string>}91*/92goog.style.styleNameCache_ = {};939495/**96* Returns the style property name in camel-case. If it does not exist and a97* vendor-specific version of the property does exist, then return the vendor-98* specific property name instead.99* @param {Element} element The element to change.100* @param {string} style Style name.101* @return {string} Vendor-specific style.102* @private103*/104goog.style.getVendorJsStyleName_ = function(element, style) {105'use strict';106var propertyName = goog.style.styleNameCache_[style];107if (!propertyName) {108var camelStyle = goog.string.toCamelCase(style);109propertyName = camelStyle;110111if (element.style[camelStyle] === undefined) {112var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +113goog.string.toTitleCase(camelStyle);114115if (element.style[prefixedStyle] !== undefined) {116propertyName = prefixedStyle;117}118}119goog.style.styleNameCache_[style] = propertyName;120}121122return propertyName;123};124125126/**127* Returns the style property name in CSS notation. If it does not exist and a128* vendor-specific version of the property does exist, then return the vendor-129* specific property name instead.130* @param {Element} element The element to change.131* @param {string} style Style name.132* @return {string} Vendor-specific style.133* @private134*/135goog.style.getVendorStyleName_ = function(element, style) {136'use strict';137var camelStyle = goog.string.toCamelCase(style);138139if (element.style[camelStyle] === undefined) {140var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +141goog.string.toTitleCase(camelStyle);142143if (element.style[prefixedStyle] !== undefined) {144return goog.dom.vendor.getVendorPrefix() + '-' + style;145}146}147148return style;149};150151152/**153* Retrieves an explicitly-set style value of a node. This returns '' if there154* isn't a style attribute on the element or if this style property has not been155* explicitly set in script.156*157* @param {Element} element Element to get style of.158* @param {string} property Property to get, css-style (if you have a camel-case159* property, use element.style[style]).160* @return {string} Style value.161*/162goog.style.getStyle = function(element, property) {163'use strict';164// element.style is '' for well-known properties which are unset.165// For for browser specific styles as 'filter' is undefined166// so we need to return '' explicitly to make it consistent across167// browsers.168var styleValue = element.style[goog.string.toCamelCase(property)];169170// Using typeof here because of a bug in Safari 5.1, where this value171// was undefined, but === undefined returned false.172if (typeof(styleValue) !== 'undefined') {173return styleValue;174}175176return element.style[goog.style.getVendorJsStyleName_(element, property)] ||177'';178};179180181/**182* Retrieves a computed style value of a node. It returns empty string if the183* value cannot be computed (which will be the case in Internet Explorer) or184* "none" if the property requested is an SVG one and it has not been185* explicitly set (firefox and webkit).186*187* @param {Element} element Element to get style of.188* @param {string} property Property to get (camel-case).189* @return {string} Style value.190*/191goog.style.getComputedStyle = function(element, property) {192'use strict';193var doc = goog.dom.getOwnerDocument(element);194if (doc.defaultView && doc.defaultView.getComputedStyle) {195var styles = doc.defaultView.getComputedStyle(element, null);196if (styles) {197// element.style[..] is undefined for browser specific styles198// as 'filter'.199return styles[property] || styles.getPropertyValue(property) || '';200}201}202203return '';204};205206207/**208* Gets the cascaded style value of a node, or null if the value cannot be209* computed (only Internet Explorer can do this).210*211* @param {Element} element Element to get style of.212* @param {string} style Property to get (camel-case).213* @return {string} Style value.214*/215goog.style.getCascadedStyle = function(element, style) {216'use strict';217// TODO(nicksantos): This should be documented to return null. #fixTypes218return /** @type {string} */ (219element.currentStyle ? element.currentStyle[style] : null);220};221222223/**224* Cross-browser pseudo get computed style. It returns the computed style where225* available. If not available it tries the cascaded style value (IE226* currentStyle) and in worst case the inline style value. It shouldn't be227* called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for228* discussion.229*230* @param {Element} element Element to get style of.231* @param {string} style Property to get (must be camelCase, not css-style.).232* @return {string} Style value.233* @private234*/235goog.style.getStyle_ = function(element, style) {236'use strict';237return goog.style.getComputedStyle(element, style) ||238goog.style.getCascadedStyle(element, style) ||239(element.style && element.style[style]);240};241242243/**244* Retrieves the computed value of the box-sizing CSS attribute.245* Browser support: http://caniuse.com/css3-boxsizing.246* @param {!Element} element The element whose box-sizing to get.247* @return {?string} 'content-box', 'border-box' or 'padding-box'. null if248* box-sizing is not supported (IE7 and below).249*/250goog.style.getComputedBoxSizing = function(element) {251'use strict';252return goog.style.getStyle_(element, 'boxSizing') ||253goog.style.getStyle_(element, 'MozBoxSizing') ||254goog.style.getStyle_(element, 'WebkitBoxSizing') || null;255};256257258/**259* Retrieves the computed value of the position CSS attribute.260* @param {Element} element The element to get the position of.261* @return {string} Position value.262*/263goog.style.getComputedPosition = function(element) {264'use strict';265return goog.style.getStyle_(element, 'position');266};267268269/**270* Retrieves the computed background color string for a given element. The271* string returned is suitable for assigning to another element's272* background-color, but is not guaranteed to be in any particular string273* format. Accessing the color in a numeric form may not be possible in all274* browsers or with all input.275*276* If the background color for the element is defined as a hexadecimal value,277* the resulting string can be parsed by goog.color.parse in all supported278* browsers.279*280* Whether named colors like "red" or "lightblue" get translated into a281* format which can be parsed is browser dependent. Calling this function on282* transparent elements will return "transparent" in most browsers or283* "rgba(0, 0, 0, 0)" in WebKit.284* @param {Element} element The element to get the background color of.285* @return {string} The computed string value of the background color.286*/287goog.style.getBackgroundColor = function(element) {288'use strict';289return goog.style.getStyle_(element, 'backgroundColor');290};291292293/**294* Retrieves the computed value of the overflow-x CSS attribute.295* @param {Element} element The element to get the overflow-x of.296* @return {string} The computed string value of the overflow-x attribute.297*/298goog.style.getComputedOverflowX = function(element) {299'use strict';300return goog.style.getStyle_(element, 'overflowX');301};302303304/**305* Retrieves the computed value of the overflow-y CSS attribute.306* @param {Element} element The element to get the overflow-y of.307* @return {string} The computed string value of the overflow-y attribute.308*/309goog.style.getComputedOverflowY = function(element) {310'use strict';311return goog.style.getStyle_(element, 'overflowY');312};313314315/**316* Retrieves the computed value of the z-index CSS attribute.317* @param {Element} element The element to get the z-index of.318* @return {string|number} The computed value of the z-index attribute.319*/320goog.style.getComputedZIndex = function(element) {321'use strict';322return goog.style.getStyle_(element, 'zIndex');323};324325326/**327* Retrieves the computed value of the text-align CSS attribute.328* @param {Element} element The element to get the text-align of.329* @return {string} The computed string value of the text-align attribute.330*/331goog.style.getComputedTextAlign = function(element) {332'use strict';333return goog.style.getStyle_(element, 'textAlign');334};335336337/**338* Retrieves the computed value of the cursor CSS attribute.339* @param {Element} element The element to get the cursor of.340* @return {string} The computed string value of the cursor attribute.341*/342goog.style.getComputedCursor = function(element) {343'use strict';344return goog.style.getStyle_(element, 'cursor');345};346347348/**349* Retrieves the computed value of the CSS transform attribute.350* @param {Element} element The element to get the transform of.351* @return {string} The computed string representation of the transform matrix.352*/353goog.style.getComputedTransform = function(element) {354'use strict';355var property = goog.style.getVendorStyleName_(element, 'transform');356return goog.style.getStyle_(element, property) ||357goog.style.getStyle_(element, 'transform');358};359360361/**362* Sets the top/left values of an element. If no unit is specified in the363* argument then it will add px. The second argument is required if the first364* argument is a string or number and is ignored if the first argument365* is a coordinate.366* @param {Element} el Element to move.367* @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.368* @param {string|number=} opt_arg2 Top position.369* @return {void}370*/371goog.style.setPosition = function(el, arg1, opt_arg2) {372'use strict';373var x, y;374375if (arg1 instanceof goog.math.Coordinate) {376x = arg1.x;377y = arg1.y;378} else {379x = arg1;380y = opt_arg2;381}382383el.style.left = goog.style.getPixelStyleValue_(384/** @type {number|string} */ (x), false);385el.style.top = goog.style.getPixelStyleValue_(386/** @type {number|string} */ (y), false);387};388389390/**391* Gets the offsetLeft and offsetTop properties of an element and returns them392* in a Coordinate object393* @param {Element} element Element.394* @return {!goog.math.Coordinate} The position.395*/396goog.style.getPosition = function(element) {397'use strict';398return new goog.math.Coordinate(399/** @type {!HTMLElement} */ (element).offsetLeft,400/** @type {!HTMLElement} */ (element).offsetTop);401};402403404/**405* Returns the viewport element for a particular document406* @param {Node=} opt_node DOM node (Document is OK) to get the viewport element407* of.408* @return {Element} document.documentElement or document.body.409*/410goog.style.getClientViewportElement = function(opt_node) {411'use strict';412var doc;413if (opt_node) {414doc = goog.dom.getOwnerDocument(opt_node);415} else {416doc = goog.dom.getDocument();417}418419// In old IE versions the document.body represented the viewport420if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&421!goog.dom.getDomHelper(doc).isCss1CompatMode()) {422return doc.body;423}424return doc.documentElement;425};426427428/**429* Calculates the viewport coordinates relative to the page/document430* containing the node. The viewport may be the browser viewport for431* non-iframe document, or the iframe container for iframe'd document.432* @param {!Document} doc The document to use as the reference point.433* @return {!goog.math.Coordinate} The page offset of the viewport.434*/435goog.style.getViewportPageOffset = function(doc) {436'use strict';437var body = doc.body;438var documentElement = doc.documentElement;439var scrollLeft = body.scrollLeft || documentElement.scrollLeft;440var scrollTop = body.scrollTop || documentElement.scrollTop;441return new goog.math.Coordinate(scrollLeft, scrollTop);442};443444445/**446* Gets the client rectangle of the DOM element.447*448* getBoundingClientRect is part of a new CSS object model draft (with a449* long-time presence in IE), replacing the error-prone parent offset450* computation and the now-deprecated Gecko getBoxObjectFor.451*452* This utility patches common browser bugs in getBoundingClientRect. It453* will fail if getBoundingClientRect is unsupported.454*455* If the element is not in the DOM, the result is undefined, and an error may456* be thrown depending on user agent.457*458* @param {!Element} el The element whose bounding rectangle is being queried.459* @return {!Object} A native bounding rectangle with numerical left, top,460* right, and bottom. Reported by Firefox to be of object type ClientRect.461* @private462*/463goog.style.getBoundingClientRect_ = function(el) {464'use strict';465try {466return el.getBoundingClientRect();467} catch (e) {468// In IE, calling getBoundingClientRect on an orphan element raises an469// "Unspecified Error". All other browsers return zeros.470return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};471}472};473474475/**476* Returns the first parent that could affect the position of a given element.477* @param {Element} element The element to get the offset parent for.478* @return {Element} The first offset parent or null if one cannot be found.479* @suppress {strictMissingProperties} Added to tighten compiler checks480*/481goog.style.getOffsetParent = function(element) {482'use strict';483// element.offsetParent does the right thing in IE7 and below. In other484// browsers it only includes elements with position absolute, relative or485// fixed, not elements with overflow set to auto or scroll.486if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {487goog.asserts.assert(element && 'offsetParent' in element);488return element.offsetParent;489}490491var doc = goog.dom.getOwnerDocument(element);492var positionStyle = goog.style.getStyle_(element, 'position');493var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';494for (var parent = element.parentNode; parent && parent != doc;495parent = parent.parentNode) {496// Skip shadowDOM roots.497if (parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT && parent.host) {498// Cast because the assignment is not type safe, and without a cast we499// start typing parent loosely and get bad disambiguation.500parent = /** @type {!Element} */ (parent.host);501}502positionStyle =503goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');504skipStatic = skipStatic && positionStyle == 'static' &&505parent != doc.documentElement && parent != doc.body;506if (!skipStatic &&507(parent.scrollWidth > parent.clientWidth ||508parent.scrollHeight > parent.clientHeight ||509positionStyle == 'fixed' || positionStyle == 'absolute' ||510positionStyle == 'relative')) {511return /** @type {!Element} */ (parent);512}513}514return null;515};516517518/**519* Calculates and returns the visible rectangle for a given element. Returns a520* box describing the visible portion of the nearest scrollable offset ancestor.521* Coordinates are given relative to the document.522*523* @param {Element} element Element to get the visible rect for.524* @return {goog.math.Box} Bounding elementBox describing the visible rect or525* null if scrollable ancestor isn't inside the visible viewport.526*/527goog.style.getVisibleRectForElement = function(element) {528'use strict';529var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);530var dom = goog.dom.getDomHelper(element);531var body = dom.getDocument().body;532var documentElement = dom.getDocument().documentElement;533var scrollEl = dom.getDocumentScrollElement();534535// Determine the size of the visible rect by climbing the dom accounting for536// all scrollable containers.537for (var el = element; el = goog.style.getOffsetParent(el);) {538// clientWidth is zero for inline block elements in IE.539// on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0540if ((!goog.userAgent.IE || el.clientWidth != 0) &&541(!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&542// body may have overflow set on it, yet we still get the entire543// viewport. In some browsers, el.offsetParent may be544// document.documentElement, so check for that too.545(el != body && el != documentElement &&546goog.style.getStyle_(el, 'overflow') != 'visible')) {547var pos = goog.style.getPageOffset(el);548var client = goog.style.getClientLeftTop(el);549pos.x += client.x;550pos.y += client.y;551552visibleRect.top = Math.max(visibleRect.top, pos.y);553visibleRect.right = Math.min(visibleRect.right, pos.x + el.clientWidth);554visibleRect.bottom =555Math.min(visibleRect.bottom, pos.y + el.clientHeight);556visibleRect.left = Math.max(visibleRect.left, pos.x);557}558}559560// Clip by window's viewport.561var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;562visibleRect.left = Math.max(visibleRect.left, scrollX);563visibleRect.top = Math.max(visibleRect.top, scrollY);564var winSize = dom.getViewportSize();565visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);566visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);567return visibleRect.top >= 0 && visibleRect.left >= 0 &&568visibleRect.bottom > visibleRect.top &&569visibleRect.right > visibleRect.left ?570visibleRect :571null;572};573574575/**576* Calculate the scroll position of `container` with the minimum amount so577* that the content and the borders of the given `element` become visible.578* If the element is bigger than the container, its top left corner will be579* aligned as close to the container's top left corner as possible.580*581* @param {Element} element The element to make visible.582* @param {Element=} opt_container The container to scroll. If not set, then the583* document scroll element will be used.584* @param {boolean=} opt_center Whether to center the element in the container.585* Defaults to false.586* @return {!goog.math.Coordinate} The new scroll position of the container,587* in form of goog.math.Coordinate(scrollLeft, scrollTop).588*/589goog.style.getContainerOffsetToScrollInto = function(590element, opt_container, opt_center) {591'use strict';592var container = opt_container || goog.dom.getDocumentScrollElement();593// Absolute position of the element's border's top left corner.594var elementPos = goog.style.getPageOffset(element);595// Absolute position of the container's border's top left corner.596var containerPos = goog.style.getPageOffset(container);597var containerBorder = goog.style.getBorderBox(container);598if (container == goog.dom.getDocumentScrollElement()) {599// The element position is calculated based on the page offset, and the600// document scroll element holds the scroll position within the page. We can601// use the scroll position to calculate the relative position from the602// element.603var relX = elementPos.x - container.scrollLeft;604var relY = elementPos.y - container.scrollTop;605if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {606// In older versions of IE getPageOffset(element) does not include the607// container border so it has to be added to accommodate.608relX += containerBorder.left;609relY += containerBorder.top;610}611} else {612// Relative pos. of the element's border box to the container's content box.613var relX = elementPos.x - containerPos.x - containerBorder.left;614var relY = elementPos.y - containerPos.y - containerBorder.top;615}616// How much the element can move in the container, i.e. the difference between617// the element's bottom-right-most and top-left-most position where it's618// fully visible.619var elementSize = goog.style.getSizeWithDisplay_(element);620var spaceX = container.clientWidth - elementSize.width;621var spaceY = container.clientHeight - elementSize.height;622var scrollLeft = container.scrollLeft;623var scrollTop = container.scrollTop;624if (opt_center) {625// All browsers round non-integer scroll positions down.626scrollLeft += relX - spaceX / 2;627scrollTop += relY - spaceY / 2;628} else {629// This formula was designed to give the correct scroll values in the630// following cases:631// - element is higher than container (spaceY < 0) => scroll down by relY632// - element is not higher that container (spaceY >= 0):633// - it is above container (relY < 0) => scroll up by abs(relY)634// - it is below container (relY > spaceY) => scroll down by relY - spaceY635// - it is in the container => don't scroll636scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));637scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));638}639return new goog.math.Coordinate(scrollLeft, scrollTop);640};641642643/**644* Changes the scroll position of `container` with the minimum amount so645* that the content and the borders of the given `element` become visible.646* If the element is bigger than the container, its top left corner will be647* aligned as close to the container's top left corner as possible.648*649* @param {Element} element The element to make visible.650* @param {Element=} opt_container The container to scroll. If not set, then the651* document scroll element will be used.652* @param {boolean=} opt_center Whether to center the element in the container.653* Defaults to false.654*/655goog.style.scrollIntoContainerView = function(656element, opt_container, opt_center) {657'use strict';658var container = opt_container || goog.dom.getDocumentScrollElement();659var offset =660goog.style.getContainerOffsetToScrollInto(element, container, opt_center);661container.scrollLeft = offset.x;662container.scrollTop = offset.y;663};664665666/**667* Returns clientLeft (width of the left border and, if the directionality is668* right to left, the vertical scrollbar) and clientTop as a coordinate object.669*670* @param {Element} el Element to get clientLeft for.671* @return {!goog.math.Coordinate} Client left and top.672*/673goog.style.getClientLeftTop = function(el) {674'use strict';675return new goog.math.Coordinate(el.clientLeft, el.clientTop);676};677678679/**680* Returns a Coordinate object relative to the top-left of the HTML document.681* Implemented as a single function to save having to do two recursive loops in682* opera and safari just to get both coordinates. If you just want one value do683* use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but684* note if you call both those methods the tree will be analysed twice.685*686* @param {Element} el Element to get the page offset for.687* @return {!goog.math.Coordinate} The page offset.688*/689goog.style.getPageOffset = function(el) {690'use strict';691var doc = goog.dom.getOwnerDocument(el);692// TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.693goog.asserts.assertObject(el, 'Parameter is required');694695// NOTE(arv): If element is hidden (display none or disconnected or any the696// ancestors are hidden) we get (0,0) by default but we still do the697// accumulation of scroll position.698699// TODO(arv): Should we check if the node is disconnected and in that case700// return (0,0)?701702var pos = new goog.math.Coordinate(0, 0);703var viewportElement = goog.style.getClientViewportElement(doc);704if (el == viewportElement) {705// viewport is always at 0,0 as that defined the coordinate system for this706// function - this avoids special case checks in the code below707return pos;708}709710var box = goog.style.getBoundingClientRect_(el);711// Must add the scroll coordinates in to get the absolute page offset712// of element since getBoundingClientRect returns relative coordinates to713// the viewport.714var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();715/** @suppress {strictMissingProperties} Added to tighten compiler checks */716pos.x = box.left + scrollCoord.x;717/** @suppress {strictMissingProperties} Added to tighten compiler checks */718pos.y = box.top + scrollCoord.y;719720return pos;721};722723724/**725* Returns the left coordinate of an element relative to the HTML document726* @param {Element} el Elements.727* @return {number} The left coordinate.728*/729goog.style.getPageOffsetLeft = function(el) {730'use strict';731return goog.style.getPageOffset(el).x;732};733734735/**736* Returns the top coordinate of an element relative to the HTML document737* @param {Element} el Elements.738* @return {number} The top coordinate.739*/740goog.style.getPageOffsetTop = function(el) {741'use strict';742return goog.style.getPageOffset(el).y;743};744745746/**747* Returns a Coordinate object relative to the top-left of an HTML document748* in an ancestor frame of this element. Used for measuring the position of749* an element inside a frame relative to a containing frame.750*751* @param {Element} el Element to get the page offset for.752* @param {Window} relativeWin The window to measure relative to. If relativeWin753* is not in the ancestor frame chain of the element, we measure relative to754* the top-most window.755* @return {!goog.math.Coordinate} The page offset.756*/757goog.style.getFramedPageOffset = function(el, relativeWin) {758'use strict';759var position = new goog.math.Coordinate(0, 0);760761// Iterate up the ancestor frame chain, keeping track of the current window762// and the current element in that window.763var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));764765// MS Edge throws when accessing "parent" if el's containing iframe has been766// deleted.767if (!goog.reflect.canAccessProperty(currentWin, 'parent')) {768return position;769}770771var currentEl = el;772do {773// if we're at the top window, we want to get the page offset.774// if we're at an inner frame, we only want to get the window position775// so that we can determine the actual page offset in the context of776// the outer window.777var offset = currentWin == relativeWin ?778goog.style.getPageOffset(currentEl) :779goog.style.getClientPositionForElement_(goog.asserts.assert(currentEl));780781position.x += offset.x;782position.y += offset.y;783} while (currentWin && currentWin != relativeWin &&784currentWin != currentWin.parent &&785(currentEl = currentWin.frameElement) &&786(currentWin = currentWin.parent));787788return position;789};790791792/**793* Translates the specified rect relative to origBase page, for newBase page.794* If origBase and newBase are the same, this function does nothing.795*796* @param {goog.math.Rect} rect The source rectangle relative to origBase page,797* and it will have the translated result.798* @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.799* @param {goog.dom.DomHelper} newBase The DomHelper for the resultant800* coordinate. This must be a DOM for an ancestor frame of origBase801* or the same as origBase.802*/803goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {804'use strict';805if (origBase.getDocument() != newBase.getDocument()) {806var body = origBase.getDocument().body;807var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());808809// Adjust Body's margin.810pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));811812if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&813!origBase.isCss1CompatMode()) {814pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());815}816817rect.left += pos.x;818rect.top += pos.y;819}820};821822823/**824* Returns the position of an element relative to another element in the825* document. A relative to B826* @param {Element|Event|goog.events.Event} a Element or mouse event whose827* position we're calculating.828* @param {Element|Event|goog.events.Event} b Element or mouse event position829* is relative to.830* @return {!goog.math.Coordinate} The relative position.831*/832goog.style.getRelativePosition = function(a, b) {833'use strict';834var ap = goog.style.getClientPosition(a);835var bp = goog.style.getClientPosition(b);836return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);837};838839840/**841* Returns the position of the event or the element's border box relative to842* the client viewport.843* @param {!Element} el Element whose position to get.844* @return {!goog.math.Coordinate} The position.845* @private846* @suppress {strictMissingProperties} Added to tighten compiler checks847*/848goog.style.getClientPositionForElement_ = function(el) {849'use strict';850var box = goog.style.getBoundingClientRect_(el);851return new goog.math.Coordinate(box.left, box.top);852};853854855/**856* Returns the position of the event or the element's border box relative to857* the client viewport. If an event is passed, and if this event is a "touch"858* event, then the position of the first changedTouches will be returned.859* @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.860* @return {!goog.math.Coordinate} The position.861* @suppress {strictMissingProperties} Added to tighten compiler checks862*/863goog.style.getClientPosition = function(el) {864'use strict';865goog.asserts.assert(el);866if (el.nodeType == goog.dom.NodeType.ELEMENT) {867return goog.style.getClientPositionForElement_(868/** @type {!Element} */ (el));869} else {870/** @suppress {strictMissingProperties} Added to tighten compiler checks */871var targetEvent = el.changedTouches ? el.changedTouches[0] : el;872return new goog.math.Coordinate(targetEvent.clientX, targetEvent.clientY);873}874};875876877/**878* Moves an element to the given coordinates relative to the client viewport.879* @param {Element} el Absolutely positioned element to set page offset for.880* It must be in the document.881* @param {number|goog.math.Coordinate} x Left position of the element's margin882* box or a coordinate object.883* @param {number=} opt_y Top position of the element's margin box.884* @return {void}885*/886goog.style.setPageOffset = function(el, x, opt_y) {887'use strict';888// Get current pageoffset889var cur = goog.style.getPageOffset(el);890891if (x instanceof goog.math.Coordinate) {892opt_y = x.y;893x = x.x;894}895896// NOTE(arv): We cannot allow strings for x and y. We could but that would897// require us to manually transform between different units898899// Work out deltas900var dx = goog.asserts.assertNumber(x) - cur.x;901var dy = Number(opt_y) - cur.y;902903// Set position to current left/top + delta904goog.style.setPosition(905el, /** @type {!HTMLElement} */ (el).offsetLeft + dx,906/** @type {!HTMLElement} */ (el).offsetTop + dy);907};908909910/**911* Sets the width/height values of an element. If an argument is numeric,912* or a goog.math.Size is passed, it is assumed to be pixels and will add913* 'px' after converting it to an integer in string form. (This just sets the914* CSS width and height properties so it might set content-box or border-box915* size depending on the box model the browser is using.)916*917* @param {Element} element Element to set the size of.918* @param {string|number|goog.math.Size} w Width of the element, or a919* size object.920* @param {string|number=} opt_h Height of the element. Required if w is not a921* size object.922* @return {void}923*/924goog.style.setSize = function(element, w, opt_h) {925'use strict';926var h;927if (w instanceof goog.math.Size) {928h = w.height;929w = w.width;930} else {931if (opt_h == undefined) {932throw new Error('missing height argument');933}934h = opt_h;935}936937goog.style.setWidth(element, /** @type {string|number} */ (w));938goog.style.setHeight(element, h);939};940941942/**943* Helper function to create a string to be set into a pixel-value style944* property of an element. Can round to the nearest integer value.945*946* @param {string|number} value The style value to be used. If a number,947* 'px' will be appended, otherwise the value will be applied directly.948* @param {boolean} round Whether to round the nearest integer (if property949* is a number).950* @return {string} The string value for the property.951* @private952*/953goog.style.getPixelStyleValue_ = function(value, round) {954'use strict';955if (typeof value == 'number') {956value = (round ? Math.round(value) : value) + 'px';957}958959return value;960};961962963/**964* Set the height of an element. Sets the element's style property.965* @param {Element} element Element to set the height of.966* @param {string|number} height The height value to set. If a number, 'px'967* will be appended, otherwise the value will be applied directly.968*/969goog.style.setHeight = function(element, height) {970'use strict';971element.style.height = goog.style.getPixelStyleValue_(height, true);972};973974975/**976* Set the width of an element. Sets the element's style property.977* @param {Element} element Element to set the width of.978* @param {string|number} width The width value to set. If a number, 'px'979* will be appended, otherwise the value will be applied directly.980*/981goog.style.setWidth = function(element, width) {982'use strict';983element.style.width = goog.style.getPixelStyleValue_(width, true);984};985986987/**988* Gets the height and width of an element, even if its display is none.989*990* Specifically, this returns the height and width of the border box,991* irrespective of the box model in effect.992*993* Note that this function does not take CSS transforms into account. Please see994* `goog.style.getTransformedSize`.995* @param {Element} element Element to get size of.996* @return {!goog.math.Size} Object with width/height properties.997*/998goog.style.getSize = function(element) {999'use strict';1000return goog.style.evaluateWithTemporaryDisplay_(1001goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));1002};100310041005/**1006* Call `fn` on `element` such that `element`'s dimensions are1007* accurate when it's passed to `fn`.1008* @param {function(!Element): T} fn Function to call with `element` as1009* an argument after temporarily changing `element`'s display such1010* that its dimensions are accurate.1011* @param {!Element} element Element (which may have display none) to use as1012* argument to `fn`.1013* @return {T} Value returned by calling `fn` with `element`.1014* @template T1015* @private1016*/1017goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {1018'use strict';1019if (goog.style.getStyle_(element, 'display') != 'none') {1020return fn(element);1021}10221023var style = element.style;1024var originalDisplay = style.display;1025var originalVisibility = style.visibility;1026var originalPosition = style.position;10271028style.visibility = 'hidden';1029style.position = 'absolute';1030style.display = 'inline';10311032var retVal = fn(element);10331034style.display = originalDisplay;1035style.position = originalPosition;1036style.visibility = originalVisibility;10371038return retVal;1039};104010411042/**1043* Gets the height and width of an element when the display is not none.1044* @param {Element} element Element to get size of.1045* @return {!goog.math.Size} Object with width/height properties.1046* @private1047* @suppress {strictMissingProperties} Added to tighten compiler checks1048*/1049goog.style.getSizeWithDisplay_ = function(element) {1050'use strict';1051var offsetWidth = /** @type {!HTMLElement} */ (element).offsetWidth;1052var offsetHeight = /** @type {!HTMLElement} */ (element).offsetHeight;1053var webkitOffsetsZero =1054goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;1055if ((offsetWidth === undefined || webkitOffsetsZero) &&1056element.getBoundingClientRect) {1057// Fall back to calling getBoundingClientRect when offsetWidth or1058// offsetHeight are not defined, or when they are zero in WebKit browsers.1059// This makes sure that we return for the correct size for SVG elements, but1060// will still return 0 on Webkit prior to 534.8, see1061// http://trac.webkit.org/changeset/67252.1062var clientRect = goog.style.getBoundingClientRect_(element);1063return new goog.math.Size(1064clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);1065}1066return new goog.math.Size(offsetWidth, offsetHeight);1067};106810691070/**1071* Gets the height and width of an element, post transform, even if its display1072* is none.1073*1074* This is like `goog.style.getSize`, except:1075* <ol>1076* <li>Takes webkitTransforms such as rotate and scale into account.1077* <li>Will return null if `element` doesn't respond to1078* `getBoundingClientRect`.1079* <li>Currently doesn't make sense on non-WebKit browsers which don't support1080* webkitTransforms.1081* </ol>1082* @param {!Element} element Element to get size of.1083* @return {goog.math.Size} Object with width/height properties.1084* @suppress {strictMissingProperties} Added to tighten compiler checks1085*/1086goog.style.getTransformedSize = function(element) {1087'use strict';1088if (!element.getBoundingClientRect) {1089return null;1090}10911092var clientRect = goog.style.evaluateWithTemporaryDisplay_(1093goog.style.getBoundingClientRect_, element);1094return new goog.math.Size(1095clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);1096};109710981099/**1100* Returns a bounding rectangle for a given element in page space.1101* @param {Element} element Element to get bounds of. Must not be display none.1102* @return {!goog.math.Rect} Bounding rectangle for the element.1103*/1104goog.style.getBounds = function(element) {1105'use strict';1106var o = goog.style.getPageOffset(element);1107var s = goog.style.getSize(element);1108return new goog.math.Rect(o.x, o.y, s.width, s.height);1109};111011111112/**1113* Converts a CSS selector in the form style-property to styleProperty.1114* @param {*} selector CSS Selector.1115* @return {string} Camel case selector.1116* @deprecated Use goog.string.toCamelCase instead.1117*/1118goog.style.toCamelCase = function(selector) {1119'use strict';1120return goog.string.toCamelCase(String(selector));1121};112211231124/**1125* Converts a CSS selector in the form styleProperty to style-property.1126* @param {string} selector Camel case selector.1127* @return {string} Selector cased.1128* @deprecated Use goog.string.toSelectorCase instead.1129*/1130goog.style.toSelectorCase = function(selector) {1131'use strict';1132return goog.string.toSelectorCase(selector);1133};113411351136/**1137* Gets the opacity of a node (x-browser). This gets the inline style opacity1138* of the node, and does not take into account the cascaded or the computed1139* style for this node.1140* @param {Element} el Element whose opacity has to be found.1141* @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}1142* if the opacity is not set.1143*/1144goog.style.getOpacity = function(el) {1145'use strict';1146goog.asserts.assert(el);1147var style = el.style;1148var result = '';1149if ('opacity' in style) {1150result = style.opacity;1151} else if ('MozOpacity' in style) {1152result = style.MozOpacity;1153} else if ('filter' in style) {1154var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);1155if (match) {1156result = String(match[1] / 100);1157}1158}1159return result == '' ? result : Number(result);1160};116111621163/**1164* Sets the opacity of a node (x-browser).1165* @param {Element} el Elements whose opacity has to be set.1166* @param {number|string} alpha Opacity between 0 and 1 or an empty string1167* {@code ''} to clear the opacity.1168* @return {void}1169*/1170goog.style.setOpacity = function(el, alpha) {1171'use strict';1172goog.asserts.assert(el);1173var style = el.style;1174if ('opacity' in style) {1175style.opacity = alpha;1176} else if ('MozOpacity' in style) {1177style.MozOpacity = alpha;1178} else if ('filter' in style) {1179// TODO(arv): Overwriting the filter might have undesired side effects.1180if (alpha === '') {1181/**1182* @suppress {strictMissingProperties} Added to tighten compiler checks1183*/1184style.filter = '';1185} else {1186/**1187* @suppress {strictMissingProperties} Added to tighten compiler checks1188*/1189style.filter = 'alpha(opacity=' + (Number(alpha) * 100) + ')';1190}1191}1192};119311941195/**1196* Sets the background of an element to a transparent image in a browser-1197* independent manner.1198*1199* This function does not support repeating backgrounds or alternate background1200* positions to match the behavior of Internet Explorer. It also does not1201* support sizingMethods other than crop since they cannot be replicated in1202* browsers other than Internet Explorer.1203*1204* @param {Element} el The element to set background on.1205* @param {string} src The image source URL.1206* @return {void}1207*/1208goog.style.setTransparentBackgroundImage = function(el, src) {1209'use strict';1210var style = el.style;1211// It is safe to use the style.filter in IE only. In Safari 'filter' is in1212// style object but access to style.filter causes it to throw an exception.1213// Note: IE8 supports images with an alpha channel.12141215// Set style properties individually instead of using background shorthand1216// to prevent overwriting a pre-existing background color.1217style.backgroundImage = 'url(' + src + ')';1218style.backgroundPosition = 'top left';1219style.backgroundRepeat = 'no-repeat';1220};122112221223/**1224* Clears the background image of an element in a browser independent manner.1225* @param {Element} el The element to clear background image for.1226*/1227goog.style.clearTransparentBackgroundImage = function(el) {1228'use strict';1229var style = el.style;1230if ('filter' in style) {1231// See TODO in setOpacity.1232/** @suppress {strictMissingProperties} Added to tighten compiler checks */1233style.filter = '';1234} else {1235// Set style properties individually instead of using background shorthand1236// to prevent overwriting a pre-existing background color.1237style.backgroundImage = 'none';1238}1239};124012411242/**1243* Shows or hides an element from the page. Hiding the element is done by1244* setting the display property to "none", removing the element from the1245* rendering hierarchy so it takes up no space. To show the element, the default1246* inherited display property is restored (defined either in stylesheets or by1247* the browser's default style rules.)1248*1249* Caveat 1: if the inherited display property for the element is set to "none"1250* by the stylesheets, that is the property that will be restored by a call to1251* showElement(), effectively toggling the display between "none" and "none".1252*1253* Caveat 2: if the element display style is set inline (by setting either1254* element.style.display or a style attribute in the HTML), a call to1255* showElement will clear that setting and defer to the inherited style in the1256* stylesheet.1257* @param {Element} el Element to show or hide.1258* @param {*} display True to render the element in its default style,1259* false to disable rendering the element.1260* @return {void}1261* @deprecated Use goog.style.setElementShown instead.1262*/1263goog.style.showElement = function(el, display) {1264'use strict';1265goog.style.setElementShown(el, display);1266};126712681269/**1270* Shows or hides an element from the page. Hiding the element is done by1271* setting the display property to "none", removing the element from the1272* rendering hierarchy so it takes up no space. To show the element, the default1273* inherited display property is restored (defined either in stylesheets or by1274* the browser's default style rules).1275*1276* Caveat 1: if the inherited display property for the element is set to "none"1277* by the stylesheets, that is the property that will be restored by a call to1278* setElementShown(), effectively toggling the display between "none" and1279* "none".1280*1281* Caveat 2: if the element display style is set inline (by setting either1282* element.style.display or a style attribute in the HTML), a call to1283* setElementShown will clear that setting and defer to the inherited style in1284* the stylesheet.1285* @param {Element} el Element to show or hide.1286* @param {*} isShown True to render the element in its default style,1287* false to disable rendering the element.1288* @return {void}1289*/1290goog.style.setElementShown = function(el, isShown) {1291'use strict';1292el.style.display = isShown ? '' : 'none';1293};129412951296/**1297* Test whether the given element has been shown or hidden via a call to1298* {@link #setElementShown}.1299*1300* Note this is strictly a companion method for a call1301* to {@link #setElementShown} and the same caveats apply; in particular, this1302* method does not guarantee that the return value will be consistent with1303* whether or not the element is actually visible.1304*1305* @param {Element} el The element to test.1306* @return {boolean} Whether the element has been shown.1307* @see #setElementShown1308*/1309goog.style.isElementShown = function(el) {1310'use strict';1311return el.style.display != 'none';1312};131313141315/**1316* Installs the style sheet into the window that contains opt_node. If1317* opt_node is null, the main window is used.1318* @param {!goog.html.SafeStyleSheet} safeStyleSheet The style sheet to install.1319* @param {?Node=} opt_node Node whose parent document should have the1320* styles installed.1321* @return {!HTMLStyleElement|!StyleSheet} In IE<11, a StyleSheet object with no1322* owning <style> tag (this is how IE creates style sheets). In every1323* other browser, a <style> element with an attached style. This1324* doesn't return a StyleSheet object so that setSafeStyleSheet can replace1325* it (otherwise, if you pass a StyleSheet to setSafeStyleSheet, it will1326* make a new StyleSheet and leave the original StyleSheet orphaned).1327*/1328goog.style.installSafeStyleSheet = function(safeStyleSheet, opt_node) {1329'use strict';1330var dh = goog.dom.getDomHelper(opt_node);13311332// IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be1333// undefined as of IE 11.1334var doc = dh.getDocument();1335if (goog.userAgent.IE && doc.createStyleSheet) {1336/** @type {(!HTMLStyleElement|!StyleSheet)} */1337var styleSheet = doc.createStyleSheet();1338goog.style.setSafeStyleSheet(styleSheet, safeStyleSheet);1339return styleSheet;1340} else {1341var head = dh.getElementsByTagNameAndClass(goog.dom.TagName.HEAD)[0];13421343// In opera documents are not guaranteed to have a head element, thus we1344// have to make sure one exists before using it.1345if (!head) {1346var body = dh.getElementsByTagNameAndClass(goog.dom.TagName.BODY)[0];1347head = dh.createDom(goog.dom.TagName.HEAD);1348body.parentNode.insertBefore(head, body);1349}1350var el = dh.createDom(goog.dom.TagName.STYLE);1351const nonce = goog.dom.safe.getStyleNonce();1352if (nonce) {1353el.setAttribute('nonce', nonce);1354}13551356// NOTE(user): Setting styles after the style element has been appended1357// to the head results in a nasty Webkit bug in certain scenarios. Please1358// refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional1359// details.1360goog.style.setSafeStyleSheet(el, safeStyleSheet);1361dh.appendChild(head, el);1362return el;1363}1364};136513661367/**1368* Removes the styles added by {@link #installSafeStyleSheet}.1369* @param {Element|StyleSheet} styleSheet The value returned by1370* {@link #installSafeStyleSheet}.1371*/1372goog.style.uninstallStyles = function(styleSheet) {1373'use strict';1374/** @suppress {strictMissingProperties} Added to tighten compiler checks */1375var node = styleSheet.ownerNode || styleSheet.owningElement ||1376/** @type {Element} */ (styleSheet);1377goog.dom.removeNode(node);1378};137913801381/**1382* Sets the content of a style element. The style element can be any valid1383* style element. This element will have its content completely replaced by1384* the safeStyleSheet.1385* @param {!Element|!StyleSheet} element A stylesheet element as returned by1386* installSafeStyleSheet.1387* @param {!goog.html.SafeStyleSheet} safeStyleSheet The new content of the1388* stylesheet.1389* @return {void}1390* @suppress {strictMissingProperties} Added to tighten compiler checks1391*/1392goog.style.setSafeStyleSheet = function(element, safeStyleSheet) {1393'use strict';1394var stylesString = goog.html.SafeStyleSheet.unwrap(safeStyleSheet);1395if (goog.userAgent.IE && element.cssText !== undefined) {1396// Adding the selectors individually caused the browser to hang if the1397// selector was invalid or there were CSS comments. Setting the cssText of1398// the style node works fine and ignores CSS that IE doesn't understand.1399// However IE >= 11 doesn't support cssText any more, so we make sure that1400// cssText is a defined property and otherwise fall back to innerHTML.1401/** @suppress {strictMissingProperties} Added to tighten compiler checks */1402element.cssText = stylesString;1403} else if (goog.global.trustedTypes) {1404goog.dom.setTextContent(/** @type {!Element} */ (element), stylesString);1405} else {1406// Setting textContent doesn't work in Safari, see b/29340337.1407/** @suppress {strictMissingProperties} Added to tighten compiler checks */1408element.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'use strict';1427var style = el.style;1428if (goog.userAgent.GECKO) {1429style.whiteSpace = '-moz-pre-wrap';1430} else {1431style.whiteSpace = 'pre-wrap';1432}1433};143414351436/**1437* Sets 'display: inline-block' for an element (cross-browser).1438* @param {Element} el Element to which the inline-block display style is to be1439* applied.1440* @return {void}1441* @see ../demos/inline_block_quirks.html1442* @see ../demos/inline_block_standards.html1443*/1444goog.style.setInlineBlock = function(el) {1445'use strict';1446var style = el.style;1447// Without position:relative, weirdness ensues. Just accept it and move on.1448style.position = 'relative';1449style.display = 'inline-block';1450};145114521453/**1454* Returns true if the element is using right to left (rtl) direction.1455* @param {Element} el The element to test.1456* @return {boolean} True for right to left, false for left to right.1457*/1458goog.style.isRightToLeft = function(el) {1459'use strict';1460return 'rtl' == goog.style.getStyle_(el, 'direction');1461};146214631464/**1465* The CSS style property corresponding to an element being1466* unselectable on the current browser platform (null if none).1467* Opera and IE instead use a DOM attribute 'unselectable'. MS Edge uses1468* the Webkit prefix.1469* @type {?string}1470* @private1471*/1472goog.style.unselectableStyle_ = goog.userAgent.GECKO ?1473'MozUserSelect' :1474goog.userAgent.WEBKIT || goog.userAgent.EDGE ? 'WebkitUserSelect' : null;147514761477/**1478* Returns true if the element is set to be unselectable, false otherwise.1479* Note that on some platforms (e.g. Mozilla), even if an element isn't set1480* to be unselectable, it will behave as such if any of its ancestors is1481* unselectable.1482* @param {Element} el Element to check.1483* @return {boolean} Whether the element is set to be unselectable.1484*/1485goog.style.isUnselectable = function(el) {1486'use strict';1487if (goog.style.unselectableStyle_) {1488return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';1489} else if (goog.userAgent.IE) {1490return el.getAttribute('unselectable') == 'on';1491}1492return false;1493};149414951496/**1497* Makes the element and its descendants selectable or unselectable. Note1498* that on some platforms (e.g. Mozilla), even if an element isn't set to1499* be unselectable, it will behave as such if any of its ancestors is1500* unselectable.1501* @param {Element} el The element to alter.1502* @param {boolean} unselectable Whether the element and its descendants1503* should be made unselectable.1504* @param {boolean=} opt_noRecurse Whether to only alter the element's own1505* selectable state, and leave its descendants alone; defaults to false.1506*/1507goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {1508'use strict';1509// TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?1510var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;1511var name = goog.style.unselectableStyle_;1512if (name) {1513// Add/remove the appropriate CSS style to/from the element and its1514// descendants.1515var value = unselectable ? 'none' : '';1516// MathML elements do not have a style property. Verify before setting.1517if (el.style) {1518el.style[name] = value;1519}1520if (descendants) {1521for (var i = 0, descendant; descendant = descendants[i]; i++) {1522if (descendant.style) {1523descendant.style[name] = value;1524}1525}1526}1527} else if (goog.userAgent.IE) {1528// Toggle the 'unselectable' attribute on the element and its descendants.1529var value = unselectable ? 'on' : '';1530el.setAttribute('unselectable', value);1531if (descendants) {1532for (var i = 0, descendant; descendant = descendants[i]; i++) {1533descendant.setAttribute('unselectable', value);1534}1535}1536}1537};153815391540/**1541* Gets the border box size for an element.1542* @param {Element} element The element to get the size for.1543* @return {!goog.math.Size} The border box size.1544*/1545goog.style.getBorderBoxSize = function(element) {1546'use strict';1547return new goog.math.Size(1548/** @type {!HTMLElement} */ (element).offsetWidth,1549/** @type {!HTMLElement} */ (element).offsetHeight);1550};155115521553/**1554* Sets the border box size of an element. This is potentially expensive in IE1555* if the document is CSS1Compat mode1556* @param {Element} element The element to set the size on.1557* @param {goog.math.Size} size The new size.1558*/1559goog.style.setBorderBoxSize = function(element, size) {1560'use strict';1561goog.style.setBoxSizingSize_(element, size, 'border-box');1562};156315641565/**1566* Gets the content box size for an element. This is potentially expensive in1567* all browsers.1568* @param {Element} element The element to get the size for.1569* @return {!goog.math.Size} The content box size.1570* @suppress {strictMissingProperties} Added to tighten compiler checks1571*/1572goog.style.getContentBoxSize = function(element) {1573'use strict';1574var doc = goog.dom.getOwnerDocument(element);1575var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;1576if (ieCurrentStyle && goog.dom.getDomHelper(doc).isCss1CompatMode() &&1577ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&1578!ieCurrentStyle.boxSizing) {1579// If IE in CSS1Compat mode than just use the width and height.1580// If we have a boxSizing then fall back on measuring the borders etc.1581/** @suppress {strictMissingProperties} Added to tighten compiler checks */1582var width = goog.style.getIePixelValue_(1583element, /** @type {string} */ (ieCurrentStyle.width), 'width',1584'pixelWidth');1585/** @suppress {strictMissingProperties} Added to tighten compiler checks */1586var height = goog.style.getIePixelValue_(1587element, /** @type {string} */ (ieCurrentStyle.height), 'height',1588'pixelHeight');1589return new goog.math.Size(width, height);1590} else {1591var borderBoxSize = goog.style.getBorderBoxSize(element);1592var paddingBox = goog.style.getPaddingBox(element);1593var borderBox = goog.style.getBorderBox(element);1594return new goog.math.Size(1595borderBoxSize.width - borderBox.left - paddingBox.left -1596paddingBox.right - borderBox.right,1597borderBoxSize.height - borderBox.top - paddingBox.top -1598paddingBox.bottom - borderBox.bottom);1599}1600};160116021603/**1604* Sets the content box size of an element. This is potentially expensive in IE1605* if the document is BackCompat mode.1606* @param {Element} element The element to set the size on.1607* @param {goog.math.Size} size The new size.1608*/1609goog.style.setContentBoxSize = function(element, size) {1610'use strict';1611goog.style.setBoxSizingSize_(element, size, 'content-box');1612};161316141615/**1616* Helper function that sets the box sizing as well as the width and height1617* @param {Element} element The element to set the size on.1618* @param {goog.math.Size} size The new size to set.1619* @param {string} boxSizing The box-sizing value.1620* @private1621*/1622goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {1623'use strict';1624var style = element.style;1625if (goog.userAgent.GECKO) {1626style.MozBoxSizing = boxSizing;1627} else if (goog.userAgent.WEBKIT) {1628style.WebkitBoxSizing = boxSizing;1629} else {1630// Includes IE8 and Opera 9.50+1631style.boxSizing = boxSizing;1632}16331634// Setting this to a negative value will throw an exception on IE1635// (and doesn't do anything different than setting it to 0).1636style.width = Math.max(size.width, 0) + 'px';1637style.height = Math.max(size.height, 0) + 'px';1638};163916401641/**1642* IE specific function that converts a non pixel unit to pixels.1643* @param {Element} element The element to convert the value for.1644* @param {string} value The current value as a string. The value must not be1645* ''.1646* @param {string} name The CSS property name to use for the converstion. This1647* should be 'left', 'top', 'width' or 'height'.1648* @param {string} pixelName The CSS pixel property name to use to get the1649* value in pixels.1650* @return {number} The value in pixels.1651* @private1652*/1653goog.style.getIePixelValue_ = function(element, value, name, pixelName) {1654'use strict';1655// Try if we already have a pixel value. IE does not do half pixels so we1656// only check if it matches a number followed by 'px'.1657if (/^\d+px?$/.test(value)) {1658return parseInt(value, 10);1659} else {1660var oldStyleValue = element.style[name];1661var oldRuntimeValue = element.runtimeStyle[name];1662// set runtime style to prevent changes1663element.runtimeStyle[name] = element.currentStyle[name];1664element.style[name] = value;1665var pixelValue = element.style[pixelName];1666// restore1667element.style[name] = oldStyleValue;1668element.runtimeStyle[name] = oldRuntimeValue;1669return +pixelValue;1670}1671};167216731674/**1675* Helper function for getting the pixel padding or margin for IE.1676* @param {Element} element The element to get the padding for.1677* @param {string} propName The property name.1678* @return {number} The pixel padding.1679* @private1680*/1681goog.style.getIePixelDistance_ = function(element, propName) {1682'use strict';1683var value = goog.style.getCascadedStyle(element, propName);1684return value ?1685goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') :16860;1687};168816891690/**1691* Gets the computed paddings or margins (on all sides) in pixels.1692* @param {Element} element The element to get the padding for.1693* @param {string} stylePrefix Pass 'padding' to retrieve the padding box,1694* or 'margin' to retrieve the margin box.1695* @return {!goog.math.Box} The computed paddings or margins.1696* @private1697*/1698goog.style.getBox_ = function(element, stylePrefix) {1699'use strict';1700if (goog.userAgent.IE) {1701var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');1702var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');1703var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');1704var bottom =1705goog.style.getIePixelDistance_(element, stylePrefix + 'Bottom');1706return new goog.math.Box(top, right, bottom, left);1707} else {1708// On non-IE browsers, getComputedStyle is always non-null.1709var left = goog.style.getComputedStyle(element, stylePrefix + 'Left');1710var right = goog.style.getComputedStyle(element, stylePrefix + 'Right');1711var top = goog.style.getComputedStyle(element, stylePrefix + 'Top');1712var bottom = goog.style.getComputedStyle(element, stylePrefix + 'Bottom');17131714// NOTE(arv): Gecko can return floating point numbers for the computed1715// style values.1716return new goog.math.Box(1717parseFloat(top), parseFloat(right), parseFloat(bottom),1718parseFloat(left));1719}1720};172117221723/**1724* Gets the computed paddings (on all sides) in pixels.1725* @param {Element} element The element to get the padding for.1726* @return {!goog.math.Box} The computed paddings.1727*/1728goog.style.getPaddingBox = function(element) {1729'use strict';1730return goog.style.getBox_(element, 'padding');1731};173217331734/**1735* Gets the computed margins (on all sides) in pixels.1736* @param {Element} element The element to get the margins for.1737* @return {!goog.math.Box} The computed margins.1738*/1739goog.style.getMarginBox = function(element) {1740'use strict';1741return goog.style.getBox_(element, 'margin');1742};174317441745/**1746* A map used to map the border width keywords to a pixel width.1747* @type {!Object}1748* @private1749*/1750goog.style.ieBorderWidthKeywords_ = {1751'thin': 2,1752'medium': 4,1753'thick': 61754};175517561757/**1758* Helper function for IE to get the pixel border.1759* @param {Element} element The element to get the pixel border for.1760* @param {string} prop The part of the property name.1761* @return {number} The value in pixels.1762* @private1763*/1764goog.style.getIePixelBorder_ = function(element, prop) {1765'use strict';1766if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {1767return 0;1768}1769var width = goog.style.getCascadedStyle(element, prop + 'Width');1770if (width in goog.style.ieBorderWidthKeywords_) {1771return goog.style.ieBorderWidthKeywords_[width];1772}1773return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');1774};177517761777/**1778* Gets the computed border widths (on all sides) in pixels1779* @param {Element} element The element to get the border widths for.1780* @return {!goog.math.Box} The computed border widths.1781*/1782goog.style.getBorderBox = function(element) {1783'use strict';1784if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {1785var left = goog.style.getIePixelBorder_(element, 'borderLeft');1786var right = goog.style.getIePixelBorder_(element, 'borderRight');1787var top = goog.style.getIePixelBorder_(element, 'borderTop');1788var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');1789return new goog.math.Box(top, right, bottom, left);1790} else {1791// On non-IE browsers, getComputedStyle is always non-null.1792var left = goog.style.getComputedStyle(element, 'borderLeftWidth');1793var right = goog.style.getComputedStyle(element, 'borderRightWidth');1794var top = goog.style.getComputedStyle(element, 'borderTopWidth');1795var bottom = goog.style.getComputedStyle(element, 'borderBottomWidth');17961797return new goog.math.Box(1798parseFloat(top), parseFloat(right), parseFloat(bottom),1799parseFloat(left));1800}1801};180218031804/**1805* Returns the font face applied to a given node. Opera and IE should return1806* the font actually displayed. Firefox returns the author's most-preferred1807* font (whether the browser is capable of displaying it or not.)1808* @param {Element} el The element whose font family is returned.1809* @return {string} The font family applied to el.1810*/1811goog.style.getFontFamily = function(el) {1812'use strict';1813var doc = goog.dom.getOwnerDocument(el);1814var font = '';1815// The moveToElementText method from the TextRange only works if the element1816// is attached to the owner document.1817if (doc.body.createTextRange && goog.dom.contains(doc, el)) {1818var range = doc.body.createTextRange();1819range.moveToElementText(el);18201821try {1822font = range.queryCommandValue('FontName');1823} catch (e) {1824// This is a workaround for a awkward exception.1825// On some IE, there is an exception coming from it.1826// The error description from this exception is:1827// This window has already been registered as a drop target1828// This is bogus description, likely due to a bug in ie.1829font = '';1830}1831}1832if (!font) {1833// Note if for some reason IE can't derive FontName with a TextRange, we1834// fallback to using currentStyle1835font = goog.style.getStyle_(el, 'fontFamily');1836}18371838// Firefox returns the applied font-family string (author's list of1839// preferred fonts.) We want to return the most-preferred font, in lieu of1840// the *actually* applied font.1841var fontsArray = font.split(',');1842if (fontsArray.length > 1) font = fontsArray[0];18431844// Sanitize for x-browser consistency:1845// Strip quotes because browsers aren't consistent with how they're1846// applied; Opera always encloses, Firefox sometimes, and IE never.1847return goog.string.stripQuotes(font, '"\'');1848};184918501851/**1852* Regular expression used for getLengthUnits.1853* @type {RegExp}1854* @private1855*/1856goog.style.lengthUnitRegex_ = /[^\d]+$/;185718581859/**1860* Returns the units used for a CSS length measurement.1861* @param {string} value A CSS length quantity.1862* @return {?string} The units of measurement.1863*/1864goog.style.getLengthUnits = function(value) {1865'use strict';1866var units = value.match(goog.style.lengthUnitRegex_);1867return units && units[0] || null;1868};186918701871/**1872* Map of absolute CSS length units1873* @type {!Object}1874* @private1875*/1876goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {1877'cm': 1,1878'in': 1,1879'mm': 1,1880'pc': 1,1881'pt': 11882};188318841885/**1886* Map of relative CSS length units that can be accurately converted to px1887* font-size values using getIePixelValue_. Only units that are defined in1888* relation to a font size are convertible (%, small, etc. are not).1889* @type {!Object}1890* @private1891*/1892goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {1893'em': 1,1894'ex': 11895};189618971898/**1899* Returns the font size, in pixels, of text in an element.1900* @param {Element} el The element whose font size is returned.1901* @return {number} The font size (in pixels).1902*/1903goog.style.getFontSize = function(el) {1904'use strict';1905var fontSize = goog.style.getStyle_(el, 'fontSize');1906var sizeUnits = goog.style.getLengthUnits(fontSize);1907if (fontSize && 'px' == sizeUnits) {1908// NOTE(user): This could be parseFloat instead, but IE doesn't return1909// decimal fractions in getStyle_ and Firefox reports the fractions, but1910// ignores them when rendering. Interestingly enough, when we force the1911// issue and size something to e.g., 50% of 25px, the browsers round in1912// opposite directions with Firefox reporting 12px and IE 13px. I punt.1913return parseInt(fontSize, 10);1914}19151916// In IE, we can convert absolute length units to a px value using1917// goog.style.getIePixelValue_. Units defined in relation to a font size1918// (em, ex) are applied relative to the element's parentNode and can also1919// be converted.1920if (goog.userAgent.IE) {1921if (String(sizeUnits) in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {1922return goog.style.getIePixelValue_(el, fontSize, 'left', 'pixelLeft');1923} else if (1924el.parentNode && el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&1925String(sizeUnits) in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {1926// Check the parent size - if it is the same it means the relative size1927// value is inherited and we therefore don't want to count it twice. If1928// it is different, this element either has explicit style or has a CSS1929// rule applying to it.1930var parentElement = /** @type {!Element} */ (el.parentNode);1931var parentSize = goog.style.getStyle_(parentElement, 'fontSize');1932return goog.style.getIePixelValue_(1933parentElement, fontSize == parentSize ? '1em' : fontSize, 'left',1934'pixelLeft');1935}1936}19371938// Sometimes we can't cleanly find the font size (some units relative to a1939// node's parent's font size are difficult: %, smaller et al), so we create1940// an invisible, absolutely-positioned span sized to be the height of an 'M'1941// rendered in its parent's (i.e., our target element's) font size. This is1942// the definition of CSS's font size attribute.1943var sizeElement = goog.dom.createDom(goog.dom.TagName.SPAN, {1944'style': 'visibility:hidden;position:absolute;' +1945'line-height:0;padding:0;margin:0;border:0;height:1em;'1946});1947goog.dom.appendChild(el, sizeElement);1948fontSize = sizeElement.offsetHeight;1949goog.dom.removeNode(sizeElement);19501951return fontSize;1952};195319541955/**1956* Parses a style attribute value. Converts CSS property names to camel case.1957* @param {string} value The style attribute value.1958* @return {!Object} Map of CSS properties to string values.1959*/1960goog.style.parseStyleAttribute = function(value) {1961'use strict';1962var result = {};1963value.split(/\s*;\s*/).forEach(function(pair) {1964'use strict';1965var keyValue = pair.match(/\s*([\w-]+)\s*:(.+)/);1966if (keyValue) {1967var styleName = keyValue[1];1968var styleValue = goog.string.trim(keyValue[2]);1969result[goog.string.toCamelCase(styleName.toLowerCase())] = styleValue;1970}1971});1972return result;1973};197419751976/**1977* Reverse of parseStyleAttribute; that is, takes a style object and returns the1978* corresponding attribute value. Converts camel case property names to proper1979* CSS selector names.1980* @param {Object} obj Map of CSS properties to values.1981* @return {string} The style attribute value.1982*/1983goog.style.toStyleAttribute = function(obj) {1984'use strict';1985var buffer = [];1986goog.object.forEach(obj, function(value, key) {1987'use strict';1988buffer.push(goog.string.toSelectorCase(key), ':', value, ';');1989});1990return buffer.join('');1991};199219931994/**1995* Sets CSS float property on an element.1996* @param {Element} el The element to set float property on.1997* @param {string} value The value of float CSS property to set on this element.1998*/1999goog.style.setFloat = function(el, value) {2000'use strict';2001el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;2002};200320042005/**2006* Gets value of explicitly-set float CSS property on an element.2007* @param {Element} el The element to get float property of.2008* @return {string} The value of explicitly-set float CSS property on this2009* element.2010*/2011goog.style.getFloat = function(el) {2012'use strict';2013return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';2014};201520162017/**2018* Returns the scroll bar width (represents the width of both horizontal2019* and vertical scroll).2020*2021* @param {string=} opt_className An optional class name (or names) to apply2022* to the invisible div created to measure the scrollbar. This is necessary2023* if some scrollbars are styled differently than others.2024* @return {number} The scroll bar width in px.2025*/2026goog.style.getScrollbarWidth = function(opt_className) {2027'use strict';2028// Add two hidden divs. The child div is larger than the parent and2029// forces scrollbars to appear on it.2030// Using overflow:scroll does not work consistently with scrollbars that2031// are styled with ::-webkit-scrollbar.2032var outerDiv = goog.dom.createElement(goog.dom.TagName.DIV);2033if (opt_className) {2034outerDiv.className = opt_className;2035}2036outerDiv.style.cssText = 'overflow:auto;' +2037'position:absolute;top:0;width:100px;height:100px';2038var innerDiv = goog.dom.createElement(goog.dom.TagName.DIV);2039goog.style.setSize(innerDiv, '200px', '200px');2040outerDiv.appendChild(innerDiv);2041goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);2042var width = outerDiv.offsetWidth - outerDiv.clientWidth;2043goog.dom.removeNode(outerDiv);2044return width;2045};204620472048/**2049* Regular expression to extract x and y translation components from a CSS2050* transform Matrix representation.2051*2052* @type {!RegExp}2053* @const2054* @private2055*/2056goog.style.MATRIX_TRANSLATION_REGEX_ = new RegExp(2057'matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +2058'[0-9\\.\\-]+, [0-9\\.\\-]+, ' +2059'([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');206020612062/**2063* Returns the x,y translation component of any CSS transforms applied to the2064* element, in pixels.2065*2066* @param {!Element} element The element to get the translation of.2067* @return {!goog.math.Coordinate} The CSS translation of the element in px.2068*/2069goog.style.getCssTranslation = function(element) {2070'use strict';2071var transform = goog.style.getComputedTransform(element);2072if (!transform) {2073return new goog.math.Coordinate(0, 0);2074}2075var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);2076if (!matches) {2077return new goog.math.Coordinate(0, 0);2078}2079return new goog.math.Coordinate(2080parseFloat(matches[1]), parseFloat(matches[2]));2081};208220832084