Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/ui/custombuttonrenderer.js
4049 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview A custom button renderer that uses CSS voodoo to render a
9
* button-like object with fake rounded corners.
10
*/
11
12
goog.provide('goog.ui.CustomButtonRenderer');
13
14
goog.require('goog.a11y.aria.Role');
15
goog.require('goog.asserts');
16
goog.require('goog.dom.NodeType');
17
goog.require('goog.dom.TagName');
18
goog.require('goog.dom.classlist');
19
goog.require('goog.string');
20
goog.require('goog.ui.ButtonRenderer');
21
goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
22
goog.requireType('goog.dom.DomHelper');
23
goog.requireType('goog.ui.Button');
24
goog.requireType('goog.ui.Control');
25
goog.requireType('goog.ui.ControlContent');
26
27
28
29
/**
30
* Custom renderer for {@link goog.ui.Button}s. Custom buttons can contain
31
* almost arbitrary HTML content, will flow like inline elements, but can be
32
* styled like block-level elements.
33
*
34
* @constructor
35
* @extends {goog.ui.ButtonRenderer}
36
*/
37
goog.ui.CustomButtonRenderer = function() {
38
'use strict';
39
goog.ui.ButtonRenderer.call(this);
40
};
41
goog.inherits(goog.ui.CustomButtonRenderer, goog.ui.ButtonRenderer);
42
goog.addSingletonGetter(goog.ui.CustomButtonRenderer);
43
44
45
/**
46
* Default CSS class to be applied to the root element of components rendered
47
* by this renderer.
48
* @type {string}
49
*/
50
goog.ui.CustomButtonRenderer.CSS_CLASS = goog.getCssName('goog-custom-button');
51
52
53
/**
54
* Returns the button's contents wrapped in the following DOM structure:
55
*
56
* <div class="goog-inline-block goog-custom-button">
57
* <div class="goog-inline-block goog-custom-button-outer-box">
58
* <div class="goog-inline-block goog-custom-button-inner-box">
59
* Contents...
60
* </div>
61
* </div>
62
* </div>
63
*
64
* Overrides {@link goog.ui.ButtonRenderer#createDom}.
65
* @param {goog.ui.Control} control goog.ui.Button to render.
66
* @return {!Element} Root element for the button.
67
* @override
68
*/
69
goog.ui.CustomButtonRenderer.prototype.createDom = function(control) {
70
'use strict';
71
var button = /** @type {goog.ui.Button} */ (control);
72
var classNames = this.getClassNames(button);
73
var buttonElement = button.getDomHelper().createDom(
74
goog.dom.TagName.DIV,
75
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' '),
76
this.createButton(button.getContent(), button.getDomHelper()));
77
this.setTooltip(buttonElement, /** @type {string}*/ (button.getTooltip()));
78
79
return buttonElement;
80
};
81
82
83
/**
84
* Returns the ARIA role to be applied to custom buttons.
85
* @return {goog.a11y.aria.Role|undefined} ARIA role.
86
* @override
87
*/
88
goog.ui.CustomButtonRenderer.prototype.getAriaRole = function() {
89
'use strict';
90
return goog.a11y.aria.Role.BUTTON;
91
};
92
93
94
/**
95
* Takes the button's root element and returns the parent element of the
96
* button's contents. Overrides the superclass implementation by taking
97
* the nested DIV structure of custom buttons into account.
98
* @param {Element} element Root element of the button whose content
99
* element is to be returned.
100
* @return {Element} The button's content element (if any).
101
* @override
102
*/
103
goog.ui.CustomButtonRenderer.prototype.getContentElement = function(element) {
104
'use strict';
105
return element && element.firstChild &&
106
/** @type {Element} */ (element.firstChild.firstChild);
107
};
108
109
110
/**
111
* Takes a text caption or existing DOM structure, and returns the content
112
* wrapped in a pseudo-rounded-corner box. Creates the following DOM structure:
113
*
114
* <div class="goog-inline-block goog-custom-button-outer-box">
115
* <div class="goog-inline-block goog-custom-button-inner-box">
116
* Contents...
117
* </div>
118
* </div>
119
*
120
* Used by both {@link #createDom} and {@link #decorate}. To be overridden
121
* by subclasses.
122
* @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
123
* in a box.
124
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
125
* @return {!Element} Pseudo-rounded-corner box containing the content.
126
*/
127
goog.ui.CustomButtonRenderer.prototype.createButton = function(content, dom) {
128
'use strict';
129
return dom.createDom(
130
goog.dom.TagName.DIV,
131
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
132
goog.getCssName(this.getCssClass(), 'outer-box'),
133
dom.createDom(
134
goog.dom.TagName.DIV,
135
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
136
goog.getCssName(this.getCssClass(), 'inner-box'),
137
content));
138
};
139
140
141
/**
142
* Returns true if this renderer can decorate the element. Overrides
143
* {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
144
* element is a DIV, false otherwise.
145
* @param {Element} element Element to decorate.
146
* @return {boolean} Whether the renderer can decorate the element.
147
* @override
148
*/
149
goog.ui.CustomButtonRenderer.prototype.canDecorate = function(element) {
150
'use strict';
151
return element.tagName == goog.dom.TagName.DIV;
152
};
153
154
155
/**
156
* Check if the button's element has a box structure.
157
* @param {goog.ui.Button} button Button instance whose structure is being
158
* checked.
159
* @param {Element} element Element of the button.
160
* @return {boolean} Whether the element has a box structure.
161
* @protected
162
*/
163
goog.ui.CustomButtonRenderer.prototype.hasBoxStructure = function(
164
button, element) {
165
'use strict';
166
var outer = button.getDomHelper().getFirstElementChild(element);
167
var outerClassName = goog.getCssName(this.getCssClass(), 'outer-box');
168
if (outer && goog.dom.classlist.contains(outer, outerClassName)) {
169
var inner = button.getDomHelper().getFirstElementChild(outer);
170
var innerClassName = goog.getCssName(this.getCssClass(), 'inner-box');
171
if (inner && goog.dom.classlist.contains(inner, innerClassName)) {
172
// We have a proper box structure.
173
return true;
174
}
175
}
176
return false;
177
};
178
179
180
/**
181
* Takes an existing element and decorates it with the custom button control.
182
* Initializes the control's ID, content, tooltip, value, and state based
183
* on the ID of the element, its child nodes, and its CSS classes, respectively.
184
* Returns the element. Overrides {@link goog.ui.ButtonRenderer#decorate}.
185
* @param {goog.ui.Control} control Button instance to decorate the element.
186
* @param {Element} element Element to decorate.
187
* @return {Element} Decorated element.
188
* @override
189
*/
190
goog.ui.CustomButtonRenderer.prototype.decorate = function(control, element) {
191
'use strict';
192
goog.asserts.assert(element);
193
194
var button = /** @type {goog.ui.Button} */ (control);
195
// Trim text nodes in the element's child node list; otherwise madness
196
// ensues (i.e. on Gecko, buttons will flicker and shift when moused over).
197
goog.ui.CustomButtonRenderer.trimTextNodes_(element, true);
198
goog.ui.CustomButtonRenderer.trimTextNodes_(element, false);
199
200
// Create the buttom dom if it has not been created.
201
if (!this.hasBoxStructure(button, element)) {
202
element.appendChild(
203
/** @type {!Node} */ (
204
this.createButton(element.childNodes, button.getDomHelper())));
205
}
206
207
goog.dom.classlist.addAll(
208
element, [goog.ui.INLINE_BLOCK_CLASSNAME, this.getCssClass()]);
209
return goog.ui.CustomButtonRenderer.superClass_.decorate.call(
210
this, button, element);
211
};
212
213
214
/**
215
* Returns the CSS class to be applied to the root element of components
216
* rendered using this renderer.
217
* @return {string} Renderer-specific CSS class.
218
* @override
219
*/
220
goog.ui.CustomButtonRenderer.prototype.getCssClass = function() {
221
'use strict';
222
return goog.ui.CustomButtonRenderer.CSS_CLASS;
223
};
224
225
226
/**
227
* Takes an element and removes leading or trailing whitespace from the start
228
* or the end of its list of child nodes. The Boolean argument determines
229
* whether to trim from the start or the end of the node list. Empty text
230
* nodes are removed, and the first non-empty text node is trimmed from the
231
* left or the right as appropriate. For example,
232
*
233
* <div class="goog-inline-block">
234
* #text ""
235
* #text "\n Hello "
236
* <span>...</span>
237
* #text " World! \n"
238
* #text ""
239
* </div>
240
*
241
* becomes
242
*
243
* <div class="goog-inline-block">
244
* #text "Hello "
245
* <span>...</span>
246
* #text " World!"
247
* </div>
248
*
249
* This is essential for Gecko, where leading/trailing whitespace messes with
250
* the layout of elements with -moz-inline-box (used in goog-inline-block), and
251
* optional but harmless for non-Gecko.
252
*
253
* @param {Element} element Element whose child node list is to be trimmed.
254
* @param {boolean} fromStart Whether to trim from the start or from the end.
255
* @private
256
*/
257
goog.ui.CustomButtonRenderer.trimTextNodes_ = function(element, fromStart) {
258
'use strict';
259
if (element) {
260
var node = fromStart ? element.firstChild : element.lastChild, next;
261
// Tag soup HTML may result in a DOM where siblings have different parents.
262
while (node && node.parentNode == element) {
263
// Get the next/previous sibling here, since the node may be removed.
264
next = fromStart ? node.nextSibling : node.previousSibling;
265
if (node.nodeType == goog.dom.NodeType.TEXT) {
266
// Found a text node.
267
var text = node.nodeValue;
268
if (goog.string.trim(text) == '') {
269
// Found an empty text node; remove it.
270
element.removeChild(node);
271
} else {
272
// Found a non-empty text node; trim from the start/end, then exit.
273
node.nodeValue = fromStart ? goog.string.trimLeft(text) :
274
goog.string.trimRight(text);
275
break;
276
}
277
} else {
278
// Found a non-text node; done.
279
break;
280
}
281
node = next;
282
}
283
}
284
};
285
286