Path: blob/trunk/third_party/closure/goog/dom/forms.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Utilities for manipulating a form and elements.16*17* @author [email protected] (Erik Arvidsson)18*/1920goog.provide('goog.dom.forms');2122goog.require('goog.dom.InputType');23goog.require('goog.dom.TagName');24goog.require('goog.structs.Map');25goog.require('goog.window');26272829/**30* Submits form data via a new window. This hides references to the parent31* window and should be used when submitting forms to untrusted 3rd party urls.32* By default, this uses the action and method of the specified form33* element. It is possible to override the default action and method if an34* optional submit element with formaction and/or formmethod attributes is35* provided.36* @param {!HTMLFormElement} form The form.37* @param {!HTMLElement=} opt_submitElement The `<button>` or `<input>` element38* used to submit the form. The element should have a submit type.39* @return {boolean} true If the form was submitted succesfully.40* @throws {!Error} If opt_submitElement is not a valid form submit element.41*/42goog.dom.forms.submitFormInNewWindow = function(form, opt_submitElement) {43var formData = goog.dom.forms.getFormDataMap(form);44var action = form.action;45var method = form.method;4647if (opt_submitElement) {48if (goog.dom.InputType.SUBMIT != opt_submitElement.type.toLowerCase()) {49throw Error('opt_submitElement does not have a valid type.');50}515253var submitValue =54/** @type {?string} */ (goog.dom.forms.getValue(opt_submitElement));55if (submitValue != null) {56goog.dom.forms.addFormDataToMap_(57formData, opt_submitElement.name, submitValue);58}5960if (opt_submitElement.getAttribute('formaction')) {61action = opt_submitElement.getAttribute('formaction');62}6364if (opt_submitElement.getAttribute('formmethod')) {65method = opt_submitElement.getAttribute('formmethod');66}67}6869return goog.dom.forms.submitFormDataInNewWindow(action, method, formData);70};7172/**73* Submits form data via a new window. This hides references to the parent74* window and should be used when submitting forms to untrusted 3rd party urls.75* @param {string} actionUri uri to submit form content to.76* @param {string} method HTTP method used to submit the form.77* @param {!goog.structs.Map<string, !Array<string>>} formData A map of the form78* data as field name to arrays of values.79* @return {boolean} true If the form was submitted succesfully.80*/81goog.dom.forms.submitFormDataInNewWindow = function(82actionUri, method, formData) {83var newWin = goog.window.openBlank('', {noreferrer: true});8485// This could be null if a new window could not be opened. e.g. if it was86// stopped by a popup blocker.87if (!newWin) {88return false;89}9091var newDocument = newWin.document;9293var newForm =94/** @type {!HTMLFormElement} */ (newDocument.createElement('form'));95newForm.method = method;96newForm.action = actionUri;9798// After this point, do not directly reference the form object's functions as99// field names can shadow the form's properties.100101formData.forEach(function(fieldValues, fieldName) {102for (var i = 0; i < fieldValues.length; i++) {103var fieldValue = fieldValues[i];104var newInput = newDocument.createElement('input');105newInput.name = fieldName;106newInput.value = fieldValue;107newInput.type = 'hidden';108HTMLFormElement.prototype.appendChild.call(newForm, newInput);109}110});111112HTMLFormElement.prototype.submit.call(newForm);113return true;114};115116117/**118* Returns form data as a map of name to value arrays. This doesn't119* support file inputs.120* @param {HTMLFormElement} form The form.121* @return {!goog.structs.Map<string, !Array<string>>} A map of the form data122* as field name to arrays of values.123*/124goog.dom.forms.getFormDataMap = function(form) {125var map = new goog.structs.Map();126goog.dom.forms.getFormDataHelper_(127form, map, goog.dom.forms.addFormDataToMap_);128return map;129};130131132/**133* Returns the form data as an application/x-www-url-encoded string. This134* doesn't support file inputs.135* @param {HTMLFormElement} form The form.136* @return {string} An application/x-www-url-encoded string.137*/138goog.dom.forms.getFormDataString = function(form) {139var sb = [];140goog.dom.forms.getFormDataHelper_(141form, sb, goog.dom.forms.addFormDataToStringBuffer_);142return sb.join('&');143};144145146/**147* Returns the form data as a map or an application/x-www-url-encoded148* string. This doesn't support file inputs.149* @param {HTMLFormElement} form The form.150* @param {Object} result The object form data is being put in.151* @param {Function} fnAppend Function that takes {@code result}, an element152* name, and an element value, and adds the name/value pair to the result153* object.154* @private155*/156goog.dom.forms.getFormDataHelper_ = function(form, result, fnAppend) {157var els = form.elements;158for (var el, i = 0; el = els[i]; i++) {159if ( // Make sure we don't include elements that are not part of the form.160// Some browsers include non-form elements. Check for 'form' property.161// See http://code.google.com/p/closure-library/issues/detail?id=227162// and163// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#the-input-element164(el.form != form) || el.disabled ||165// HTMLFieldSetElement has a form property but no value.166el.tagName == goog.dom.TagName.FIELDSET) {167continue;168}169170var name = el.name;171switch (el.type.toLowerCase()) {172case goog.dom.InputType.FILE:173// file inputs are not supported174case goog.dom.InputType.SUBMIT:175case goog.dom.InputType.RESET:176case goog.dom.InputType.BUTTON:177// don't submit these178break;179case goog.dom.InputType.SELECT_MULTIPLE:180var values = goog.dom.forms.getValue(el);181if (values != null) {182for (var value, j = 0; value = values[j]; j++) {183fnAppend(result, name, value);184}185}186break;187default:188var value = goog.dom.forms.getValue(el);189if (value != null) {190fnAppend(result, name, value);191}192}193}194195// input[type=image] are not included in the elements collection196var inputs = form.getElementsByTagName(String(goog.dom.TagName.INPUT));197for (var input, i = 0; input = inputs[i]; i++) {198if (input.form == form &&199input.type.toLowerCase() == goog.dom.InputType.IMAGE) {200name = input.name;201fnAppend(result, name, input.value);202fnAppend(result, name + '.x', '0');203fnAppend(result, name + '.y', '0');204}205}206};207208209/**210* Adds the name/value pair to the map.211* @param {!goog.structs.Map<string, !Array<string>>} map The map to add to.212* @param {string} name The name.213* @param {string} value The value.214* @private215*/216goog.dom.forms.addFormDataToMap_ = function(map, name, value) {217var array = map.get(name);218if (!array) {219array = [];220map.set(name, array);221}222array.push(value);223};224225226/**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* @private232*/233goog.dom.forms.addFormDataToStringBuffer_ = function(sb, name, value) {234sb.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));235};236237238/**239* Whether the form has a file input.240* @param {HTMLFormElement} form The form.241* @return {boolean} Whether the form has a file input.242*/243goog.dom.forms.hasFileInput = function(form) {244var els = form.elements;245for (var el, i = 0; el = els[i]; i++) {246if (!el.disabled && el.type &&247el.type.toLowerCase() == goog.dom.InputType.FILE) {248return true;249}250}251return false;252};253254255/**256* Enables or disables either all elements in a form or a single form element.257* @param {Element} el The element, either a form or an element within a form.258* @param {boolean} disabled Whether the element should be disabled.259*/260goog.dom.forms.setDisabled = function(el, disabled) {261// disable all elements in a form262if (el.tagName == goog.dom.TagName.FORM) {263var els = /** @type {!HTMLFormElement} */ (el).elements;264for (var i = 0; el = els[i]; i++) {265goog.dom.forms.setDisabled(el, disabled);266}267} else {268// makes sure to blur buttons, multi-selects, and any elements which269// maintain keyboard/accessibility focus when disabled270if (disabled == true) {271el.blur();272}273el.disabled = disabled;274}275};276277278/**279* Focuses, and optionally selects the content of, a form element.280* @param {Element} el The form element.281*/282goog.dom.forms.focusAndSelect = function(el) {283el.focus();284if (el.select) {285el.select();286}287};288289290/**291* Whether a form element has a value.292* @param {Element} el The element.293* @return {boolean} Whether the form has a value.294*/295goog.dom.forms.hasValue = function(el) {296var value = goog.dom.forms.getValue(el);297return !!value;298};299300301/**302* Whether a named form field has a value.303* @param {HTMLFormElement} form The form element.304* @param {string} name Name of an input to the form.305* @return {boolean} Whether the form has a value.306*/307goog.dom.forms.hasValueByName = function(form, name) {308var value = goog.dom.forms.getValueByName(form, name);309return !!value;310};311312313/**314* Gets the current value of any element with a type.315* @param {Element} el The element.316* @return {string|Array<string>|null} The current value of the element317* (or null).318*/319goog.dom.forms.getValue = function(el) {320var type = /** @type {!HTMLInputElement} */ (el).type;321if (!goog.isDef(type)) {322return null;323}324switch (type.toLowerCase()) {325case goog.dom.InputType.CHECKBOX:326case goog.dom.InputType.RADIO:327return goog.dom.forms.getInputChecked_(el);328case goog.dom.InputType.SELECT_ONE:329return goog.dom.forms.getSelectSingle_(el);330case goog.dom.InputType.SELECT_MULTIPLE:331return goog.dom.forms.getSelectMultiple_(el);332default:333return goog.isDef(el.value) ? el.value : null;334}335};336337338/**339* Returns the value of the named form field. In the case of radio buttons,340* returns the value of the checked button with the given name.341*342* @param {HTMLFormElement} form The form element.343* @param {string} name Name of an input to the form.344*345* @return {Array<string>|string|null} The value of the form element, or346* null if the form element does not exist or has no value.347*/348goog.dom.forms.getValueByName = function(form, name) {349var els = form.elements[name];350351if (els) {352if (els.type) {353return goog.dom.forms.getValue(els);354} else {355for (var i = 0; i < els.length; i++) {356var val = goog.dom.forms.getValue(els[i]);357if (val) {358return val;359}360}361}362}363return null;364};365366367/**368* Gets the current value of a checkable input element.369* @param {Element} el The element.370* @return {?string} The value of the form element (or null).371* @private372*/373goog.dom.forms.getInputChecked_ = function(el) {374return el.checked ? /** @type {?} */ (el).value : null;375};376377378/**379* Gets the current value of a select-one element.380* @param {Element} el The element.381* @return {?string} The value of the form element (or null).382* @private383*/384goog.dom.forms.getSelectSingle_ = function(el) {385var selectedIndex = /** @type {!HTMLSelectElement} */ (el).selectedIndex;386return selectedIndex >= 0 ?387/** @type {!HTMLSelectElement} */ (el).options[selectedIndex].value :388null;389};390391392/**393* Gets the current value of a select-multiple element.394* @param {Element} el The element.395* @return {Array<string>?} The value of the form element (or null).396* @private397*/398goog.dom.forms.getSelectMultiple_ = function(el) {399var values = [];400for (var option, i = 0;401option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {402if (option.selected) {403values.push(option.value);404}405}406return values.length ? values : null;407};408409410/**411* Sets the current value of any element with a type.412* @param {Element} el The element.413* @param {*=} opt_value The value to give to the element, which will be coerced414* by the browser in the default case using toString. This value should be415* an array for setting the value of select multiple elements.416*/417goog.dom.forms.setValue = function(el, opt_value) {418var type = /** @type {!HTMLInputElement} */ (el).type;419if (goog.isDef(type)) {420switch (type.toLowerCase()) {421case goog.dom.InputType.CHECKBOX:422case goog.dom.InputType.RADIO:423goog.dom.forms.setInputChecked_(424el,425/** @type {string} */ (opt_value));426break;427case goog.dom.InputType.SELECT_ONE:428goog.dom.forms.setSelectSingle_(429el,430/** @type {string} */ (opt_value));431break;432case goog.dom.InputType.SELECT_MULTIPLE:433goog.dom.forms.setSelectMultiple_(434el,435/** @type {Array<string>} */ (opt_value));436break;437default:438el.value = goog.isDefAndNotNull(opt_value) ? opt_value : '';439}440}441};442443444/**445* Sets a checkable input element's checked property.446* #TODO(user): This seems potentially unintuitive since it doesn't set447* the value property but my hunch is that the primary use case is to check a448* checkbox, not to reset its value property.449* @param {Element} el The element.450* @param {string|boolean=} opt_value The value, sets the element checked if451* val is set.452* @private453*/454goog.dom.forms.setInputChecked_ = function(el, opt_value) {455el.checked = opt_value;456};457458459/**460* Sets the value of a select-one element.461* @param {Element} el The element.462* @param {string=} opt_value The value of the selected option element.463* @private464*/465goog.dom.forms.setSelectSingle_ = function(el, opt_value) {466// unset any prior selections467el.selectedIndex = -1;468if (goog.isString(opt_value)) {469for (var option, i = 0;470option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {471if (option.value == opt_value) {472option.selected = true;473break;474}475}476}477};478479480/**481* Sets the value of a select-multiple element.482* @param {Element} el The element.483* @param {Array<string>|string=} opt_value The value of the selected option484* element(s).485* @private486*/487goog.dom.forms.setSelectMultiple_ = function(el, opt_value) {488// reset string opt_values as an array489if (goog.isString(opt_value)) {490opt_value = [opt_value];491}492for (var option, i = 0;493option = /** @type {!HTMLSelectElement} */ (el).options[i]; i++) {494// we have to reset the other options to false for select-multiple495option.selected = false;496if (opt_value) {497for (var value, j = 0; value = opt_value[j]; j++) {498if (option.value == value) {499option.selected = true;500}501}502}503}504};505506507