123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // Copyright 2006 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Utilities for manipulating a form and elements.
- *
- * @author arv@google.com (Erik Arvidsson)
- */
- goog.provide('goog.dom.forms');
- goog.require('goog.dom.InputType');
- goog.require('goog.dom.TagName');
- goog.require('goog.structs.Map');
- goog.require('goog.window');
- /**
- * Submits form data via a new window. This hides references to the parent
- * window and should be used when submitting forms to untrusted 3rd party urls.
- * By default, this uses the action and method of the specified form
- * element. It is possible to override the default action and method if an
- * optional submit element with formaction and/or formmethod attributes is
- * provided.
- * @param {!HTMLFormElement} form The form.
- * @param {!HTMLElement=} opt_submitElement The `<button>` or `<input>` element
- * used to submit the form. The element should have a submit type.
- * @return {boolean} true If the form was submitted succesfully.
- * @throws {!Error} If opt_submitElement is not a valid form submit element.
- */
- goog.dom.forms.submitFormInNewWindow = function(form, opt_submitElement) {
- var formData = goog.dom.forms.getFormDataMap(form);
- var action = form.action;
- var method = form.method;
- if (opt_submitElement) {
- if (goog.dom.InputType.SUBMIT != opt_submitElement.type.toLowerCase()) {
- throw Error('opt_submitElement does not have a valid type.');
- }
- var submitValue =
- /** @type {?string} */ (goog.dom.forms.getValue(opt_submitElement));
- if (submitValue != null) {
- goog.dom.forms.addFormDataToMap_(
- formData, opt_submitElement.name, submitValue);
- }
- if (opt_submitElement.getAttribute('formaction')) {
- action = opt_submitElement.getAttribute('formaction');
- }
- if (opt_submitElement.getAttribute('formmethod')) {
- method = opt_submitElement.getAttribute('formmethod');
- }
- }
- return goog.dom.forms.submitFormDataInNewWindow(action, method, formData);
- };
- /**
- * Submits form data via a new window. This hides references to the parent
- * window and should be used when submitting forms to untrusted 3rd party urls.
- * @param {string} actionUri uri to submit form content to.
- * @param {string} method HTTP method used to submit the form.
- * @param {!goog.structs.Map<string, !Array<string>>} formData A map of the form
- * data as field name to arrays of values.
- * @return {boolean} true If the form was submitted succesfully.
- */
- goog.dom.forms.submitFormDataInNewWindow = function(
- actionUri, method, formData) {
- var newWin = goog.window.openBlank('', {noreferrer: true});
- // This could be null if a new window could not be opened. e.g. if it was
- // stopped by a popup blocker.
- if (!newWin) {
- return false;
- }
- var newDocument = newWin.document;
- var newForm =
- /** @type {!HTMLFormElement} */ (newDocument.createElement('form'));
- newForm.method = method;
- newForm.action = actionUri;
- // After this point, do not directly reference the form object's functions as
- // field names can shadow the form's properties.
- formData.forEach(function(fieldValues, fieldName) {
- for (var i = 0; i < fieldValues.length; i++) {
- var fieldValue = fieldValues[i];
- var newInput = newDocument.createElement('input');
- newInput.name = fieldName;
- newInput.value = fieldValue;
- newInput.type = 'hidden';
- HTMLFormElement.prototype.appendChild.call(newForm, newInput);
- }
- });
- HTMLFormElement.prototype.submit.call(newForm);
- return true;
- };
- /**
- * Returns form data as a map of name to value arrays. This doesn't
- * support file inputs.
- * @param {HTMLFormElement} form The form.
- * @return {!goog.structs.Map<string, !Array<string>>} A map of the form data
- * as field name to arrays of values.
- */
- goog.dom.forms.getFormDataMap = function(form) {
- var map = new goog.structs.Map();
- goog.dom.forms.getFormDataHelper_(
- form, map, goog.dom.forms.addFormDataToMap_);
- return map;
- };
- /**
- * Returns the form data as an application/x-www-url-encoded string. This
- * doesn't support file inputs.
- * @param {HTMLFormElement} form The form.
- * @return {string} An application/x-www-url-encoded string.
- */
- goog.dom.forms.getFormDataString = function(form) {
- var sb = [];
- goog.dom.forms.getFormDataHelper_(
- form, sb, goog.dom.forms.addFormDataToStringBuffer_);
- return sb.join('&');
- };
- /**
- * Returns the form data as a map or an application/x-www-url-encoded
- * string. This doesn't support file inputs.
- * @param {HTMLFormElement} form The form.
- * @param {Object} result The object form data is being put in.
- * @param {Function} fnAppend Function that takes {@code result}, an element
- * name, and an element value, and adds the name/value pair to the result
- * object.
- * @private
- */
- goog.dom.forms.getFormDataHelper_ = function(form, result, fnAppend) {
- var els = form.elements;
- for (var el, i = 0; el = els[i]; i++) {
- if ( // Make sure we don't include elements that are not part of the form.
- // Some browsers include non-form elements. Check for 'form' property.
- // See http://code.google.com/p/closure-library/issues/detail?id=227
- // and
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#the-input-element
- (el.form != form) || el.disabled ||
- // HTMLFieldSetElement has a form property but no value.
- el.tagName == goog.dom.TagName.FIELDSET) {
- continue;
- }
- var name = el.name;
- switch (el.type.toLowerCase()) {
- case goog.dom.InputType.FILE:
- // file inputs are not supported
- case goog.dom.InputType.SUBMIT:
- case goog.dom.InputType.RESET:
- case goog.dom.InputType.BUTTON:
- // don't submit these
- break;
- case goog.dom.InputType.SELECT_MULTIPLE:
- var values = goog.dom.forms.getValue(el);
- if (values != null) {
- for (var value, j = 0; value = values[j]; j++) {
- fnAppend(result, name, value);
- }
- }
- break;
- default:
- var value = goog.dom.forms.getValue(el);
- if (value != null) {
- fnAppend(result, name, value);
- }
- }
- }
- // input[type=image] are not included in the elements collection
- var inputs = form.getElementsByTagName(String(goog.dom.TagName.INPUT));
- for (var input, i = 0; input = inputs[i]; i++) {
- if (input.form == form &&
- input.type.toLowerCase() == goog.dom.InputType.IMAGE) {
- name = input.name;
- fnAppend(result, name, input.value);
- fnAppend(result, name + '.x', '0');
- fnAppend(result, name + '.y', '0');
- }
- }
- };
- /**
- * Adds the name/value pair to the map.
- * @param {!goog.structs.Map<string, !Array<string>>} map The map to add to.
- * @param {string} name The name.
- * @param {string} value The value.
- * @private
- */
- goog.dom.forms.addFormDataToMap_ = function(map, name, value) {
- var array = map.get(name);
- if (!array) {
- array = [];
- map.set(name, array);
- }
- array.push(value);
- };
- /**
- * Adds a name/value pair to an string buffer array in the form 'name=value'.
- * @param {Array<string>} sb The string buffer array for storing data.
- * @param {string} name The name.
- * @param {string} value The value.
- * @private
- */
- goog.dom.forms.addFormDataToStringBuffer_ = function(sb, name, value) {
- sb.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
- };
- /**
- * Whether the form has a file input.
- * @param {HTMLFormElement} form The form.
- * @return {boolean} Whether the form has a file input.
- */
- goog.dom.forms.hasFileInput = function(form) {
- var els = form.elements;
- for (var el, i = 0; el = els[i]; i++) {
- if (!el.disabled && el.type &&
- el.type.toLowerCase() == goog.dom.InputType.FILE) {
- return true;
- }
- }
- return false;
- };
- /**
- * Enables or disables either all elements in a form or a single form element.
- * @param {Element} el The element, either a form or an element within a form.
- * @param {boolean} disabled Whether the element should be disabled.
- */
- goog.dom.forms.setDisabled = function(el, disabled) {
- // disable all elements in a form
- if (el.tagName == goog.dom.TagName.FORM) {
- var els = /** @type {!HTMLFormElement} */ (el).elements;
- for (var i = 0; el = els[i]; i++) {
- goog.dom.forms.setDisabled(el, disabled);
- }
- } else {
- // makes sure to blur buttons, multi-selects, and any elements which
- // maintain keyboard/accessibility focus when disabled
- if (disabled == true) {
- el.blur();
- }
- el.disabled = disabled;
- }
- };
- /**
- * Focuses, and optionally selects the content of, a form element.
- * @param {Element} el The form element.
- */
- goog.dom.forms.focusAndSelect = function(el) {
- el.focus();
- if (el.select) {
- el.select();
- }
- };
- /**
- * Whether a form element has a value.
- * @param {Element} el The element.
- * @return {boolean} Whether the form has a value.
- */
- goog.dom.forms.hasValue = function(el) {
- var value = goog.dom.forms.getValue(el);
- return !!value;
- };
- /**
- * Whether a named form field has a value.
- * @param {HTMLFormElement} form The form element.
- * @param {string} name Name of an input to the form.
- * @return {boolean} Whether the form has a value.
- */
- goog.dom.forms.hasValueByName = function(form, name) {
- var value = goog.dom.forms.getValueByName(form, name);
- return !!value;
- };
- /**
- * Gets the current value of any element with a type.
- * @param {Element} el The element.
- * @return {string|Array<string>|null} The current value of the element
- * (or null).
- */
- goog.dom.forms.getValue = function(el) {
- var type = /** @type {!HTMLInputElement} */ (el).type;
- if (!goog.isDef(type)) {
- return null;
- }
- switch (type.toLowerCase()) {
- case goog.dom.InputType.CHECKBOX:
- case goog.dom.InputType.RADIO:
- return goog.dom.forms.getInputChecked_(el);
- case goog.dom.InputType.SELECT_ONE:
- return goog.dom.forms.getSelectSingle_(el);
- case goog.dom.InputType.SELECT_MULTIPLE:
- return goog.dom.forms.getSelectMultiple_(el);
- default:
- return goog.isDef(el.value) ? el.value : null;
- }
- };
- /**
- * Returns the value of the named form field. In the case of radio buttons,
- * returns the value of the checked button with the given name.
- *
- * @param {HTMLFormElement} form The form element.
- * @param {string} name Name of an input to the form.
- *
- * @return {Array<string>|string|null} The value of the form element, or
- * null if the form element does not exist or has no value.
- */
- goog.dom.forms.getValueByName = function(form, name) {
- var els = form.elements[name];
- if (els) {
- if (els.type) {
- return goog.dom.forms.getValue(els);
- } else {
- for (var i = 0; i < els.length; i++) {
- var val = goog.dom.forms.getValue(els[i]);
- if (val) {
- return val;
- }
- }
- }
- }
- return null;
- };
- /**
- * Gets the current value of a checkable input element.
- * @param {Element} el The element.
- * @return {?string} The value of the form element (or null).
- * @private
- */
- goog.dom.forms.getInputChecked_ = function(el) {
- return el.checked ? /** @type {?} */ (el).value : null;
- };
- /**
- * Gets the current value of a select-one element.
- * @param {Element} el The element.
- * @return {?string} The value of the form element (or null).
- * @private
- */
- goog.dom.forms.getSelectSingle_ = function(el) {
- var selectedIndex = /** @type {!HTMLSelectElement} */ (el).selectedIndex;
- return selectedIndex >= 0 ?
- /** @type {!HTMLSelectElement} */ (el).options[selectedIndex].value :
- null;
- };
- /**
- * Gets the current value of a select-multiple element.
- * @param {Element} el The element.
- * @return {Array<string>?} The value of the form element (or null).
- * @private
- */
- goog.dom.forms.getSelectMultiple_ = function(el) {
- var values = [];
- for (var option, i = 0;
- option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {
- if (option.selected) {
- values.push(option.value);
- }
- }
- return values.length ? values : null;
- };
- /**
- * Sets the current value of any element with a type.
- * @param {Element} el The element.
- * @param {*=} opt_value The value to give to the element, which will be coerced
- * by the browser in the default case using toString. This value should be
- * an array for setting the value of select multiple elements.
- */
- goog.dom.forms.setValue = function(el, opt_value) {
- var type = /** @type {!HTMLInputElement} */ (el).type;
- if (goog.isDef(type)) {
- switch (type.toLowerCase()) {
- case goog.dom.InputType.CHECKBOX:
- case goog.dom.InputType.RADIO:
- goog.dom.forms.setInputChecked_(
- el,
- /** @type {string} */ (opt_value));
- break;
- case goog.dom.InputType.SELECT_ONE:
- goog.dom.forms.setSelectSingle_(
- el,
- /** @type {string} */ (opt_value));
- break;
- case goog.dom.InputType.SELECT_MULTIPLE:
- goog.dom.forms.setSelectMultiple_(
- el,
- /** @type {Array<string>} */ (opt_value));
- break;
- default:
- el.value = goog.isDefAndNotNull(opt_value) ? opt_value : '';
- }
- }
- };
- /**
- * Sets a checkable input element's checked property.
- * #TODO(user): This seems potentially unintuitive since it doesn't set
- * the value property but my hunch is that the primary use case is to check a
- * checkbox, not to reset its value property.
- * @param {Element} el The element.
- * @param {string|boolean=} opt_value The value, sets the element checked if
- * val is set.
- * @private
- */
- goog.dom.forms.setInputChecked_ = function(el, opt_value) {
- el.checked = opt_value;
- };
- /**
- * Sets the value of a select-one element.
- * @param {Element} el The element.
- * @param {string=} opt_value The value of the selected option element.
- * @private
- */
- goog.dom.forms.setSelectSingle_ = function(el, opt_value) {
- // unset any prior selections
- el.selectedIndex = -1;
- if (goog.isString(opt_value)) {
- for (var option, i = 0;
- option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {
- if (option.value == opt_value) {
- option.selected = true;
- break;
- }
- }
- }
- };
- /**
- * Sets the value of a select-multiple element.
- * @param {Element} el The element.
- * @param {Array<string>|string=} opt_value The value of the selected option
- * element(s).
- * @private
- */
- goog.dom.forms.setSelectMultiple_ = function(el, opt_value) {
- // reset string opt_values as an array
- if (goog.isString(opt_value)) {
- opt_value = [opt_value];
- }
- for (var option, i = 0;
- option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {
- // we have to reset the other options to false for select-multiple
- option.selected = false;
- if (opt_value) {
- for (var value, j = 0; value = opt_value[j]; j++) {
- if (option.value == value) {
- option.selected = true;
- }
- }
- }
- }
- };
|