Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/style/style.js
4079 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for element styles.
9
*
10
* @see ../demos/inline_block_quirks.html
11
* @see ../demos/inline_block_standards.html
12
* @see ../demos/style_viewport.html
13
*/
14
15
goog.provide('goog.style');
16
17
18
goog.require('goog.asserts');
19
goog.require('goog.dom');
20
goog.require('goog.dom.NodeType');
21
goog.require('goog.dom.TagName');
22
goog.require('goog.dom.safe');
23
goog.require('goog.dom.vendor');
24
goog.require('goog.html.SafeStyleSheet');
25
goog.require('goog.math.Box');
26
goog.require('goog.math.Coordinate');
27
goog.require('goog.math.Rect');
28
goog.require('goog.math.Size');
29
goog.require('goog.object');
30
goog.require('goog.reflect');
31
goog.require('goog.string');
32
goog.require('goog.userAgent');
33
goog.requireType('goog.events.Event');
34
35
36
/**
37
* Sets a style value on an element.
38
*
39
* This function is not indended to patch issues in the browser's style
40
* handling, but to allow easy programmatic access to setting dash-separated
41
* style properties. An example is setting a batch of properties from a data
42
* object without overwriting old styles. When possible, use native APIs:
43
* elem.style.propertyKey = 'value' or (if obliterating old styles is fine)
44
* elem.style.cssText = 'property1: value1; property2: value2'.
45
*
46
* @param {Element} element The element to change.
47
* @param {string|Object} style If a string, a style name. If an object, a hash
48
* of style names to style values.
49
* @param {string|number|boolean=} opt_value If style was a string, then this
50
* should be the value.
51
* @return {void}
52
*/
53
goog.style.setStyle = function(element, style, opt_value) {
54
'use strict';
55
if (typeof style === 'string') {
56
goog.style.setStyle_(element, opt_value, style);
57
} else {
58
for (var key in style) {
59
goog.style.setStyle_(element, style[key], key);
60
}
61
}
62
};
63
64
65
/**
66
* Sets a style value on an element, with parameters swapped to work with
67
* `goog.object.forEach()`. Prepends a vendor-specific prefix when
68
* necessary.
69
* @param {Element} element The element to change.
70
* @param {string|number|boolean|undefined} value Style value.
71
* @param {string} style Style name.
72
* @private
73
*/
74
goog.style.setStyle_ = function(element, value, style) {
75
'use strict';
76
var propertyName = goog.style.getVendorJsStyleName_(element, style);
77
78
if (propertyName) {
79
// TODO(johnlenz): coerce to string?
80
element.style[propertyName] = /** @type {?} */ (value);
81
}
82
};
83
84
85
/**
86
* Style name cache that stores previous property name lookups.
87
*
88
* This is used by setStyle to speed up property lookups, entries look like:
89
* { StyleName: ActualPropertyName }
90
*
91
* @private {!Object<string, string>}
92
*/
93
goog.style.styleNameCache_ = {};
94
95
96
/**
97
* Returns the style property name in camel-case. If it does not exist and a
98
* vendor-specific version of the property does exist, then return the vendor-
99
* specific property name instead.
100
* @param {Element} element The element to change.
101
* @param {string} style Style name.
102
* @return {string} Vendor-specific style.
103
* @private
104
*/
105
goog.style.getVendorJsStyleName_ = function(element, style) {
106
'use strict';
107
var propertyName = goog.style.styleNameCache_[style];
108
if (!propertyName) {
109
var camelStyle = goog.string.toCamelCase(style);
110
propertyName = camelStyle;
111
112
if (element.style[camelStyle] === undefined) {
113
var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
114
goog.string.toTitleCase(camelStyle);
115
116
if (element.style[prefixedStyle] !== undefined) {
117
propertyName = prefixedStyle;
118
}
119
}
120
goog.style.styleNameCache_[style] = propertyName;
121
}
122
123
return propertyName;
124
};
125
126
127
/**
128
* Returns the style property name in CSS notation. If it does not exist and a
129
* vendor-specific version of the property does exist, then return the vendor-
130
* specific property name instead.
131
* @param {Element} element The element to change.
132
* @param {string} style Style name.
133
* @return {string} Vendor-specific style.
134
* @private
135
*/
136
goog.style.getVendorStyleName_ = function(element, style) {
137
'use strict';
138
var camelStyle = goog.string.toCamelCase(style);
139
140
if (element.style[camelStyle] === undefined) {
141
var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
142
goog.string.toTitleCase(camelStyle);
143
144
if (element.style[prefixedStyle] !== undefined) {
145
return goog.dom.vendor.getVendorPrefix() + '-' + style;
146
}
147
}
148
149
return style;
150
};
151
152
153
/**
154
* Retrieves an explicitly-set style value of a node. This returns '' if there
155
* isn't a style attribute on the element or if this style property has not been
156
* explicitly set in script.
157
*
158
* @param {Element} element Element to get style of.
159
* @param {string} property Property to get, css-style (if you have a camel-case
160
* property, use element.style[style]).
161
* @return {string} Style value.
162
*/
163
goog.style.getStyle = function(element, property) {
164
'use strict';
165
// element.style is '' for well-known properties which are unset.
166
// For for browser specific styles as 'filter' is undefined
167
// so we need to return '' explicitly to make it consistent across
168
// browsers.
169
var styleValue = element.style[goog.string.toCamelCase(property)];
170
171
// Using typeof here because of a bug in Safari 5.1, where this value
172
// was undefined, but === undefined returned false.
173
if (typeof(styleValue) !== 'undefined') {
174
return styleValue;
175
}
176
177
return element.style[goog.style.getVendorJsStyleName_(element, property)] ||
178
'';
179
};
180
181
182
/**
183
* Retrieves a computed style value of a node. It returns empty string if the
184
* value cannot be computed (which will be the case in Internet Explorer) or
185
* "none" if the property requested is an SVG one and it has not been
186
* explicitly set (firefox and webkit).
187
*
188
* @param {Element} element Element to get style of.
189
* @param {string} property Property to get (camel-case).
190
* @return {string} Style value.
191
*/
192
goog.style.getComputedStyle = function(element, property) {
193
'use strict';
194
var doc = goog.dom.getOwnerDocument(element);
195
if (doc.defaultView && doc.defaultView.getComputedStyle) {
196
var styles = doc.defaultView.getComputedStyle(element, null);
197
if (styles) {
198
// element.style[..] is undefined for browser specific styles
199
// as 'filter'.
200
return styles[property] || styles.getPropertyValue(property) || '';
201
}
202
}
203
204
return '';
205
};
206
207
208
/**
209
* Gets the cascaded style value of a node, or null if the value cannot be
210
* computed (only Internet Explorer can do this).
211
*
212
* @param {Element} element Element to get style of.
213
* @param {string} style Property to get (camel-case).
214
* @return {string} Style value.
215
*/
216
goog.style.getCascadedStyle = function(element, style) {
217
'use strict';
218
// TODO(nicksantos): This should be documented to return null. #fixTypes
219
return /** @type {string} */ (
220
element.currentStyle ? element.currentStyle[style] : null);
221
};
222
223
224
/**
225
* Cross-browser pseudo get computed style. It returns the computed style where
226
* available. If not available it tries the cascaded style value (IE
227
* currentStyle) and in worst case the inline style value. It shouldn't be
228
* called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for
229
* discussion.
230
*
231
* @param {Element} element Element to get style of.
232
* @param {string} style Property to get (must be camelCase, not css-style.).
233
* @return {string} Style value.
234
* @private
235
*/
236
goog.style.getStyle_ = function(element, style) {
237
'use strict';
238
return goog.style.getComputedStyle(element, style) ||
239
goog.style.getCascadedStyle(element, style) ||
240
(element.style && element.style[style]);
241
};
242
243
244
/**
245
* Retrieves the computed value of the box-sizing CSS attribute.
246
* Browser support: http://caniuse.com/css3-boxsizing.
247
* @param {!Element} element The element whose box-sizing to get.
248
* @return {?string} 'content-box', 'border-box' or 'padding-box'. null if
249
* box-sizing is not supported (IE7 and below).
250
*/
251
goog.style.getComputedBoxSizing = function(element) {
252
'use strict';
253
return goog.style.getStyle_(element, 'boxSizing') ||
254
goog.style.getStyle_(element, 'MozBoxSizing') ||
255
goog.style.getStyle_(element, 'WebkitBoxSizing') || null;
256
};
257
258
259
/**
260
* Retrieves the computed value of the position CSS attribute.
261
* @param {Element} element The element to get the position of.
262
* @return {string} Position value.
263
*/
264
goog.style.getComputedPosition = function(element) {
265
'use strict';
266
return goog.style.getStyle_(element, 'position');
267
};
268
269
270
/**
271
* Retrieves the computed background color string for a given element. The
272
* string returned is suitable for assigning to another element's
273
* background-color, but is not guaranteed to be in any particular string
274
* format. Accessing the color in a numeric form may not be possible in all
275
* browsers or with all input.
276
*
277
* If the background color for the element is defined as a hexadecimal value,
278
* the resulting string can be parsed by goog.color.parse in all supported
279
* browsers.
280
*
281
* Whether named colors like "red" or "lightblue" get translated into a
282
* format which can be parsed is browser dependent. Calling this function on
283
* transparent elements will return "transparent" in most browsers or
284
* "rgba(0, 0, 0, 0)" in WebKit.
285
* @param {Element} element The element to get the background color of.
286
* @return {string} The computed string value of the background color.
287
*/
288
goog.style.getBackgroundColor = function(element) {
289
'use strict';
290
return goog.style.getStyle_(element, 'backgroundColor');
291
};
292
293
294
/**
295
* Retrieves the computed value of the overflow-x CSS attribute.
296
* @param {Element} element The element to get the overflow-x of.
297
* @return {string} The computed string value of the overflow-x attribute.
298
*/
299
goog.style.getComputedOverflowX = function(element) {
300
'use strict';
301
return goog.style.getStyle_(element, 'overflowX');
302
};
303
304
305
/**
306
* Retrieves the computed value of the overflow-y CSS attribute.
307
* @param {Element} element The element to get the overflow-y of.
308
* @return {string} The computed string value of the overflow-y attribute.
309
*/
310
goog.style.getComputedOverflowY = function(element) {
311
'use strict';
312
return goog.style.getStyle_(element, 'overflowY');
313
};
314
315
316
/**
317
* Retrieves the computed value of the z-index CSS attribute.
318
* @param {Element} element The element to get the z-index of.
319
* @return {string|number} The computed value of the z-index attribute.
320
*/
321
goog.style.getComputedZIndex = function(element) {
322
'use strict';
323
return goog.style.getStyle_(element, 'zIndex');
324
};
325
326
327
/**
328
* Retrieves the computed value of the text-align CSS attribute.
329
* @param {Element} element The element to get the text-align of.
330
* @return {string} The computed string value of the text-align attribute.
331
*/
332
goog.style.getComputedTextAlign = function(element) {
333
'use strict';
334
return goog.style.getStyle_(element, 'textAlign');
335
};
336
337
338
/**
339
* Retrieves the computed value of the cursor CSS attribute.
340
* @param {Element} element The element to get the cursor of.
341
* @return {string} The computed string value of the cursor attribute.
342
*/
343
goog.style.getComputedCursor = function(element) {
344
'use strict';
345
return goog.style.getStyle_(element, 'cursor');
346
};
347
348
349
/**
350
* Retrieves the computed value of the CSS transform attribute.
351
* @param {Element} element The element to get the transform of.
352
* @return {string} The computed string representation of the transform matrix.
353
*/
354
goog.style.getComputedTransform = function(element) {
355
'use strict';
356
var property = goog.style.getVendorStyleName_(element, 'transform');
357
return goog.style.getStyle_(element, property) ||
358
goog.style.getStyle_(element, 'transform');
359
};
360
361
362
/**
363
* Sets the top/left values of an element. If no unit is specified in the
364
* argument then it will add px. The second argument is required if the first
365
* argument is a string or number and is ignored if the first argument
366
* is a coordinate.
367
* @param {Element} el Element to move.
368
* @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.
369
* @param {string|number=} opt_arg2 Top position.
370
* @return {void}
371
*/
372
goog.style.setPosition = function(el, arg1, opt_arg2) {
373
'use strict';
374
var x, y;
375
376
if (arg1 instanceof goog.math.Coordinate) {
377
x = arg1.x;
378
y = arg1.y;
379
} else {
380
x = arg1;
381
y = opt_arg2;
382
}
383
384
el.style.left = goog.style.getPixelStyleValue_(
385
/** @type {number|string} */ (x), false);
386
el.style.top = goog.style.getPixelStyleValue_(
387
/** @type {number|string} */ (y), false);
388
};
389
390
391
/**
392
* Gets the offsetLeft and offsetTop properties of an element and returns them
393
* in a Coordinate object
394
* @param {Element} element Element.
395
* @return {!goog.math.Coordinate} The position.
396
*/
397
goog.style.getPosition = function(element) {
398
'use strict';
399
return new goog.math.Coordinate(
400
/** @type {!HTMLElement} */ (element).offsetLeft,
401
/** @type {!HTMLElement} */ (element).offsetTop);
402
};
403
404
405
/**
406
* Returns the viewport element for a particular document
407
* @param {Node=} opt_node DOM node (Document is OK) to get the viewport element
408
* of.
409
* @return {Element} document.documentElement or document.body.
410
*/
411
goog.style.getClientViewportElement = function(opt_node) {
412
'use strict';
413
var doc;
414
if (opt_node) {
415
doc = goog.dom.getOwnerDocument(opt_node);
416
} else {
417
doc = goog.dom.getDocument();
418
}
419
420
// In old IE versions the document.body represented the viewport
421
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
422
!goog.dom.getDomHelper(doc).isCss1CompatMode()) {
423
return doc.body;
424
}
425
return doc.documentElement;
426
};
427
428
429
/**
430
* Calculates the viewport coordinates relative to the page/document
431
* containing the node. The viewport may be the browser viewport for
432
* non-iframe document, or the iframe container for iframe'd document.
433
* @param {!Document} doc The document to use as the reference point.
434
* @return {!goog.math.Coordinate} The page offset of the viewport.
435
*/
436
goog.style.getViewportPageOffset = function(doc) {
437
'use strict';
438
var body = doc.body;
439
var documentElement = doc.documentElement;
440
var scrollLeft = body.scrollLeft || documentElement.scrollLeft;
441
var scrollTop = body.scrollTop || documentElement.scrollTop;
442
return new goog.math.Coordinate(scrollLeft, scrollTop);
443
};
444
445
446
/**
447
* Gets the client rectangle of the DOM element.
448
*
449
* getBoundingClientRect is part of a new CSS object model draft (with a
450
* long-time presence in IE), replacing the error-prone parent offset
451
* computation and the now-deprecated Gecko getBoxObjectFor.
452
*
453
* This utility patches common browser bugs in getBoundingClientRect. It
454
* will fail if getBoundingClientRect is unsupported.
455
*
456
* If the element is not in the DOM, the result is undefined, and an error may
457
* be thrown depending on user agent.
458
*
459
* @param {!Element} el The element whose bounding rectangle is being queried.
460
* @return {!Object} A native bounding rectangle with numerical left, top,
461
* right, and bottom. Reported by Firefox to be of object type ClientRect.
462
* @private
463
*/
464
goog.style.getBoundingClientRect_ = function(el) {
465
'use strict';
466
try {
467
return el.getBoundingClientRect();
468
} catch (e) {
469
// In IE, calling getBoundingClientRect on an orphan element raises an
470
// "Unspecified Error". All other browsers return zeros.
471
return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
472
}
473
};
474
475
476
/**
477
* Returns the first parent that could affect the position of a given element.
478
* @param {Element} element The element to get the offset parent for.
479
* @return {Element} The first offset parent or null if one cannot be found.
480
* @suppress {strictMissingProperties} Added to tighten compiler checks
481
*/
482
goog.style.getOffsetParent = function(element) {
483
'use strict';
484
// element.offsetParent does the right thing in IE7 and below. In other
485
// browsers it only includes elements with position absolute, relative or
486
// fixed, not elements with overflow set to auto or scroll.
487
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {
488
goog.asserts.assert(element && 'offsetParent' in element);
489
return element.offsetParent;
490
}
491
492
var doc = goog.dom.getOwnerDocument(element);
493
var positionStyle = goog.style.getStyle_(element, 'position');
494
var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
495
for (var parent = element.parentNode; parent && parent != doc;
496
parent = parent.parentNode) {
497
// Skip shadowDOM roots.
498
if (parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT && parent.host) {
499
// Cast because the assignment is not type safe, and without a cast we
500
// start typing parent loosely and get bad disambiguation.
501
parent = /** @type {!Element} */ (parent.host);
502
}
503
positionStyle =
504
goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');
505
skipStatic = skipStatic && positionStyle == 'static' &&
506
parent != doc.documentElement && parent != doc.body;
507
if (!skipStatic &&
508
(parent.scrollWidth > parent.clientWidth ||
509
parent.scrollHeight > parent.clientHeight ||
510
positionStyle == 'fixed' || positionStyle == 'absolute' ||
511
positionStyle == 'relative')) {
512
return /** @type {!Element} */ (parent);
513
}
514
}
515
return null;
516
};
517
518
519
/**
520
* Calculates and returns the visible rectangle for a given element. Returns a
521
* box describing the visible portion of the nearest scrollable offset ancestor.
522
* Coordinates are given relative to the document.
523
*
524
* @param {Element} element Element to get the visible rect for.
525
* @return {goog.math.Box} Bounding elementBox describing the visible rect or
526
* null if scrollable ancestor isn't inside the visible viewport.
527
*/
528
goog.style.getVisibleRectForElement = function(element) {
529
'use strict';
530
var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
531
var dom = goog.dom.getDomHelper(element);
532
var body = dom.getDocument().body;
533
var documentElement = dom.getDocument().documentElement;
534
var scrollEl = dom.getDocumentScrollElement();
535
536
// Determine the size of the visible rect by climbing the dom accounting for
537
// all scrollable containers.
538
for (var el = element; el = goog.style.getOffsetParent(el);) {
539
// clientWidth is zero for inline block elements in IE.
540
// on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0
541
if ((!goog.userAgent.IE || el.clientWidth != 0) &&
542
(!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&
543
// body may have overflow set on it, yet we still get the entire
544
// viewport. In some browsers, el.offsetParent may be
545
// document.documentElement, so check for that too.
546
(el != body && el != documentElement &&
547
goog.style.getStyle_(el, 'overflow') != 'visible')) {
548
var pos = goog.style.getPageOffset(el);
549
var client = goog.style.getClientLeftTop(el);
550
pos.x += client.x;
551
pos.y += client.y;
552
553
visibleRect.top = Math.max(visibleRect.top, pos.y);
554
visibleRect.right = Math.min(visibleRect.right, pos.x + el.clientWidth);
555
visibleRect.bottom =
556
Math.min(visibleRect.bottom, pos.y + el.clientHeight);
557
visibleRect.left = Math.max(visibleRect.left, pos.x);
558
}
559
}
560
561
// Clip by window's viewport.
562
var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
563
visibleRect.left = Math.max(visibleRect.left, scrollX);
564
visibleRect.top = Math.max(visibleRect.top, scrollY);
565
var winSize = dom.getViewportSize();
566
visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
567
visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
568
return visibleRect.top >= 0 && visibleRect.left >= 0 &&
569
visibleRect.bottom > visibleRect.top &&
570
visibleRect.right > visibleRect.left ?
571
visibleRect :
572
null;
573
};
574
575
576
/**
577
* Calculate the scroll position of `container` with the minimum amount so
578
* that the content and the borders of the given `element` become visible.
579
* If the element is bigger than the container, its top left corner will be
580
* aligned as close to the container's top left corner as possible.
581
*
582
* @param {Element} element The element to make visible.
583
* @param {Element=} opt_container The container to scroll. If not set, then the
584
* document scroll element will be used.
585
* @param {boolean=} opt_center Whether to center the element in the container.
586
* Defaults to false.
587
* @return {!goog.math.Coordinate} The new scroll position of the container,
588
* in form of goog.math.Coordinate(scrollLeft, scrollTop).
589
*/
590
goog.style.getContainerOffsetToScrollInto = function(
591
element, opt_container, opt_center) {
592
'use strict';
593
var container = opt_container || goog.dom.getDocumentScrollElement();
594
// Absolute position of the element's border's top left corner.
595
var elementPos = goog.style.getPageOffset(element);
596
// Absolute position of the container's border's top left corner.
597
var containerPos = goog.style.getPageOffset(container);
598
var containerBorder = goog.style.getBorderBox(container);
599
if (container == goog.dom.getDocumentScrollElement()) {
600
// The element position is calculated based on the page offset, and the
601
// document scroll element holds the scroll position within the page. We can
602
// use the scroll position to calculate the relative position from the
603
// element.
604
var relX = elementPos.x - container.scrollLeft;
605
var relY = elementPos.y - container.scrollTop;
606
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
607
// In older versions of IE getPageOffset(element) does not include the
608
// container border so it has to be added to accommodate.
609
relX += containerBorder.left;
610
relY += containerBorder.top;
611
}
612
} else {
613
// Relative pos. of the element's border box to the container's content box.
614
var relX = elementPos.x - containerPos.x - containerBorder.left;
615
var relY = elementPos.y - containerPos.y - containerBorder.top;
616
}
617
// How much the element can move in the container, i.e. the difference between
618
// the element's bottom-right-most and top-left-most position where it's
619
// fully visible.
620
var elementSize = goog.style.getSizeWithDisplay_(element);
621
var spaceX = container.clientWidth - elementSize.width;
622
var spaceY = container.clientHeight - elementSize.height;
623
var scrollLeft = container.scrollLeft;
624
var scrollTop = container.scrollTop;
625
if (opt_center) {
626
// All browsers round non-integer scroll positions down.
627
scrollLeft += relX - spaceX / 2;
628
scrollTop += relY - spaceY / 2;
629
} else {
630
// This formula was designed to give the correct scroll values in the
631
// following cases:
632
// - element is higher than container (spaceY < 0) => scroll down by relY
633
// - element is not higher that container (spaceY >= 0):
634
// - it is above container (relY < 0) => scroll up by abs(relY)
635
// - it is below container (relY > spaceY) => scroll down by relY - spaceY
636
// - it is in the container => don't scroll
637
scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));
638
scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
639
}
640
return new goog.math.Coordinate(scrollLeft, scrollTop);
641
};
642
643
644
/**
645
* Changes the scroll position of `container` with the minimum amount so
646
* that the content and the borders of the given `element` become visible.
647
* If the element is bigger than the container, its top left corner will be
648
* aligned as close to the container's top left corner as possible.
649
*
650
* @param {Element} element The element to make visible.
651
* @param {Element=} opt_container The container to scroll. If not set, then the
652
* document scroll element will be used.
653
* @param {boolean=} opt_center Whether to center the element in the container.
654
* Defaults to false.
655
*/
656
goog.style.scrollIntoContainerView = function(
657
element, opt_container, opt_center) {
658
'use strict';
659
var container = opt_container || goog.dom.getDocumentScrollElement();
660
var offset =
661
goog.style.getContainerOffsetToScrollInto(element, container, opt_center);
662
container.scrollLeft = offset.x;
663
container.scrollTop = offset.y;
664
};
665
666
667
/**
668
* Returns clientLeft (width of the left border and, if the directionality is
669
* right to left, the vertical scrollbar) and clientTop as a coordinate object.
670
*
671
* @param {Element} el Element to get clientLeft for.
672
* @return {!goog.math.Coordinate} Client left and top.
673
*/
674
goog.style.getClientLeftTop = function(el) {
675
'use strict';
676
return new goog.math.Coordinate(el.clientLeft, el.clientTop);
677
};
678
679
680
/**
681
* Returns a Coordinate object relative to the top-left of the HTML document.
682
* Implemented as a single function to save having to do two recursive loops in
683
* opera and safari just to get both coordinates. If you just want one value do
684
* use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but
685
* note if you call both those methods the tree will be analysed twice.
686
*
687
* @param {Element} el Element to get the page offset for.
688
* @return {!goog.math.Coordinate} The page offset.
689
*/
690
goog.style.getPageOffset = function(el) {
691
'use strict';
692
var doc = goog.dom.getOwnerDocument(el);
693
// TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.
694
goog.asserts.assertObject(el, 'Parameter is required');
695
696
// NOTE(arv): If element is hidden (display none or disconnected or any the
697
// ancestors are hidden) we get (0,0) by default but we still do the
698
// accumulation of scroll position.
699
700
// TODO(arv): Should we check if the node is disconnected and in that case
701
// return (0,0)?
702
703
var pos = new goog.math.Coordinate(0, 0);
704
var viewportElement = goog.style.getClientViewportElement(doc);
705
if (el == viewportElement) {
706
// viewport is always at 0,0 as that defined the coordinate system for this
707
// function - this avoids special case checks in the code below
708
return pos;
709
}
710
711
var box = goog.style.getBoundingClientRect_(el);
712
// Must add the scroll coordinates in to get the absolute page offset
713
// of element since getBoundingClientRect returns relative coordinates to
714
// the viewport.
715
var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();
716
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
717
pos.x = box.left + scrollCoord.x;
718
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
719
pos.y = box.top + scrollCoord.y;
720
721
return pos;
722
};
723
724
725
/**
726
* Returns the left coordinate of an element relative to the HTML document
727
* @param {Element} el Elements.
728
* @return {number} The left coordinate.
729
*/
730
goog.style.getPageOffsetLeft = function(el) {
731
'use strict';
732
return goog.style.getPageOffset(el).x;
733
};
734
735
736
/**
737
* Returns the top coordinate of an element relative to the HTML document
738
* @param {Element} el Elements.
739
* @return {number} The top coordinate.
740
*/
741
goog.style.getPageOffsetTop = function(el) {
742
'use strict';
743
return goog.style.getPageOffset(el).y;
744
};
745
746
747
/**
748
* Returns a Coordinate object relative to the top-left of an HTML document
749
* in an ancestor frame of this element. Used for measuring the position of
750
* an element inside a frame relative to a containing frame.
751
*
752
* @param {Element} el Element to get the page offset for.
753
* @param {Window} relativeWin The window to measure relative to. If relativeWin
754
* is not in the ancestor frame chain of the element, we measure relative to
755
* the top-most window.
756
* @return {!goog.math.Coordinate} The page offset.
757
*/
758
goog.style.getFramedPageOffset = function(el, relativeWin) {
759
'use strict';
760
var position = new goog.math.Coordinate(0, 0);
761
762
// Iterate up the ancestor frame chain, keeping track of the current window
763
// and the current element in that window.
764
var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));
765
766
// MS Edge throws when accessing "parent" if el's containing iframe has been
767
// deleted.
768
if (!goog.reflect.canAccessProperty(currentWin, 'parent')) {
769
return position;
770
}
771
772
var currentEl = el;
773
do {
774
// if we're at the top window, we want to get the page offset.
775
// if we're at an inner frame, we only want to get the window position
776
// so that we can determine the actual page offset in the context of
777
// the outer window.
778
var offset = currentWin == relativeWin ?
779
goog.style.getPageOffset(currentEl) :
780
goog.style.getClientPositionForElement_(goog.asserts.assert(currentEl));
781
782
position.x += offset.x;
783
position.y += offset.y;
784
} while (currentWin && currentWin != relativeWin &&
785
currentWin != currentWin.parent &&
786
(currentEl = currentWin.frameElement) &&
787
(currentWin = currentWin.parent));
788
789
return position;
790
};
791
792
793
/**
794
* Translates the specified rect relative to origBase page, for newBase page.
795
* If origBase and newBase are the same, this function does nothing.
796
*
797
* @param {goog.math.Rect} rect The source rectangle relative to origBase page,
798
* and it will have the translated result.
799
* @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.
800
* @param {goog.dom.DomHelper} newBase The DomHelper for the resultant
801
* coordinate. This must be a DOM for an ancestor frame of origBase
802
* or the same as origBase.
803
*/
804
goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {
805
'use strict';
806
if (origBase.getDocument() != newBase.getDocument()) {
807
var body = origBase.getDocument().body;
808
var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());
809
810
// Adjust Body's margin.
811
pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));
812
813
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
814
!origBase.isCss1CompatMode()) {
815
pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());
816
}
817
818
rect.left += pos.x;
819
rect.top += pos.y;
820
}
821
};
822
823
824
/**
825
* Returns the position of an element relative to another element in the
826
* document. A relative to B
827
* @param {Element|Event|goog.events.Event} a Element or mouse event whose
828
* position we're calculating.
829
* @param {Element|Event|goog.events.Event} b Element or mouse event position
830
* is relative to.
831
* @return {!goog.math.Coordinate} The relative position.
832
*/
833
goog.style.getRelativePosition = function(a, b) {
834
'use strict';
835
var ap = goog.style.getClientPosition(a);
836
var bp = goog.style.getClientPosition(b);
837
return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);
838
};
839
840
841
/**
842
* Returns the position of the event or the element's border box relative to
843
* the client viewport.
844
* @param {!Element} el Element whose position to get.
845
* @return {!goog.math.Coordinate} The position.
846
* @private
847
* @suppress {strictMissingProperties} Added to tighten compiler checks
848
*/
849
goog.style.getClientPositionForElement_ = function(el) {
850
'use strict';
851
var box = goog.style.getBoundingClientRect_(el);
852
return new goog.math.Coordinate(box.left, box.top);
853
};
854
855
856
/**
857
* Returns the position of the event or the element's border box relative to
858
* the client viewport. If an event is passed, and if this event is a "touch"
859
* event, then the position of the first changedTouches will be returned.
860
* @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.
861
* @return {!goog.math.Coordinate} The position.
862
* @suppress {strictMissingProperties} Added to tighten compiler checks
863
*/
864
goog.style.getClientPosition = function(el) {
865
'use strict';
866
goog.asserts.assert(el);
867
if (el.nodeType == goog.dom.NodeType.ELEMENT) {
868
return goog.style.getClientPositionForElement_(
869
/** @type {!Element} */ (el));
870
} else {
871
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
872
var targetEvent = el.changedTouches ? el.changedTouches[0] : el;
873
return new goog.math.Coordinate(targetEvent.clientX, targetEvent.clientY);
874
}
875
};
876
877
878
/**
879
* Moves an element to the given coordinates relative to the client viewport.
880
* @param {Element} el Absolutely positioned element to set page offset for.
881
* It must be in the document.
882
* @param {number|goog.math.Coordinate} x Left position of the element's margin
883
* box or a coordinate object.
884
* @param {number=} opt_y Top position of the element's margin box.
885
* @return {void}
886
*/
887
goog.style.setPageOffset = function(el, x, opt_y) {
888
'use strict';
889
// Get current pageoffset
890
var cur = goog.style.getPageOffset(el);
891
892
if (x instanceof goog.math.Coordinate) {
893
opt_y = x.y;
894
x = x.x;
895
}
896
897
// NOTE(arv): We cannot allow strings for x and y. We could but that would
898
// require us to manually transform between different units
899
900
// Work out deltas
901
var dx = goog.asserts.assertNumber(x) - cur.x;
902
var dy = Number(opt_y) - cur.y;
903
904
// Set position to current left/top + delta
905
goog.style.setPosition(
906
el, /** @type {!HTMLElement} */ (el).offsetLeft + dx,
907
/** @type {!HTMLElement} */ (el).offsetTop + dy);
908
};
909
910
911
/**
912
* Sets the width/height values of an element. If an argument is numeric,
913
* or a goog.math.Size is passed, it is assumed to be pixels and will add
914
* 'px' after converting it to an integer in string form. (This just sets the
915
* CSS width and height properties so it might set content-box or border-box
916
* size depending on the box model the browser is using.)
917
*
918
* @param {Element} element Element to set the size of.
919
* @param {string|number|goog.math.Size} w Width of the element, or a
920
* size object.
921
* @param {string|number=} opt_h Height of the element. Required if w is not a
922
* size object.
923
* @return {void}
924
*/
925
goog.style.setSize = function(element, w, opt_h) {
926
'use strict';
927
var h;
928
if (w instanceof goog.math.Size) {
929
h = w.height;
930
w = w.width;
931
} else {
932
if (opt_h == undefined) {
933
throw new Error('missing height argument');
934
}
935
h = opt_h;
936
}
937
938
goog.style.setWidth(element, /** @type {string|number} */ (w));
939
goog.style.setHeight(element, h);
940
};
941
942
943
/**
944
* Helper function to create a string to be set into a pixel-value style
945
* property of an element. Can round to the nearest integer value.
946
*
947
* @param {string|number} value The style value to be used. If a number,
948
* 'px' will be appended, otherwise the value will be applied directly.
949
* @param {boolean} round Whether to round the nearest integer (if property
950
* is a number).
951
* @return {string} The string value for the property.
952
* @private
953
*/
954
goog.style.getPixelStyleValue_ = function(value, round) {
955
'use strict';
956
if (typeof value == 'number') {
957
value = (round ? Math.round(value) : value) + 'px';
958
}
959
960
return value;
961
};
962
963
964
/**
965
* Set the height of an element. Sets the element's style property.
966
* @param {Element} element Element to set the height of.
967
* @param {string|number} height The height value to set. If a number, 'px'
968
* will be appended, otherwise the value will be applied directly.
969
*/
970
goog.style.setHeight = function(element, height) {
971
'use strict';
972
element.style.height = goog.style.getPixelStyleValue_(height, true);
973
};
974
975
976
/**
977
* Set the width of an element. Sets the element's style property.
978
* @param {Element} element Element to set the width of.
979
* @param {string|number} width The width value to set. If a number, 'px'
980
* will be appended, otherwise the value will be applied directly.
981
*/
982
goog.style.setWidth = function(element, width) {
983
'use strict';
984
element.style.width = goog.style.getPixelStyleValue_(width, true);
985
};
986
987
988
/**
989
* Gets the height and width of an element, even if its display is none.
990
*
991
* Specifically, this returns the height and width of the border box,
992
* irrespective of the box model in effect.
993
*
994
* Note that this function does not take CSS transforms into account. Please see
995
* `goog.style.getTransformedSize`.
996
* @param {Element} element Element to get size of.
997
* @return {!goog.math.Size} Object with width/height properties.
998
*/
999
goog.style.getSize = function(element) {
1000
'use strict';
1001
return goog.style.evaluateWithTemporaryDisplay_(
1002
goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));
1003
};
1004
1005
1006
/**
1007
* Call `fn` on `element` such that `element`'s dimensions are
1008
* accurate when it's passed to `fn`.
1009
* @param {function(!Element): T} fn Function to call with `element` as
1010
* an argument after temporarily changing `element`'s display such
1011
* that its dimensions are accurate.
1012
* @param {!Element} element Element (which may have display none) to use as
1013
* argument to `fn`.
1014
* @return {T} Value returned by calling `fn` with `element`.
1015
* @template T
1016
* @private
1017
*/
1018
goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {
1019
'use strict';
1020
if (goog.style.getStyle_(element, 'display') != 'none') {
1021
return fn(element);
1022
}
1023
1024
var style = element.style;
1025
var originalDisplay = style.display;
1026
var originalVisibility = style.visibility;
1027
var originalPosition = style.position;
1028
1029
style.visibility = 'hidden';
1030
style.position = 'absolute';
1031
style.display = 'inline';
1032
1033
var retVal = fn(element);
1034
1035
style.display = originalDisplay;
1036
style.position = originalPosition;
1037
style.visibility = originalVisibility;
1038
1039
return retVal;
1040
};
1041
1042
1043
/**
1044
* Gets the height and width of an element when the display is not none.
1045
* @param {Element} element Element to get size of.
1046
* @return {!goog.math.Size} Object with width/height properties.
1047
* @private
1048
* @suppress {strictMissingProperties} Added to tighten compiler checks
1049
*/
1050
goog.style.getSizeWithDisplay_ = function(element) {
1051
'use strict';
1052
var offsetWidth = /** @type {!HTMLElement} */ (element).offsetWidth;
1053
var offsetHeight = /** @type {!HTMLElement} */ (element).offsetHeight;
1054
var webkitOffsetsZero =
1055
goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;
1056
if ((offsetWidth === undefined || webkitOffsetsZero) &&
1057
element.getBoundingClientRect) {
1058
// Fall back to calling getBoundingClientRect when offsetWidth or
1059
// offsetHeight are not defined, or when they are zero in WebKit browsers.
1060
// This makes sure that we return for the correct size for SVG elements, but
1061
// will still return 0 on Webkit prior to 534.8, see
1062
// http://trac.webkit.org/changeset/67252.
1063
var clientRect = goog.style.getBoundingClientRect_(element);
1064
return new goog.math.Size(
1065
clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
1066
}
1067
return new goog.math.Size(offsetWidth, offsetHeight);
1068
};
1069
1070
1071
/**
1072
* Gets the height and width of an element, post transform, even if its display
1073
* is none.
1074
*
1075
* This is like `goog.style.getSize`, except:
1076
* <ol>
1077
* <li>Takes webkitTransforms such as rotate and scale into account.
1078
* <li>Will return null if `element` doesn't respond to
1079
* `getBoundingClientRect`.
1080
* <li>Currently doesn't make sense on non-WebKit browsers which don't support
1081
* webkitTransforms.
1082
* </ol>
1083
* @param {!Element} element Element to get size of.
1084
* @return {goog.math.Size} Object with width/height properties.
1085
* @suppress {strictMissingProperties} Added to tighten compiler checks
1086
*/
1087
goog.style.getTransformedSize = function(element) {
1088
'use strict';
1089
if (!element.getBoundingClientRect) {
1090
return null;
1091
}
1092
1093
var clientRect = goog.style.evaluateWithTemporaryDisplay_(
1094
goog.style.getBoundingClientRect_, element);
1095
return new goog.math.Size(
1096
clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
1097
};
1098
1099
1100
/**
1101
* Returns a bounding rectangle for a given element in page space.
1102
* @param {Element} element Element to get bounds of. Must not be display none.
1103
* @return {!goog.math.Rect} Bounding rectangle for the element.
1104
*/
1105
goog.style.getBounds = function(element) {
1106
'use strict';
1107
var o = goog.style.getPageOffset(element);
1108
var s = goog.style.getSize(element);
1109
return new goog.math.Rect(o.x, o.y, s.width, s.height);
1110
};
1111
1112
1113
/**
1114
* Converts a CSS selector in the form style-property to styleProperty.
1115
* @param {*} selector CSS Selector.
1116
* @return {string} Camel case selector.
1117
* @deprecated Use goog.string.toCamelCase instead.
1118
*/
1119
goog.style.toCamelCase = function(selector) {
1120
'use strict';
1121
return goog.string.toCamelCase(String(selector));
1122
};
1123
1124
1125
/**
1126
* Converts a CSS selector in the form styleProperty to style-property.
1127
* @param {string} selector Camel case selector.
1128
* @return {string} Selector cased.
1129
* @deprecated Use goog.string.toSelectorCase instead.
1130
*/
1131
goog.style.toSelectorCase = function(selector) {
1132
'use strict';
1133
return goog.string.toSelectorCase(selector);
1134
};
1135
1136
1137
/**
1138
* Gets the opacity of a node (x-browser). This gets the inline style opacity
1139
* of the node, and does not take into account the cascaded or the computed
1140
* style for this node.
1141
* @param {Element} el Element whose opacity has to be found.
1142
* @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}
1143
* if the opacity is not set.
1144
*/
1145
goog.style.getOpacity = function(el) {
1146
'use strict';
1147
goog.asserts.assert(el);
1148
var style = el.style;
1149
var result = '';
1150
if ('opacity' in style) {
1151
result = style.opacity;
1152
} else if ('MozOpacity' in style) {
1153
result = style.MozOpacity;
1154
} else if ('filter' in style) {
1155
var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);
1156
if (match) {
1157
result = String(match[1] / 100);
1158
}
1159
}
1160
return result == '' ? result : Number(result);
1161
};
1162
1163
1164
/**
1165
* Sets the opacity of a node (x-browser).
1166
* @param {Element} el Elements whose opacity has to be set.
1167
* @param {number|string} alpha Opacity between 0 and 1 or an empty string
1168
* {@code ''} to clear the opacity.
1169
* @return {void}
1170
*/
1171
goog.style.setOpacity = function(el, alpha) {
1172
'use strict';
1173
goog.asserts.assert(el);
1174
var style = el.style;
1175
if ('opacity' in style) {
1176
style.opacity = alpha;
1177
} else if ('MozOpacity' in style) {
1178
style.MozOpacity = alpha;
1179
} else if ('filter' in style) {
1180
// TODO(arv): Overwriting the filter might have undesired side effects.
1181
if (alpha === '') {
1182
/**
1183
* @suppress {strictMissingProperties} Added to tighten compiler checks
1184
*/
1185
style.filter = '';
1186
} else {
1187
/**
1188
* @suppress {strictMissingProperties} Added to tighten compiler checks
1189
*/
1190
style.filter = 'alpha(opacity=' + (Number(alpha) * 100) + ')';
1191
}
1192
}
1193
};
1194
1195
1196
/**
1197
* Sets the background of an element to a transparent image in a browser-
1198
* independent manner.
1199
*
1200
* This function does not support repeating backgrounds or alternate background
1201
* positions to match the behavior of Internet Explorer. It also does not
1202
* support sizingMethods other than crop since they cannot be replicated in
1203
* browsers other than Internet Explorer.
1204
*
1205
* @param {Element} el The element to set background on.
1206
* @param {string} src The image source URL.
1207
* @return {void}
1208
*/
1209
goog.style.setTransparentBackgroundImage = function(el, src) {
1210
'use strict';
1211
var style = el.style;
1212
// It is safe to use the style.filter in IE only. In Safari 'filter' is in
1213
// style object but access to style.filter causes it to throw an exception.
1214
// Note: IE8 supports images with an alpha channel.
1215
1216
// Set style properties individually instead of using background shorthand
1217
// to prevent overwriting a pre-existing background color.
1218
style.backgroundImage = 'url(' + src + ')';
1219
style.backgroundPosition = 'top left';
1220
style.backgroundRepeat = 'no-repeat';
1221
};
1222
1223
1224
/**
1225
* Clears the background image of an element in a browser independent manner.
1226
* @param {Element} el The element to clear background image for.
1227
*/
1228
goog.style.clearTransparentBackgroundImage = function(el) {
1229
'use strict';
1230
var style = el.style;
1231
if ('filter' in style) {
1232
// See TODO in setOpacity.
1233
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
1234
style.filter = '';
1235
} else {
1236
// Set style properties individually instead of using background shorthand
1237
// to prevent overwriting a pre-existing background color.
1238
style.backgroundImage = 'none';
1239
}
1240
};
1241
1242
1243
/**
1244
* Shows or hides an element from the page. Hiding the element is done by
1245
* setting the display property to "none", removing the element from the
1246
* rendering hierarchy so it takes up no space. To show the element, the default
1247
* inherited display property is restored (defined either in stylesheets or by
1248
* the browser's default style rules.)
1249
*
1250
* Caveat 1: if the inherited display property for the element is set to "none"
1251
* by the stylesheets, that is the property that will be restored by a call to
1252
* showElement(), effectively toggling the display between "none" and "none".
1253
*
1254
* Caveat 2: if the element display style is set inline (by setting either
1255
* element.style.display or a style attribute in the HTML), a call to
1256
* showElement will clear that setting and defer to the inherited style in the
1257
* stylesheet.
1258
* @param {Element} el Element to show or hide.
1259
* @param {*} display True to render the element in its default style,
1260
* false to disable rendering the element.
1261
* @return {void}
1262
* @deprecated Use goog.style.setElementShown instead.
1263
*/
1264
goog.style.showElement = function(el, display) {
1265
'use strict';
1266
goog.style.setElementShown(el, display);
1267
};
1268
1269
1270
/**
1271
* Shows or hides an element from the page. Hiding the element is done by
1272
* setting the display property to "none", removing the element from the
1273
* rendering hierarchy so it takes up no space. To show the element, the default
1274
* inherited display property is restored (defined either in stylesheets or by
1275
* the browser's default style rules).
1276
*
1277
* Caveat 1: if the inherited display property for the element is set to "none"
1278
* by the stylesheets, that is the property that will be restored by a call to
1279
* setElementShown(), effectively toggling the display between "none" and
1280
* "none".
1281
*
1282
* Caveat 2: if the element display style is set inline (by setting either
1283
* element.style.display or a style attribute in the HTML), a call to
1284
* setElementShown will clear that setting and defer to the inherited style in
1285
* the stylesheet.
1286
* @param {Element} el Element to show or hide.
1287
* @param {*} isShown True to render the element in its default style,
1288
* false to disable rendering the element.
1289
* @return {void}
1290
*/
1291
goog.style.setElementShown = function(el, isShown) {
1292
'use strict';
1293
el.style.display = isShown ? '' : 'none';
1294
};
1295
1296
1297
/**
1298
* Test whether the given element has been shown or hidden via a call to
1299
* {@link #setElementShown}.
1300
*
1301
* Note this is strictly a companion method for a call
1302
* to {@link #setElementShown} and the same caveats apply; in particular, this
1303
* method does not guarantee that the return value will be consistent with
1304
* whether or not the element is actually visible.
1305
*
1306
* @param {Element} el The element to test.
1307
* @return {boolean} Whether the element has been shown.
1308
* @see #setElementShown
1309
*/
1310
goog.style.isElementShown = function(el) {
1311
'use strict';
1312
return el.style.display != 'none';
1313
};
1314
1315
1316
/**
1317
* Installs the style sheet into the window that contains opt_node. If
1318
* opt_node is null, the main window is used.
1319
* @param {!goog.html.SafeStyleSheet} safeStyleSheet The style sheet to install.
1320
* @param {?Node=} opt_node Node whose parent document should have the
1321
* styles installed.
1322
* @return {!HTMLStyleElement|!StyleSheet} In IE<11, a StyleSheet object with no
1323
* owning &lt;style&gt; tag (this is how IE creates style sheets). In every
1324
* other browser, a &lt;style&gt; element with an attached style. This
1325
* doesn't return a StyleSheet object so that setSafeStyleSheet can replace
1326
* it (otherwise, if you pass a StyleSheet to setSafeStyleSheet, it will
1327
* make a new StyleSheet and leave the original StyleSheet orphaned).
1328
*/
1329
goog.style.installSafeStyleSheet = function(safeStyleSheet, opt_node) {
1330
'use strict';
1331
var dh = goog.dom.getDomHelper(opt_node);
1332
1333
// IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be
1334
// undefined as of IE 11.
1335
var doc = dh.getDocument();
1336
if (goog.userAgent.IE && doc.createStyleSheet) {
1337
/** @type {(!HTMLStyleElement|!StyleSheet)} */
1338
var styleSheet = doc.createStyleSheet();
1339
goog.style.setSafeStyleSheet(styleSheet, safeStyleSheet);
1340
return styleSheet;
1341
} else {
1342
var head = dh.getElementsByTagNameAndClass(goog.dom.TagName.HEAD)[0];
1343
1344
// In opera documents are not guaranteed to have a head element, thus we
1345
// have to make sure one exists before using it.
1346
if (!head) {
1347
var body = dh.getElementsByTagNameAndClass(goog.dom.TagName.BODY)[0];
1348
head = dh.createDom(goog.dom.TagName.HEAD);
1349
body.parentNode.insertBefore(head, body);
1350
}
1351
var el = dh.createDom(goog.dom.TagName.STYLE);
1352
const nonce = goog.dom.safe.getStyleNonce();
1353
if (nonce) {
1354
el.setAttribute('nonce', nonce);
1355
}
1356
1357
// NOTE(user): Setting styles after the style element has been appended
1358
// to the head results in a nasty Webkit bug in certain scenarios. Please
1359
// refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional
1360
// details.
1361
goog.style.setSafeStyleSheet(el, safeStyleSheet);
1362
dh.appendChild(head, el);
1363
return el;
1364
}
1365
};
1366
1367
1368
/**
1369
* Removes the styles added by {@link #installSafeStyleSheet}.
1370
* @param {Element|StyleSheet} styleSheet The value returned by
1371
* {@link #installSafeStyleSheet}.
1372
*/
1373
goog.style.uninstallStyles = function(styleSheet) {
1374
'use strict';
1375
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
1376
var node = styleSheet.ownerNode || styleSheet.owningElement ||
1377
/** @type {Element} */ (styleSheet);
1378
goog.dom.removeNode(node);
1379
};
1380
1381
1382
/**
1383
* Sets the content of a style element. The style element can be any valid
1384
* style element. This element will have its content completely replaced by
1385
* the safeStyleSheet.
1386
* @param {!Element|!StyleSheet} element A stylesheet element as returned by
1387
* installSafeStyleSheet.
1388
* @param {!goog.html.SafeStyleSheet} safeStyleSheet The new content of the
1389
* stylesheet.
1390
* @return {void}
1391
* @suppress {strictMissingProperties} Added to tighten compiler checks
1392
*/
1393
goog.style.setSafeStyleSheet = function(element, safeStyleSheet) {
1394
'use strict';
1395
var stylesString = goog.html.SafeStyleSheet.unwrap(safeStyleSheet);
1396
if (goog.userAgent.IE && element.cssText !== undefined) {
1397
// Adding the selectors individually caused the browser to hang if the
1398
// selector was invalid or there were CSS comments. Setting the cssText of
1399
// the style node works fine and ignores CSS that IE doesn't understand.
1400
// However IE >= 11 doesn't support cssText any more, so we make sure that
1401
// cssText is a defined property and otherwise fall back to innerHTML.
1402
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
1403
element.cssText = stylesString;
1404
} else if (goog.global.trustedTypes) {
1405
goog.dom.setTextContent(/** @type {!Element} */ (element), stylesString);
1406
} else {
1407
// Setting textContent doesn't work in Safari, see b/29340337.
1408
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
1409
element.innerHTML = stylesString;
1410
}
1411
};
1412
1413
1414
/**
1415
* Sets 'white-space: pre-wrap' for a node (x-browser).
1416
*
1417
* There are as many ways of specifying pre-wrap as there are browsers.
1418
*
1419
* CSS3/IE8: white-space: pre-wrap;
1420
* Mozilla: white-space: -moz-pre-wrap;
1421
* Opera: white-space: -o-pre-wrap;
1422
* IE6/7: white-space: pre; word-wrap: break-word;
1423
*
1424
* @param {Element} el Element to enable pre-wrap for.
1425
*/
1426
goog.style.setPreWrap = function(el) {
1427
'use strict';
1428
var style = el.style;
1429
if (goog.userAgent.GECKO) {
1430
style.whiteSpace = '-moz-pre-wrap';
1431
} else {
1432
style.whiteSpace = 'pre-wrap';
1433
}
1434
};
1435
1436
1437
/**
1438
* Sets 'display: inline-block' for an element (cross-browser).
1439
* @param {Element} el Element to which the inline-block display style is to be
1440
* applied.
1441
* @return {void}
1442
* @see ../demos/inline_block_quirks.html
1443
* @see ../demos/inline_block_standards.html
1444
*/
1445
goog.style.setInlineBlock = function(el) {
1446
'use strict';
1447
var style = el.style;
1448
// Without position:relative, weirdness ensues. Just accept it and move on.
1449
style.position = 'relative';
1450
style.display = 'inline-block';
1451
};
1452
1453
1454
/**
1455
* Returns true if the element is using right to left (rtl) direction.
1456
* @param {Element} el The element to test.
1457
* @return {boolean} True for right to left, false for left to right.
1458
*/
1459
goog.style.isRightToLeft = function(el) {
1460
'use strict';
1461
return 'rtl' == goog.style.getStyle_(el, 'direction');
1462
};
1463
1464
1465
/**
1466
* The CSS style property corresponding to an element being
1467
* unselectable on the current browser platform (null if none).
1468
* Opera and IE instead use a DOM attribute 'unselectable'. MS Edge uses
1469
* the Webkit prefix.
1470
* @type {?string}
1471
* @private
1472
*/
1473
goog.style.unselectableStyle_ = goog.userAgent.GECKO ?
1474
'MozUserSelect' :
1475
goog.userAgent.WEBKIT || goog.userAgent.EDGE ? 'WebkitUserSelect' : null;
1476
1477
1478
/**
1479
* Returns true if the element is set to be unselectable, false otherwise.
1480
* Note that on some platforms (e.g. Mozilla), even if an element isn't set
1481
* to be unselectable, it will behave as such if any of its ancestors is
1482
* unselectable.
1483
* @param {Element} el Element to check.
1484
* @return {boolean} Whether the element is set to be unselectable.
1485
*/
1486
goog.style.isUnselectable = function(el) {
1487
'use strict';
1488
if (goog.style.unselectableStyle_) {
1489
return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';
1490
} else if (goog.userAgent.IE) {
1491
return el.getAttribute('unselectable') == 'on';
1492
}
1493
return false;
1494
};
1495
1496
1497
/**
1498
* Makes the element and its descendants selectable or unselectable. Note
1499
* that on some platforms (e.g. Mozilla), even if an element isn't set to
1500
* be unselectable, it will behave as such if any of its ancestors is
1501
* unselectable.
1502
* @param {Element} el The element to alter.
1503
* @param {boolean} unselectable Whether the element and its descendants
1504
* should be made unselectable.
1505
* @param {boolean=} opt_noRecurse Whether to only alter the element's own
1506
* selectable state, and leave its descendants alone; defaults to false.
1507
*/
1508
goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {
1509
'use strict';
1510
// TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?
1511
var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;
1512
var name = goog.style.unselectableStyle_;
1513
if (name) {
1514
// Add/remove the appropriate CSS style to/from the element and its
1515
// descendants.
1516
var value = unselectable ? 'none' : '';
1517
// MathML elements do not have a style property. Verify before setting.
1518
if (el.style) {
1519
el.style[name] = value;
1520
}
1521
if (descendants) {
1522
for (var i = 0, descendant; descendant = descendants[i]; i++) {
1523
if (descendant.style) {
1524
descendant.style[name] = value;
1525
}
1526
}
1527
}
1528
} else if (goog.userAgent.IE) {
1529
// Toggle the 'unselectable' attribute on the element and its descendants.
1530
var value = unselectable ? 'on' : '';
1531
el.setAttribute('unselectable', value);
1532
if (descendants) {
1533
for (var i = 0, descendant; descendant = descendants[i]; i++) {
1534
descendant.setAttribute('unselectable', value);
1535
}
1536
}
1537
}
1538
};
1539
1540
1541
/**
1542
* Gets the border box size for an element.
1543
* @param {Element} element The element to get the size for.
1544
* @return {!goog.math.Size} The border box size.
1545
*/
1546
goog.style.getBorderBoxSize = function(element) {
1547
'use strict';
1548
return new goog.math.Size(
1549
/** @type {!HTMLElement} */ (element).offsetWidth,
1550
/** @type {!HTMLElement} */ (element).offsetHeight);
1551
};
1552
1553
1554
/**
1555
* Sets the border box size of an element. This is potentially expensive in IE
1556
* if the document is CSS1Compat mode
1557
* @param {Element} element The element to set the size on.
1558
* @param {goog.math.Size} size The new size.
1559
*/
1560
goog.style.setBorderBoxSize = function(element, size) {
1561
'use strict';
1562
goog.style.setBoxSizingSize_(element, size, 'border-box');
1563
};
1564
1565
1566
/**
1567
* Gets the content box size for an element. This is potentially expensive in
1568
* all browsers.
1569
* @param {Element} element The element to get the size for.
1570
* @return {!goog.math.Size} The content box size.
1571
* @suppress {strictMissingProperties} Added to tighten compiler checks
1572
*/
1573
goog.style.getContentBoxSize = function(element) {
1574
'use strict';
1575
var doc = goog.dom.getOwnerDocument(element);
1576
var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;
1577
if (ieCurrentStyle && goog.dom.getDomHelper(doc).isCss1CompatMode() &&
1578
ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&
1579
!ieCurrentStyle.boxSizing) {
1580
// If IE in CSS1Compat mode than just use the width and height.
1581
// If we have a boxSizing then fall back on measuring the borders etc.
1582
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
1583
var width = goog.style.getIePixelValue_(
1584
element, /** @type {string} */ (ieCurrentStyle.width), 'width',
1585
'pixelWidth');
1586
/** @suppress {strictMissingProperties} Added to tighten compiler checks */
1587
var height = goog.style.getIePixelValue_(
1588
element, /** @type {string} */ (ieCurrentStyle.height), 'height',
1589
'pixelHeight');
1590
return new goog.math.Size(width, height);
1591
} else {
1592
var borderBoxSize = goog.style.getBorderBoxSize(element);
1593
var paddingBox = goog.style.getPaddingBox(element);
1594
var borderBox = goog.style.getBorderBox(element);
1595
return new goog.math.Size(
1596
borderBoxSize.width - borderBox.left - paddingBox.left -
1597
paddingBox.right - borderBox.right,
1598
borderBoxSize.height - borderBox.top - paddingBox.top -
1599
paddingBox.bottom - borderBox.bottom);
1600
}
1601
};
1602
1603
1604
/**
1605
* Sets the content box size of an element. This is potentially expensive in IE
1606
* if the document is BackCompat mode.
1607
* @param {Element} element The element to set the size on.
1608
* @param {goog.math.Size} size The new size.
1609
*/
1610
goog.style.setContentBoxSize = function(element, size) {
1611
'use strict';
1612
goog.style.setBoxSizingSize_(element, size, 'content-box');
1613
};
1614
1615
1616
/**
1617
* Helper function that sets the box sizing as well as the width and height
1618
* @param {Element} element The element to set the size on.
1619
* @param {goog.math.Size} size The new size to set.
1620
* @param {string} boxSizing The box-sizing value.
1621
* @private
1622
*/
1623
goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {
1624
'use strict';
1625
var style = element.style;
1626
if (goog.userAgent.GECKO) {
1627
style.MozBoxSizing = boxSizing;
1628
} else if (goog.userAgent.WEBKIT) {
1629
style.WebkitBoxSizing = boxSizing;
1630
} else {
1631
// Includes IE8 and Opera 9.50+
1632
style.boxSizing = boxSizing;
1633
}
1634
1635
// Setting this to a negative value will throw an exception on IE
1636
// (and doesn't do anything different than setting it to 0).
1637
style.width = Math.max(size.width, 0) + 'px';
1638
style.height = Math.max(size.height, 0) + 'px';
1639
};
1640
1641
1642
/**
1643
* IE specific function that converts a non pixel unit to pixels.
1644
* @param {Element} element The element to convert the value for.
1645
* @param {string} value The current value as a string. The value must not be
1646
* ''.
1647
* @param {string} name The CSS property name to use for the converstion. This
1648
* should be 'left', 'top', 'width' or 'height'.
1649
* @param {string} pixelName The CSS pixel property name to use to get the
1650
* value in pixels.
1651
* @return {number} The value in pixels.
1652
* @private
1653
*/
1654
goog.style.getIePixelValue_ = function(element, value, name, pixelName) {
1655
'use strict';
1656
// Try if we already have a pixel value. IE does not do half pixels so we
1657
// only check if it matches a number followed by 'px'.
1658
if (/^\d+px?$/.test(value)) {
1659
return parseInt(value, 10);
1660
} else {
1661
var oldStyleValue = element.style[name];
1662
var oldRuntimeValue = element.runtimeStyle[name];
1663
// set runtime style to prevent changes
1664
element.runtimeStyle[name] = element.currentStyle[name];
1665
element.style[name] = value;
1666
var pixelValue = element.style[pixelName];
1667
// restore
1668
element.style[name] = oldStyleValue;
1669
element.runtimeStyle[name] = oldRuntimeValue;
1670
return +pixelValue;
1671
}
1672
};
1673
1674
1675
/**
1676
* Helper function for getting the pixel padding or margin for IE.
1677
* @param {Element} element The element to get the padding for.
1678
* @param {string} propName The property name.
1679
* @return {number} The pixel padding.
1680
* @private
1681
*/
1682
goog.style.getIePixelDistance_ = function(element, propName) {
1683
'use strict';
1684
var value = goog.style.getCascadedStyle(element, propName);
1685
return value ?
1686
goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') :
1687
0;
1688
};
1689
1690
1691
/**
1692
* Gets the computed paddings or margins (on all sides) in pixels.
1693
* @param {Element} element The element to get the padding for.
1694
* @param {string} stylePrefix Pass 'padding' to retrieve the padding box,
1695
* or 'margin' to retrieve the margin box.
1696
* @return {!goog.math.Box} The computed paddings or margins.
1697
* @private
1698
*/
1699
goog.style.getBox_ = function(element, stylePrefix) {
1700
'use strict';
1701
if (goog.userAgent.IE) {
1702
var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');
1703
var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');
1704
var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');
1705
var bottom =
1706
goog.style.getIePixelDistance_(element, stylePrefix + 'Bottom');
1707
return new goog.math.Box(top, right, bottom, left);
1708
} else {
1709
// On non-IE browsers, getComputedStyle is always non-null.
1710
var left = goog.style.getComputedStyle(element, stylePrefix + 'Left');
1711
var right = goog.style.getComputedStyle(element, stylePrefix + 'Right');
1712
var top = goog.style.getComputedStyle(element, stylePrefix + 'Top');
1713
var bottom = goog.style.getComputedStyle(element, stylePrefix + 'Bottom');
1714
1715
// NOTE(arv): Gecko can return floating point numbers for the computed
1716
// style values.
1717
return new goog.math.Box(
1718
parseFloat(top), parseFloat(right), parseFloat(bottom),
1719
parseFloat(left));
1720
}
1721
};
1722
1723
1724
/**
1725
* Gets the computed paddings (on all sides) in pixels.
1726
* @param {Element} element The element to get the padding for.
1727
* @return {!goog.math.Box} The computed paddings.
1728
*/
1729
goog.style.getPaddingBox = function(element) {
1730
'use strict';
1731
return goog.style.getBox_(element, 'padding');
1732
};
1733
1734
1735
/**
1736
* Gets the computed margins (on all sides) in pixels.
1737
* @param {Element} element The element to get the margins for.
1738
* @return {!goog.math.Box} The computed margins.
1739
*/
1740
goog.style.getMarginBox = function(element) {
1741
'use strict';
1742
return goog.style.getBox_(element, 'margin');
1743
};
1744
1745
1746
/**
1747
* A map used to map the border width keywords to a pixel width.
1748
* @type {!Object}
1749
* @private
1750
*/
1751
goog.style.ieBorderWidthKeywords_ = {
1752
'thin': 2,
1753
'medium': 4,
1754
'thick': 6
1755
};
1756
1757
1758
/**
1759
* Helper function for IE to get the pixel border.
1760
* @param {Element} element The element to get the pixel border for.
1761
* @param {string} prop The part of the property name.
1762
* @return {number} The value in pixels.
1763
* @private
1764
*/
1765
goog.style.getIePixelBorder_ = function(element, prop) {
1766
'use strict';
1767
if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {
1768
return 0;
1769
}
1770
var width = goog.style.getCascadedStyle(element, prop + 'Width');
1771
if (width in goog.style.ieBorderWidthKeywords_) {
1772
return goog.style.ieBorderWidthKeywords_[width];
1773
}
1774
return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');
1775
};
1776
1777
1778
/**
1779
* Gets the computed border widths (on all sides) in pixels
1780
* @param {Element} element The element to get the border widths for.
1781
* @return {!goog.math.Box} The computed border widths.
1782
*/
1783
goog.style.getBorderBox = function(element) {
1784
'use strict';
1785
if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
1786
var left = goog.style.getIePixelBorder_(element, 'borderLeft');
1787
var right = goog.style.getIePixelBorder_(element, 'borderRight');
1788
var top = goog.style.getIePixelBorder_(element, 'borderTop');
1789
var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');
1790
return new goog.math.Box(top, right, bottom, left);
1791
} else {
1792
// On non-IE browsers, getComputedStyle is always non-null.
1793
var left = goog.style.getComputedStyle(element, 'borderLeftWidth');
1794
var right = goog.style.getComputedStyle(element, 'borderRightWidth');
1795
var top = goog.style.getComputedStyle(element, 'borderTopWidth');
1796
var bottom = goog.style.getComputedStyle(element, 'borderBottomWidth');
1797
1798
return new goog.math.Box(
1799
parseFloat(top), parseFloat(right), parseFloat(bottom),
1800
parseFloat(left));
1801
}
1802
};
1803
1804
1805
/**
1806
* Returns the font face applied to a given node. Opera and IE should return
1807
* the font actually displayed. Firefox returns the author's most-preferred
1808
* font (whether the browser is capable of displaying it or not.)
1809
* @param {Element} el The element whose font family is returned.
1810
* @return {string} The font family applied to el.
1811
*/
1812
goog.style.getFontFamily = function(el) {
1813
'use strict';
1814
var doc = goog.dom.getOwnerDocument(el);
1815
var font = '';
1816
// The moveToElementText method from the TextRange only works if the element
1817
// is attached to the owner document.
1818
if (doc.body.createTextRange && goog.dom.contains(doc, el)) {
1819
var range = doc.body.createTextRange();
1820
range.moveToElementText(el);
1821
1822
try {
1823
font = range.queryCommandValue('FontName');
1824
} catch (e) {
1825
// This is a workaround for a awkward exception.
1826
// On some IE, there is an exception coming from it.
1827
// The error description from this exception is:
1828
// This window has already been registered as a drop target
1829
// This is bogus description, likely due to a bug in ie.
1830
font = '';
1831
}
1832
}
1833
if (!font) {
1834
// Note if for some reason IE can't derive FontName with a TextRange, we
1835
// fallback to using currentStyle
1836
font = goog.style.getStyle_(el, 'fontFamily');
1837
}
1838
1839
// Firefox returns the applied font-family string (author's list of
1840
// preferred fonts.) We want to return the most-preferred font, in lieu of
1841
// the *actually* applied font.
1842
var fontsArray = font.split(',');
1843
if (fontsArray.length > 1) font = fontsArray[0];
1844
1845
// Sanitize for x-browser consistency:
1846
// Strip quotes because browsers aren't consistent with how they're
1847
// applied; Opera always encloses, Firefox sometimes, and IE never.
1848
return goog.string.stripQuotes(font, '"\'');
1849
};
1850
1851
1852
/**
1853
* Regular expression used for getLengthUnits.
1854
* @type {RegExp}
1855
* @private
1856
*/
1857
goog.style.lengthUnitRegex_ = /[^\d]+$/;
1858
1859
1860
/**
1861
* Returns the units used for a CSS length measurement.
1862
* @param {string} value A CSS length quantity.
1863
* @return {?string} The units of measurement.
1864
*/
1865
goog.style.getLengthUnits = function(value) {
1866
'use strict';
1867
var units = value.match(goog.style.lengthUnitRegex_);
1868
return units && units[0] || null;
1869
};
1870
1871
1872
/**
1873
* Map of absolute CSS length units
1874
* @type {!Object}
1875
* @private
1876
*/
1877
goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {
1878
'cm': 1,
1879
'in': 1,
1880
'mm': 1,
1881
'pc': 1,
1882
'pt': 1
1883
};
1884
1885
1886
/**
1887
* Map of relative CSS length units that can be accurately converted to px
1888
* font-size values using getIePixelValue_. Only units that are defined in
1889
* relation to a font size are convertible (%, small, etc. are not).
1890
* @type {!Object}
1891
* @private
1892
*/
1893
goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {
1894
'em': 1,
1895
'ex': 1
1896
};
1897
1898
1899
/**
1900
* Returns the font size, in pixels, of text in an element.
1901
* @param {Element} el The element whose font size is returned.
1902
* @return {number} The font size (in pixels).
1903
*/
1904
goog.style.getFontSize = function(el) {
1905
'use strict';
1906
var fontSize = goog.style.getStyle_(el, 'fontSize');
1907
var sizeUnits = goog.style.getLengthUnits(fontSize);
1908
if (fontSize && 'px' == sizeUnits) {
1909
// NOTE(user): This could be parseFloat instead, but IE doesn't return
1910
// decimal fractions in getStyle_ and Firefox reports the fractions, but
1911
// ignores them when rendering. Interestingly enough, when we force the
1912
// issue and size something to e.g., 50% of 25px, the browsers round in
1913
// opposite directions with Firefox reporting 12px and IE 13px. I punt.
1914
return parseInt(fontSize, 10);
1915
}
1916
1917
// In IE, we can convert absolute length units to a px value using
1918
// goog.style.getIePixelValue_. Units defined in relation to a font size
1919
// (em, ex) are applied relative to the element's parentNode and can also
1920
// be converted.
1921
if (goog.userAgent.IE) {
1922
if (String(sizeUnits) in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {
1923
return goog.style.getIePixelValue_(el, fontSize, 'left', 'pixelLeft');
1924
} else if (
1925
el.parentNode && el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&
1926
String(sizeUnits) in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {
1927
// Check the parent size - if it is the same it means the relative size
1928
// value is inherited and we therefore don't want to count it twice. If
1929
// it is different, this element either has explicit style or has a CSS
1930
// rule applying to it.
1931
var parentElement = /** @type {!Element} */ (el.parentNode);
1932
var parentSize = goog.style.getStyle_(parentElement, 'fontSize');
1933
return goog.style.getIePixelValue_(
1934
parentElement, fontSize == parentSize ? '1em' : fontSize, 'left',
1935
'pixelLeft');
1936
}
1937
}
1938
1939
// Sometimes we can't cleanly find the font size (some units relative to a
1940
// node's parent's font size are difficult: %, smaller et al), so we create
1941
// an invisible, absolutely-positioned span sized to be the height of an 'M'
1942
// rendered in its parent's (i.e., our target element's) font size. This is
1943
// the definition of CSS's font size attribute.
1944
var sizeElement = goog.dom.createDom(goog.dom.TagName.SPAN, {
1945
'style': 'visibility:hidden;position:absolute;' +
1946
'line-height:0;padding:0;margin:0;border:0;height:1em;'
1947
});
1948
goog.dom.appendChild(el, sizeElement);
1949
fontSize = sizeElement.offsetHeight;
1950
goog.dom.removeNode(sizeElement);
1951
1952
return fontSize;
1953
};
1954
1955
1956
/**
1957
* Parses a style attribute value. Converts CSS property names to camel case.
1958
* @param {string} value The style attribute value.
1959
* @return {!Object} Map of CSS properties to string values.
1960
*/
1961
goog.style.parseStyleAttribute = function(value) {
1962
'use strict';
1963
var result = {};
1964
value.split(/\s*;\s*/).forEach(function(pair) {
1965
'use strict';
1966
var keyValue = pair.match(/\s*([\w-]+)\s*:(.+)/);
1967
if (keyValue) {
1968
var styleName = keyValue[1];
1969
var styleValue = goog.string.trim(keyValue[2]);
1970
result[goog.string.toCamelCase(styleName.toLowerCase())] = styleValue;
1971
}
1972
});
1973
return result;
1974
};
1975
1976
1977
/**
1978
* Reverse of parseStyleAttribute; that is, takes a style object and returns the
1979
* corresponding attribute value. Converts camel case property names to proper
1980
* CSS selector names.
1981
* @param {Object} obj Map of CSS properties to values.
1982
* @return {string} The style attribute value.
1983
*/
1984
goog.style.toStyleAttribute = function(obj) {
1985
'use strict';
1986
var buffer = [];
1987
goog.object.forEach(obj, function(value, key) {
1988
'use strict';
1989
buffer.push(goog.string.toSelectorCase(key), ':', value, ';');
1990
});
1991
return buffer.join('');
1992
};
1993
1994
1995
/**
1996
* Sets CSS float property on an element.
1997
* @param {Element} el The element to set float property on.
1998
* @param {string} value The value of float CSS property to set on this element.
1999
*/
2000
goog.style.setFloat = function(el, value) {
2001
'use strict';
2002
el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;
2003
};
2004
2005
2006
/**
2007
* Gets value of explicitly-set float CSS property on an element.
2008
* @param {Element} el The element to get float property of.
2009
* @return {string} The value of explicitly-set float CSS property on this
2010
* element.
2011
*/
2012
goog.style.getFloat = function(el) {
2013
'use strict';
2014
return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';
2015
};
2016
2017
2018
/**
2019
* Returns the scroll bar width (represents the width of both horizontal
2020
* and vertical scroll).
2021
*
2022
* @param {string=} opt_className An optional class name (or names) to apply
2023
* to the invisible div created to measure the scrollbar. This is necessary
2024
* if some scrollbars are styled differently than others.
2025
* @return {number} The scroll bar width in px.
2026
*/
2027
goog.style.getScrollbarWidth = function(opt_className) {
2028
'use strict';
2029
// Add two hidden divs. The child div is larger than the parent and
2030
// forces scrollbars to appear on it.
2031
// Using overflow:scroll does not work consistently with scrollbars that
2032
// are styled with ::-webkit-scrollbar.
2033
var outerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
2034
if (opt_className) {
2035
outerDiv.className = opt_className;
2036
}
2037
outerDiv.style.cssText = 'overflow:auto;' +
2038
'position:absolute;top:0;width:100px;height:100px';
2039
var innerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
2040
goog.style.setSize(innerDiv, '200px', '200px');
2041
outerDiv.appendChild(innerDiv);
2042
goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
2043
var width = outerDiv.offsetWidth - outerDiv.clientWidth;
2044
goog.dom.removeNode(outerDiv);
2045
return width;
2046
};
2047
2048
2049
/**
2050
* Regular expression to extract x and y translation components from a CSS
2051
* transform Matrix representation.
2052
*
2053
* @type {!RegExp}
2054
* @const
2055
* @private
2056
*/
2057
goog.style.MATRIX_TRANSLATION_REGEX_ = new RegExp(
2058
'matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +
2059
'[0-9\\.\\-]+, [0-9\\.\\-]+, ' +
2060
'([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');
2061
2062
2063
/**
2064
* Returns the x,y translation component of any CSS transforms applied to the
2065
* element, in pixels.
2066
*
2067
* @param {!Element} element The element to get the translation of.
2068
* @return {!goog.math.Coordinate} The CSS translation of the element in px.
2069
*/
2070
goog.style.getCssTranslation = function(element) {
2071
'use strict';
2072
var transform = goog.style.getComputedTransform(element);
2073
if (!transform) {
2074
return new goog.math.Coordinate(0, 0);
2075
}
2076
var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);
2077
if (!matches) {
2078
return new goog.math.Coordinate(0, 0);
2079
}
2080
return new goog.math.Coordinate(
2081
parseFloat(matches[1]), parseFloat(matches[2]));
2082
};
2083
2084