/* * Copyright (c) 2001-2009, TIBCO Software Inc. * Use, modification, and distribution subject to terms of license. */ /** * Provides support for legacy HTML GET and POST forms. Allows the submission of forms with arbitrary * key-value pairs as well as file upload. *

* Prompting the user for a file upload field (promptForFile()) is only supported in * Microsoft Internet Explorer. * * @since 3.0 */ jsx3.Class.defineClass("jsx3.net.Form", null, [jsx3.util.EventDispatcher], function(Form, Form_prototype) { var LOG = jsx3.util.Logger.getLogger(Form.jsxclass.getName()); /** @private @jsxobf-clobber */ Form.SERIAL = 0; /** @private @jsxobf-clobber */ Form.DEFAULT_POLL = 250; /** @private @jsxobf-clobber */ Form.DEFAULT_TIMEOUT = 30000; /** * {String} * @final @jsxobf-final */ Form.METHOD_GET = "get"; /** * {String} * @final @jsxobf-final */ Form.METHOD_POST = "post"; /** @private @jsxobf-clobber */ Form.ENCTYPE_NORMAL = "application/x-www-form-urlencoded"; /** @private @jsxobf-clobber */ Form.ENCTYPE_MULTIPART = "multipart/form-data"; /** * {String} Event type published when a file has been chosen through user interaction. The event has properties field and value. * @final @jsxobf-final */ Form.EVENT_FILE_SELECTED = "file"; /** * {String} Event type published when the response has loaded. * @final @jsxobf-final */ Form.EVENT_ON_RESPONSE = "response"; /** * {String} Event type published when a security error occurs trying to access the response. * @final @jsxobf-final */ Form.EVENT_ON_ERROR = "error"; /** * {String} Event type published when the response is still not ready after the specified timeout period. * @final @jsxobf-final */ Form.EVENT_ON_TIMEOUT = "timeout"; /** * instance initializer * @param strMethod {String} form method, METHOD_GET (default) or METHOD_POST * @param strAction {String} the URL to submit to * @param bMultipart {boolean} if true the form can support file upload */ Form_prototype.init = function(strMethod, strAction, bMultipart) { /* @jsxobf-clobber */ this._id = "jsx_httpform_" + (Form.SERIAL++); // overload this method, but in a private way if (strMethod == -1) { this.initIFrame(strAction); } else { this.initIFrame(); this.setMethod(strMethod != null ? strMethod : Form.METHOD_GET); this.setAction(strAction); this.setMultipart(bMultipart); } }; /** * Creates a new form and initialize it from the HTML representation of a form. * @param strFragment {String} the html fragment containing a

tag. * @throws an error if the fragment was not well-formed or did not contain a form tag. * @return {jsx3.net.Form} */ Form.newFromFragment = function(strFragment) { return new Form(-1, strFragment); }; /** * Creates the invisible IFRAME that will contain the form and the response. * @private * @jsxobf-clobber */ Form_prototype.initIFrame = function(strFormHtml) { var theForm = strFormHtml; if (! theForm) theForm = '
'; var docHTML = '' + theForm + ''; jsx3.html.insertAdjacentHTML(document.body, "beforeEnd", ""); /* @jsxobf-clobber */ this._span = document.getElementById(this._id + "_ispan"); /* @jsxobf-clobber */ this._iframe = this.eval(this._id + "_frame"); // Mozilla HTML mode uses contentDocument instead of document var doc = this._iframe.document || this._iframe.contentDocument; if (doc == null) throw new jsx3.Exception(jsx3._msg("htfrm.no_ifr", this)); doc.open(); doc.write(docHTML); doc.close(); this._form = doc.getElementsByTagName("form")[0]; if (strFormHtml) { if (this._form == null) throw new jsx3.Exception(jsx3._msg("htfrm.bad_frag", strFormHtml)); // cache these attributes of the form in this instance so that getters work if (!this._form.method) this._form.method = Form.METHOD_GET; this._action = this._form.action; this._method = this._form.method.toLowerCase(); this._multipart = Boolean(this._form.encoding) && this._form.encoding.toLowerCase() == Form.ENCTYPE_MULTIPART; } }; /** * Returns the method of this form. * @return {String} METHOD_GET or METHOD_POST. */ Form_prototype.getMethod = function() { return this._method; }; /** * Sets the method of this form. * @param method {String} METHOD_GET or METHOD_POST. */ Form_prototype.setMethod = function( method ) { method = method != null ? method.toLowerCase() : Form.METHOD_GET; /* @jsxobf-clobber */ this._method = method; this.getRenderedForm().setAttribute("method", method); }; /** * Returns the action of this form, the URL that this form is submitted to. * @return {String} action */ Form_prototype.getAction = function() { return this._action; }; /** * Sets the action of this form. * @param action {String} */ Form_prototype.setAction = function( action ) { /* @JSC */ if (! jsx3.CLASS_LOADER.IE) { action = jsx3.app.Browser.getLocation().resolve(action).toString(); /* @JSC */ } /* @jsxobf-clobber */ this._action = action; this.getRenderedForm().setAttribute("action", action); }; /** * Returns whether this form is multipart. Only multipart forms may upload files. * @return {boolean} */ Form_prototype.getMultipart = function() { return this._multipart; }; /** * Sets whether this form is multipart. * @param multipart {boolean} */ Form_prototype.setMultipart = function( multipart ) { /* @jsxobf-clobber */ this._multipart = multipart; this.getRenderedForm().setAttribute("encoding", multipart ? Form.ENCTYPE_MULTIPART : Form.ENCTYPE_NORMAL); }; /** @private @jsxobf-clobber */ Form_prototype.getRenderedSpan = function() { return this._span; }; /** @private @jsxobf-clobber */ Form_prototype.getRenderedIFrame = function() { return this._iframe; }; /** @private @jsxobf-clobber */ Form_prototype.getRenderedForm = function() { return this._form; }; /** * Returns the value of a field in this form. * @param strName {String} the name of the form field to query. * @return {String} the field value or null if no such field exists. */ Form_prototype.getField = function(strName) { var input = this.getRenderedForm().elements[strName]; return (input != null && typeof(input) == "object") ? input.value : null; }; /** * Returns the names of all fields in this form. * @return {Array} */ Form_prototype.getFields = function() { var names = []; var e = this.getRenderedForm().elements; for (var i = 0; i < e.length; i++) { names.push(e[i].name); } return names; }; /** * Sets the value of a field in this form. Line breaks and whitespace are honored although any line breaks * will be converted to either \r\n or \n depending on the platform. * Setting the value to null will set it to the empty string. * * @param strName {String} the name of the form field to set. * @param strValue {String} the new value of form field. * @param bConcat {boolean} if true, will append " " + strValue to the existing value. The space is * only inserted if the existing value is not empty. */ Form_prototype.setField = function(strName, strValue, bConcat) { var form = this.getRenderedForm(); var input = form.elements[strName]; if (input == null || typeof(input) != "object") { jsx3.html.insertAdjacentHTML(form, "beforeEnd", "
" + strName + ":
"); input = form.elements[strName]; } if (strValue == null) strValue = ""; if (bConcat && input.value) { input.value = input.value + " " + strValue; } else { input.value = strValue; } }; /** * Removes a field from this form. * @param strName {String} the name of the form field to remove. */ Form_prototype.removeField = function(strName) { var form = this.getRenderedForm(); var input = form.elements[strName]; if (input != null && input.parentNode) jsx3.html.removeNode(input.parentNode); }; /** * Adds a file upload field to this form. * @param strName {String} the name of the new field. */ Form_prototype.addFileUploadField = function(strName) { var form = this.getRenderedForm(); var input = form.elements[strName]; if (input == null || typeof(input) != "object") { jsx3.html.insertAdjacentHTML(form, "beforeEnd", "
" + strName + ":
"); input = form.elements[strName]; var me = this; input.onchange = function() {me.onFileFieldChanged(strName, input.value);}; } else { throw new jsx3.Exception(jsx3._msg("htfrm.dup", this, strName)); } }; /** * Invokes the operating system file browser to choose a file for a file upload field. This method is not * supported in browsers other than Microsoft Internet Explorer. * * @param strFieldName {String} the name of the file upload field. */ Form_prototype.promptForFile = function(strFieldName) { var form = this.getRenderedForm(); var input = form.elements[strFieldName]; if (input != null && input.type == "file") { /* @JSC */ if (jsx3.CLASS_LOADER.IE) { input.click(); /* @JSC */ } else { LOG.warn(jsx3._msg("htfrm.prompt")); input.click(); /* @JSC */ } } else { throw new jsx3.Exception(jsx3._msg("htfrm.no_file", this, strFieldName)); } }; /** @private @jsxobf-clobber */ Form_prototype.onFileFieldChanged = function(strField, strValue) { this.publish({subject:Form.EVENT_FILE_SELECTED, field:strField, value:strValue}); }; /** @private @jsxobf-clobber */ Form_prototype.onResponseLoad = function() { this._form = null; this.publish({subject:Form.EVENT_ON_RESPONSE}); }; /* @JSC */ if (jsx3.CLASS_LOADER.IE) { /** * Sends the form. Sending the form is always asynchronous. Once a form has been sent it may not be reused. * @param intPollInterval {int} milliseconds between checking for a response. If not provided, the default value is 1/4 sec. * @param intTimeout {int} total milliseconds before timeout. If not provided, the default value is 30 sec. */ Form_prototype.send = function(intPollInterval, intTimeout) { if (intPollInterval == null) intPollInterval = Form.DEFAULT_POLL; if (intTimeout == null) intTimeout = Form.DEFAULT_TIMEOUT; this._form.submit(); var count = 0; var max = intTimeout <= 0 ? Number.MAX_VALUE : Math.ceil(intTimeout/intPollInterval); var me = this; /* @jsxobf-clobber */ this._intervalId = window.setInterval(function() {me.responsePoll(++count < max);}, intPollInterval); }; /* @jsxobf-clobber */ Form_prototype.responsePoll = function(bContinue) { try { this._iframe.document.readyState == ""; // empty statement may trigger exception } catch (e) { window.clearInterval(this._intervalId); this._intervalId = null; this.onResponseError(jsx3._msg("htfrm.sec", this, jsx3.NativeError.wrap(e))); return; } if (this._iframe.document.readyState == "complete" || this._iframe.document.readyState == "loaded") { window.clearInterval(this._intervalId); this._intervalId = null; this.onResponseLoad(); } else if (! bContinue) { window.clearInterval(this._intervalId); this._intervalId = null; this.destroy(); this.publish({subject:Form.EVENT_ON_TIMEOUT}); } }; /** @private @jsxobf-clobber */ Form_prototype.onResponseError = function(strMessage) { this._form = null; this.publish({subject:Form.EVENT_ON_ERROR, message:strMessage}); }; /** * Stops polling for a response. */ Form_prototype.abort = function() { window.clearInterval(this._intervalId); }; /** * Returns the content of the response as a string. * @return {String} */ Form_prototype.getResponseText = function() { var dcmt = this._iframe.document; //first check form invalid header info returned from the server (mimeType fails in these cases) if (typeof(dcmt.mimeType) != "unknown") { // BROWSER: won't work in Mozilla if (dcmt.mimeType.indexOf("XML") == 0) { return dcmt.XMLDocument.xml; } else if(dcmt.mimeType.indexOf("HTML") == 0) { return jsx3.html.getOuterHTML(this._iframe.document.childNodes[0]); } else if(dcmt.mimeType.indexOf("Text") == 0) { return this._iframe.document.childNodes[0].innerText; } } return this._iframe.document.body.innerHTML; }; /** * Returns the content of the response as an XML document. * @return {jsx3.xml.Document} */ Form_prototype.getResponseXML = function() { var dcmt = this._iframe.document; if ((typeof(dcmt.mimeType) == "string" && dcmt.mimeType.indexOf("XML") == 0) || (dcmt.XMLDocument && dcmt.XMLDocument.documentElement)) { var objDoc = new jsx3.xml.Document(); objDoc.loadXML(dcmt.XMLDocument.documentElement.xml); return objDoc; } else { var source = null; if (dcmt.mimeType.indexOf("HTML") == 0) { source = jsx3.html.getOuterHTML(this._iframe.document.childNodes[0]); } else if (dcmt.mimeType.indexOf("Text") == 0) { source = this._iframe.document.childNodes[0].innerText; } else { source = this._iframe.document.body.innerHTML; } var doc = new jsx3.xml.Document(); doc.loadXML(source); if (doc.hasError()) { LOG.error(jsx3._msg("htfrm.bad_xml", doc.getError())); return null; } return doc; } }; /* @JSC */ } else { Form_prototype.send = function(intPollInterval, intTimeout) { if (intPollInterval == null) intPollInterval = Form.DEFAULT_POLL; if (intTimeout == null) intTimeout = Form.DEFAULT_TIMEOUT; var me = this; this._iframe.onload = function() { me._onIFrameLoad(); }; try { this._form.submit(); } catch (e) { this.onResponseError(jsx3.NativeError.wrap(e).toString()); return; } /* @jsxobf-clobber */ this._intervalId = window.setTimeout(function() {me.onIFrameTimeout();}, intTimeout); }; Form_prototype.abort = function() { this._iframe.onload = null; window.clearTimeout(this._intervalId); }; /** @private @jsxobf-clobber */ Form_prototype.onResponseError = function(strMessage) { this._iframe.onload = null; window.clearTimeout(this._intervalId); this.publish({subject:Form.EVENT_ON_ERROR, message:strMessage}); }; /* @jsxobf-clobber */ Form_prototype._onIFrameLoad = function() { this._iframe.onload = null; if (this._intervalId) { window.clearTimeout(this._intervalId); this._intervalId = null; } try { try { if (window.netscape && netscape.security) netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead'); } catch (e) { } var dcmt = this._iframe.contentDocument; var a = " " + dcmt.childNodes[0]; } catch (e) { this.publish({subject:Form.EVENT_ON_ERROR, message:jsx3.NativeError.wrap(e).toString()}); return; } this.onResponseLoad(); }; /* @jsxobf-clobber */ Form_prototype.onIFrameTimeout = function() { this._iframe.onload = null; this._intervalId = null; this.destroy(); this.publish({subject:Form.EVENT_ON_TIMEOUT}); }; Form_prototype.getResponseText = function() { var dcmt = this._iframe.contentDocument; if (dcmt instanceof XMLDocument) { return (new XMLSerializer()).serializeToString(dcmt); } else if (dcmt instanceof HTMLDocument) { return jsx3.html.getOuterHTML(dcmt); } else { LOG.warn(jsx3._msg("htfrm.bad_dt", dcmt)); return ""; } }; Form_prototype.getResponseXML = function() { var dcmt = this._iframe.contentDocument; if (dcmt instanceof XMLDocument || dcmt instanceof HTMLDocument) { return new jsx3.xml.Document().loadXML(this.getResponseText()); } else { LOG.warn(jsx3._msg("htfrm.bad_dt", dcmt)); return new jsx3.xml.Document().loadXML(""); } }; /* @JSC */ } /** * Destroys the form and the hidden IFRAME. This method should be called after receiving an onResponse, onError, or * onTimeout event for proper garbage collection. */ Form_prototype.destroy = function() { var span = this.getRenderedSpan(); if (span != null) { jsx3.html.removeNode(span); this._span = null; this._iframe = null; this._form = null; } else { LOG.warn(jsx3._msg("htfrm.destr", this)); } }; /** * Reveals the IFRAME containing this form for debugging purposes. Dimensions of the revealed form may be provided * or a default position and dimensions will be used. * @param l {int} pixels from the left side of the HTML page that the IFRAME will be displayed. * @param t {int} pixels from the top of the HTML page that the IFRAME will be displayed. * @param w {int} width of the revealed IFRAME, in pixels. * @param h {int} height of the revealed IFRAME, in pixels. * @since 3.2 * @see #conceal() */ Form_prototype.reveal = function(l, t, w, h) { if (l == null) l = 10; if (t == null) t = 10; if (w == null) w = Math.round(this._span.parentNode.offsetWidth / 3); if (h == null) h = Math.round(this._span.parentNode.offsetHeight / 3); var style = this._span.style; style.left = l + "px"; style.top = t + "px"; style.width = w + "px"; style.height = h + "px"; style.zIndex = 30000; }; /** * Hides the IFRAME containing this form after it has been shown by calling reveal(). * @since 3.2 * @see #reveal() */ Form_prototype.conceal = function() { var style = this._span.style; style.left = "-50px"; style.top = "0px"; style.width = "10px"; style.height = "10px"; style.zIndex = 0; }; Form_prototype.toString = function() { return "@Form " + this.getAction(); }; }); /* @JSC :: begin DEP */ /** * @deprecated Renamed to jsx3.net.Form * @see jsx3.net.Form * @jsxdoc-definition jsx3.Class.defineClass("jsx3.HttpForm", -, null, function(){}); */ jsx3.HttpForm = jsx3.net.Form; /* @JSC :: end */