Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/third_party/closure/goog/dom/forms.js
4152 views
1
/**
2
* @license
3
* Copyright The Closure Library Authors.
4
* SPDX-License-Identifier: Apache-2.0
5
*/
6
7
/**
8
* @fileoverview Utilities for manipulating a form and elements.
9
*
10
* @suppress {strictMissingProperties}
11
*/
12
13
goog.provide('goog.dom.forms');
14
15
goog.require('goog.dom.InputType');
16
goog.require('goog.dom.TagName');
17
goog.require('goog.dom.safe');
18
goog.require('goog.structs.Map');
19
goog.require('goog.window');
20
21
22
/**
23
* Submits form data via a new window. This hides references to the parent
24
* window and should be used when submitting forms to untrusted 3rd party urls.
25
* By default, this uses the action and method of the specified form
26
* element. It is possible to override the default action and method if an
27
* optional submit element with formaction and/or formmethod attributes is
28
* provided.
29
* @param {!HTMLFormElement} form The form.
30
* @param {!HTMLElement=} opt_submitElement The `<button>` or `<input>` element
31
* used to submit the form. The element should have a submit type.
32
* @return {boolean} true If the form was submitted succesfully.
33
* @throws {!Error} If opt_submitElement is not a valid form submit element.
34
*/
35
goog.dom.forms.submitFormInNewWindow = function(form, opt_submitElement) {
36
'use strict';
37
var formData = goog.dom.forms.getFormDataMap(form);
38
var action = form.action;
39
var method = form.method;
40
41
if (opt_submitElement) {
42
if (goog.dom.InputType.SUBMIT != opt_submitElement.type.toLowerCase()) {
43
throw new Error('opt_submitElement does not have a valid type.');
44
}
45
46
47
var submitValue =
48
/** @type {?string} */ (goog.dom.forms.getValue(opt_submitElement));
49
if (submitValue != null) {
50
goog.dom.forms.addFormDataToMap_(
51
formData, opt_submitElement.name, submitValue);
52
}
53
54
if (opt_submitElement.getAttribute('formaction')) {
55
action = opt_submitElement.getAttribute('formaction');
56
}
57
58
if (opt_submitElement.getAttribute('formmethod')) {
59
method = opt_submitElement.getAttribute('formmethod');
60
}
61
}
62
63
return goog.dom.forms.submitFormDataInNewWindow(action, method, formData);
64
};
65
66
/**
67
* Submits form data via a new window. This hides references to the parent
68
* window and should be used when submitting forms to untrusted 3rd party urls.
69
* @param {string} actionUri uri to submit form content to.
70
* @param {string} method HTTP method used to submit the form.
71
* @param {!goog.structs.Map<string, !Array<string>>} formData A map of the form
72
* data as field name to arrays of values.
73
* @return {boolean} true If the form was submitted succesfully.
74
*/
75
goog.dom.forms.submitFormDataInNewWindow = function(
76
actionUri, method, formData) {
77
'use strict';
78
var newWin = goog.window.openBlank('', {noreferrer: true});
79
80
// This could be null if a new window could not be opened. e.g. if it was
81
// stopped by a popup blocker.
82
if (!newWin) {
83
return false;
84
}
85
86
var newDocument = newWin.document;
87
88
var newForm =
89
/** @type {!HTMLFormElement} */ (newDocument.createElement('form'));
90
newForm.method = method;
91
goog.dom.safe.setFormElementAction(newForm, actionUri);
92
93
// After this point, do not directly reference the form object's functions as
94
// field names can shadow the form's properties.
95
96
formData.forEach(function(fieldValues, fieldName) {
97
'use strict';
98
for (var i = 0; i < fieldValues.length; i++) {
99
var fieldValue = fieldValues[i];
100
var newInput = newDocument.createElement('input');
101
newInput.name = fieldName;
102
newInput.value = fieldValue;
103
newInput.type = 'hidden';
104
HTMLFormElement.prototype.appendChild.call(newForm, newInput);
105
}
106
});
107
108
HTMLFormElement.prototype.submit.call(newForm);
109
return true;
110
};
111
112
113
/**
114
* Returns form data as a map of name to value arrays. This doesn't
115
* support file inputs.
116
* @param {HTMLFormElement} form The form.
117
* @return {!goog.structs.Map<string, !Array<string>>} A map of the form data
118
* as field name to arrays of values.
119
*/
120
goog.dom.forms.getFormDataMap = function(form) {
121
'use strict';
122
var map = new goog.structs.Map();
123
goog.dom.forms.getFormDataHelper_(
124
form, map, goog.dom.forms.addFormDataToMap_);
125
return map;
126
};
127
128
129
/**
130
* Returns the form data as an application/x-www-url-encoded string. This
131
* doesn't support file inputs.
132
* @param {HTMLFormElement} form The form.
133
* @return {string} An application/x-www-url-encoded string.
134
*/
135
goog.dom.forms.getFormDataString = function(form) {
136
'use strict';
137
var sb = [];
138
goog.dom.forms.getFormDataHelper_(
139
form, sb, goog.dom.forms.addFormDataToStringBuffer_);
140
return sb.join('&');
141
};
142
143
144
/**
145
* Returns the form data as a map or an application/x-www-url-encoded
146
* string. This doesn't support file inputs.
147
* @param {HTMLFormElement} form The form.
148
* @param {Object} result The object form data is being put in.
149
* @param {Function} fnAppend Function that takes `result`, an element
150
* name, and an element value, and adds the name/value pair to the result
151
* object.
152
* @private
153
*/
154
goog.dom.forms.getFormDataHelper_ = function(form, result, fnAppend) {
155
'use strict';
156
var els = form.elements;
157
for (var el, i = 0; el = els.item(i); i++) {
158
if ( // Make sure we don't include elements that are not part of the form.
159
// Some browsers include non-form elements. Check for 'form' property.
160
// See http://code.google.com/p/closure-library/issues/detail?id=227
161
// and
162
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#the-input-element
163
(el.form != form) || el.disabled ||
164
// HTMLFieldSetElement has a form property but no value.
165
el.tagName == goog.dom.TagName.FIELDSET) {
166
continue;
167
}
168
169
var name = el.name;
170
switch (el.type.toLowerCase()) {
171
case goog.dom.InputType.FILE:
172
// file inputs are not supported
173
case goog.dom.InputType.SUBMIT:
174
case goog.dom.InputType.RESET:
175
case goog.dom.InputType.BUTTON:
176
// don't submit these
177
break;
178
case goog.dom.InputType.SELECT_MULTIPLE:
179
var values = goog.dom.forms.getValue(el);
180
if (values != null) {
181
for (var value, j = 0; value = values[j]; j++) {
182
fnAppend(result, name, value);
183
}
184
}
185
break;
186
default:
187
var value = goog.dom.forms.getValue(el);
188
if (value != null) {
189
fnAppend(result, name, value);
190
}
191
}
192
}
193
194
// input[type=image] are not included in the elements collection
195
var inputs = form.getElementsByTagName(String(goog.dom.TagName.INPUT));
196
for (var input, i = 0; input = inputs[i]; i++) {
197
if (input.form == form &&
198
input.type.toLowerCase() == goog.dom.InputType.IMAGE) {
199
name = input.name;
200
fnAppend(result, name, input.value);
201
fnAppend(result, name + '.x', '0');
202
fnAppend(result, name + '.y', '0');
203
}
204
}
205
};
206
207
208
/**
209
* Adds the name/value pair to the map.
210
* @param {!goog.structs.Map<string, !Array<string>>} map The map to add to.
211
* @param {string} name The name.
212
* @param {string} value The value.
213
* @private
214
*/
215
goog.dom.forms.addFormDataToMap_ = function(map, name, value) {
216
'use strict';
217
var array = map.get(name);
218
if (!array) {
219
array = [];
220
map.set(name, array);
221
}
222
array.push(value);
223
};
224
225
226
/**
227
* Adds a name/value pair to an string buffer array in the form 'name=value'.
228
* @param {Array<string>} sb The string buffer array for storing data.
229
* @param {string} name The name.
230
* @param {string} value The value.
231
* @private
232
*/
233
goog.dom.forms.addFormDataToStringBuffer_ = function(sb, name, value) {
234
'use strict';
235
sb.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
236
};
237
238
239
/**
240
* Whether the form has a file input.
241
* @param {HTMLFormElement} form The form.
242
* @return {boolean} Whether the form has a file input.
243
*/
244
goog.dom.forms.hasFileInput = function(form) {
245
'use strict';
246
var els = form.elements;
247
for (var el, i = 0; el = els[i]; i++) {
248
if (!el.disabled && el.type &&
249
el.type.toLowerCase() == goog.dom.InputType.FILE) {
250
return true;
251
}
252
}
253
return false;
254
};
255
256
257
/**
258
* Enables or disables either all elements in a form or a single form element.
259
* @param {Element} el The element, either a form or an element within a form.
260
* @param {boolean} disabled Whether the element should be disabled.
261
*/
262
goog.dom.forms.setDisabled = function(el, disabled) {
263
'use strict';
264
// disable all elements in a form
265
if (el.tagName == goog.dom.TagName.FORM) {
266
var els = /** @type {!HTMLFormElement} */ (el).elements;
267
for (var i = 0; el = els.item(i); i++) {
268
goog.dom.forms.setDisabled(el, disabled);
269
}
270
} else {
271
// makes sure to blur buttons, multi-selects, and any elements which
272
// maintain keyboard/accessibility focus when disabled
273
if (disabled == true) {
274
el.blur();
275
}
276
el.disabled = disabled;
277
}
278
};
279
280
281
/**
282
* Focuses, and optionally selects the content of, a form element.
283
* @param {Element} el The form element.
284
*/
285
goog.dom.forms.focusAndSelect = function(el) {
286
'use strict';
287
el.focus();
288
if (el.select) {
289
el.select();
290
}
291
};
292
293
294
/**
295
* Whether a form element has a value.
296
* @param {Element} el The element.
297
* @return {boolean} Whether the form has a value.
298
*/
299
goog.dom.forms.hasValue = function(el) {
300
'use strict';
301
var value = goog.dom.forms.getValue(el);
302
return !!value;
303
};
304
305
306
/**
307
* Whether a named form field has a value.
308
* @param {HTMLFormElement} form The form element.
309
* @param {string} name Name of an input to the form.
310
* @return {boolean} Whether the form has a value.
311
*/
312
goog.dom.forms.hasValueByName = function(form, name) {
313
'use strict';
314
var value = goog.dom.forms.getValueByName(form, name);
315
return !!value;
316
};
317
318
319
/**
320
* Gets the current value of any element with a type.
321
* @param {null|!Element|!RadioNodeList<?>} input The element.
322
* @return {string|Array<string>|null} The current value of the element
323
* (or null).
324
*/
325
goog.dom.forms.getValue = function(input) {
326
'use strict';
327
// Elements with a type may need more specialized logic.
328
var type = /** {{type: (string|undefined)}} */ (input).type;
329
330
if (typeof type === 'string') {
331
var el = /** @type {!Element} */ (input);
332
333
switch (type.toLowerCase()) {
334
case goog.dom.InputType.CHECKBOX:
335
case goog.dom.InputType.RADIO:
336
return goog.dom.forms.getInputChecked_(el);
337
case goog.dom.InputType.SELECT_ONE:
338
return goog.dom.forms.getSelectSingle_(el);
339
case goog.dom.InputType.SELECT_MULTIPLE:
340
return goog.dom.forms.getSelectMultiple_(el);
341
default:
342
// Not every element with a value has a type (e.g. meter and progress).
343
}
344
}
345
346
// Coerce `undefined` to `null`.
347
return input.value != null ? input.value : null;
348
};
349
350
351
/**
352
* Returns the value of the named form field. In the case of radio buttons,
353
* returns the value of the checked button with the given name.
354
*
355
* @param {HTMLFormElement} form The form element.
356
* @param {string} name Name of an input to the form.
357
*
358
* @return {Array<string>|string|null} The value of the form element, or
359
* null if the form element does not exist or has no value.
360
*/
361
goog.dom.forms.getValueByName = function(form, name) {
362
'use strict';
363
var els = form.elements[name];
364
365
if (!els) {
366
return null;
367
} else if (els.type) {
368
return goog.dom.forms.getValue(/** @type {!Element} */ (els));
369
} else {
370
for (var i = 0; i < els.length; i++) {
371
var val = goog.dom.forms.getValue(els[i]);
372
if (val) {
373
return val;
374
}
375
}
376
return null;
377
}
378
};
379
380
381
/**
382
* Gets the current value of a checkable input element.
383
* @param {Element} el The element.
384
* @return {?string} The value of the form element (or null).
385
* @private
386
*/
387
goog.dom.forms.getInputChecked_ = function(el) {
388
'use strict';
389
return el.checked ? /** @type {?} */ (el).value : null;
390
};
391
392
393
/**
394
* Gets the current value of a select-one element.
395
* @param {Element} el The element.
396
* @return {?string} The value of the form element (or null).
397
* @private
398
*/
399
goog.dom.forms.getSelectSingle_ = function(el) {
400
'use strict';
401
var selectedIndex = /** @type {!HTMLSelectElement} */ (el).selectedIndex;
402
return selectedIndex >= 0 ?
403
/** @type {!HTMLSelectElement} */ (el).options[selectedIndex].value :
404
null;
405
};
406
407
408
/**
409
* Gets the current value of a select-multiple element.
410
* @param {Element} el The element.
411
* @return {Array<string>?} The value of the form element (or null).
412
* @private
413
*/
414
goog.dom.forms.getSelectMultiple_ = function(el) {
415
'use strict';
416
var values = [];
417
for (var option, i = 0;
418
option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {
419
if (option.selected) {
420
values.push(option.value);
421
}
422
}
423
return values.length ? values : null;
424
};
425
426
427
/**
428
* Sets the current value of any element with a type.
429
* @param {Element} el The element.
430
* @param {*=} opt_value The value to give to the element, which will be coerced
431
* by the browser in the default case using toString. This value should be
432
* an array for setting the value of select multiple elements.
433
*/
434
goog.dom.forms.setValue = function(el, opt_value) {
435
'use strict';
436
// Elements with a type may need more specialized logic.
437
var type = /** @type {!HTMLInputElement} */ (el).type;
438
switch (typeof type === 'string' && type.toLowerCase()) {
439
case goog.dom.InputType.CHECKBOX:
440
case goog.dom.InputType.RADIO:
441
goog.dom.forms.setInputChecked_(
442
el,
443
/** @type {string} */ (opt_value));
444
return;
445
case goog.dom.InputType.SELECT_ONE:
446
goog.dom.forms.setSelectSingle_(
447
el,
448
/** @type {string} */ (opt_value));
449
return;
450
case goog.dom.InputType.SELECT_MULTIPLE:
451
goog.dom.forms.setSelectMultiple_(
452
el,
453
/** @type {!Array<string>} */ (opt_value));
454
return;
455
default:
456
// Not every element with a value has a type (e.g. meter and progress).
457
el.value = opt_value != null ? opt_value : '';
458
}
459
};
460
461
462
/**
463
* Sets a checkable input element's checked property.
464
* #TODO(user): This seems potentially unintuitive since it doesn't set
465
* the value property but my hunch is that the primary use case is to check a
466
* checkbox, not to reset its value property.
467
* @param {Element} el The element.
468
* @param {string|boolean=} opt_value The value, sets the element checked if
469
* val is set.
470
* @private
471
*/
472
goog.dom.forms.setInputChecked_ = function(el, opt_value) {
473
'use strict';
474
el.checked = opt_value;
475
};
476
477
478
/**
479
* Sets the value of a select-one element.
480
* @param {Element} el The element.
481
* @param {string=} opt_value The value of the selected option element.
482
* @private
483
*/
484
goog.dom.forms.setSelectSingle_ = function(el, opt_value) {
485
'use strict';
486
// unset any prior selections
487
el.selectedIndex = -1;
488
if (typeof opt_value === 'string') {
489
for (var option, i = 0;
490
option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {
491
if (option.value == opt_value) {
492
option.selected = true;
493
break;
494
}
495
}
496
}
497
};
498
499
500
/**
501
* Sets the value of a select-multiple element.
502
* @param {Element} el The element.
503
* @param {Array<string>|string=} opt_value The value of the selected option
504
* element(s).
505
* @private
506
*/
507
goog.dom.forms.setSelectMultiple_ = function(el, opt_value) {
508
'use strict';
509
// reset string opt_values as an array
510
if (typeof opt_value === 'string') {
511
opt_value = [opt_value];
512
}
513
for (var option, i = 0;
514
option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {
515
// we have to reset the other options to false for select-multiple
516
option.selected = false;
517
if (opt_value) {
518
for (var value, j = 0; value = opt_value[j]; j++) {
519
if (option.value == value) {
520
option.selected = true;
521
}
522
}
523
}
524
}
525
};
526
527